游戏开发论坛

 找回密码
 立即注册
搜索
查看: 7532|回复: 10

[讨论] 分享一个模拟怪物掉落的vba自定义函数

[复制链接]

15

主题

207

帖子

283

积分

中级会员

Rank: 3Rank: 3

积分
283
发表于 2010-5-6 19:06:00 | 显示全部楼层 |阅读模式
最近我用VBA模拟了怪物掉落(其实我的目的不是为了模拟掉落,而是为了设计物品的随机属性,模拟掉落没太大意义),熟悉VBA的同学如果感兴趣,大家可以共同研究下

在掉落表模式下,每个道具都有对应的权重,每次掉落检测就是按照权重随机选取一个道具

如果一个怪物一次掉落多个道具,游戏里分“所有的道具都不重复”和“道具允许重复”这两种情况

在数学上,这分别对应 无放回加权抽样 和 有放回加权抽样

我在excel里设计了两个自定义函数实现这个功能:
sample(range1,range2)和
sample_wor(range1,range2,range3)

其中range1引用某个excel表格区域,里面是道具名称或者序号列表
range2引用的某个excel表格区域,是对应的权重列表
以上两个变量在这两个函数里,含义相同
而第二个函数里的
range3引用的某个excel表格区域,是已经掉落的道具名称或者序号列,无放回抽样要求抽取出来的样本不能与它们冲突

如果某个道具对应的权重是0,代表这个道具无法掉落.

下面是VBA代码,但我感觉可能在运行效率上存在改良的余地,如果一次随机生成上万件道具的属性,速度会比较慢

Function Sample(ByVal range1 As Range, ByVal range2 As Range)
Dim array1 As Variant
Dim array2 As Variant

If range1.Rows.Count = 1 Then
ReDim array1(1 To range1.Columns.Count)
ReDim array2(1 To range2.Columns.Count)
j = 1

For i = 1 To range1.Columns.Count
    If range2.Cells(1, i) = 0 Then
    Else
    array1(j) = range1.Cells(1, i)
    array2(j) = range2.Cells(1, i)
    j = j + 1
    End If
Next



Else


ReDim array1(1 To range1.Rows.Count)
ReDim array2(1 To range2.Rows.Count)
j = 1

For i = 1 To range1.Rows.Count
    If range2.Cells(i, 1) = 0 Then
    Else
    array1(j) = range1.Cells(i, 1)
    array2(j) = range2.Cells(i, 1)
    j = j + 1
    End If
Next

End If

weight_sum = Application.WorksheetFunction.Sum(range2)
random_number = Int(weight_sum * Rnd() + 1)


i = 1
weight_subsum = array2(1)

Do While weight_subsum < random_number
i = i + 1
weight_subsum = weight_subsum + array2(i)
Loop


Sample = array1(i)


End Function


Function Sample_wor(ByVal range1 As Range, ByVal range2 As Range, ByVal range3 As Range)

Dim array1 As Variant
Dim array2 As Variant

If range1.Rows.Count = 1 Then
ReDim array1(1 To range1.Columns.Count)
ReDim array2(1 To range2.Columns.Count)
j = 1

For i = 1 To range1.Columns.Count
    If range2.Cells(1, i) = 0 Or Isexist(range1.Cells(1, i), range3) Then
    Else
    array1(j) = range1.Cells(1, i)
    array2(j) = range2.Cells(1, i)
    j = j + 1
    End If
Next



Else


ReDim array1(1 To range1.Rows.Count)
ReDim array2(1 To range2.Rows.Count)
j = 1

For i = 1 To range1.Rows.Count
    If range2.Cells(i, 1) = 0 Or Isexist(range1.Cells(i, 1), range3) Then
    Else
    array1(j) = range1.Cells(i, 1)
    array2(j) = range2.Cells(i, 1)
    j = j + 1
    End If
Next

End If


weight_sum = 0

    For i = 1 To UBound(array2)
        weight_sum = weight_sum + array2(i)
    Next i

random_number = Int(weight_sum * Rnd() + 1)


i = 1
weight_subsum = array2(1)

Do While weight_subsum < random_number
i = i + 1
weight_subsum = weight_subsum + array2(i)
Loop


Sample_wor = array1(i)


End Function

Function Isexist(a, ByVal range1 As Range)
Arr = 0

For Each b In range1
If a = b.Value Then
Arr = Arr + 1
End If
Next

If Arr = 0 Then
Isexist = False
Else
Isexist = True
End If

End Function

7

主题

56

帖子

56

积分

注册会员

Rank: 2

积分
56
发表于 2010-5-7 09:15:00 | 显示全部楼层

Re:分享一个模拟怪物掉落的vba自定义函数

对于我这种编程比较菜的人来说,见到EXCEL表格才能理解函数内容- -

8

主题

205

帖子

238

积分

中级会员

Rank: 3Rank: 3

积分
238
发表于 2010-5-7 10:18:00 | 显示全部楼层

Re:分享一个模拟怪物掉落的vba自定义函数

VB不会用,MATLAB也不会用,最近打算学一种楼主觉得哪种比较适合我。
有C++ 和 JAVA的语言基础,也只能算略懂。

15

主题

207

帖子

283

积分

中级会员

Rank: 3Rank: 3

积分
283
 楼主| 发表于 2010-5-7 12:57:00 | 显示全部楼层

Re: Re:分享一个模拟怪物掉落的vba自定义函数

游戏地球: Re:分享一个模拟怪物掉落的vba自定义函数

VB不会用,MATLAB也不会用,最近打算学一种楼主觉得哪种比较适合我。
有C++ 和 JAVA的语言基础,也只能算略懂。

我当初是先学matlab再学vb

其实这两种都差不多,如果你有C或java基础。编程很多困难是来自于算法设计。

现在开始学习的话,要看你目前想做什么:
如果偏重于操作公式表格excel,那毫无疑问要用vba
如果是求解比较复杂的数学模型,比如求解优化问题,涉及矩阵计算,就要用matlab,它也能操作excel表格

我当初因为需要矩阵计算求解一些优化问题和概率模拟,所以用了matlab编程。前段时间需要大量填制装备表格,需要无放回加权抽样函数,就只能用vba

10

主题

115

帖子

123

积分

注册会员

Rank: 2

积分
123
发表于 2010-5-7 14:57:00 | 显示全部楼层

Re: 分享一个模拟怪物掉落的vba自定义函数

哈耶克: 分享一个模拟怪物掉落的vba自定义函数

ReDim array1(1 To range1.Rows.Count)
ReDim array2(1 To range2.Rows.Count)
j = 1

For i = 1 To range1.Rows.Count
    If range2.Cells(i, 1) = 0 Or Isexist(range1.Cells(i, 1), range3) Then
    Else
    array1(j) = range1.Cells(i, 1)
    array2(j) = range2.Cells(i, 1)
    j = j + 1
    End If
Next

有些问题,可能是你没有测试过。修改如下
ReDim array1(1 To range1.Rows.Count * range1.Columns.Count)
ReDim array2(1 To range2.Rows.Count * range2.Columns.Count)
j = 1

For i = 1 To range1.Rows.Count
    For ii = 1 To range2.Columns.Count
    If range2.Cells(i, ii) = 0 Then
   
    Else
    array1(j) = range1.Cells(i, ii)
    array2(j) = range2.Cells(i, ii)
     j = j + 1
    End If
    Next
   
Next
End If
有空可以交流下,我是也是数据流的策划。说实话你比我厉害~

15

主题

207

帖子

283

积分

中级会员

Rank: 3Rank: 3

积分
283
 楼主| 发表于 2010-5-7 15:58:00 | 显示全部楼层

Re:分享一个模拟怪物掉落的vba自定义函数

楼上的,我只用单行或者单列的数据区域,所以没有采用你的方法

如果你这样修改,使得多个行列的一般区域也能适用,那么确实比我这个函数更有一般性

还有我总觉得,VBA应该自带一个简单的方法来实现检测某个元素是否存在于某个数组中,只是我没找到,不得已随便写了个函数,这个函数本质是统计出现次数,如果只是检测是否存在,只要arr达到1就可以退出循环,减少运算量。只是一般情况下,冲突不多的时候,这样做也提高不了多少效率

10

主题

115

帖子

123

积分

注册会员

Rank: 2

积分
123
发表于 2010-5-7 16:25:00 | 显示全部楼层

Re:分享一个模拟怪物掉落的vba自定义函数

哦,哪是我看错了。。。原来是没明白你代码的意思。。
效率的事觉得只要不是太夸张就可以,具体的效率问题。直接让程序解决吧。
execl自带的 COUNTIF 函数可以用用

34

主题

425

帖子

479

积分

中级会员

Rank: 3Rank: 3

积分
479
发表于 2010-5-11 09:40:00 | 显示全部楼层

Re:分享一个模拟怪物掉落的vba自定义函数

既然是模拟,又何必在乎效率?逻辑走通了,别过分繁杂不就行了?
如果是程序,讨论C++的效率也不会在这讨论。
楼主不是来秀代码的吧?
不过,我也喜欢用VBA,有兴趣讨论点结构上的事。最近做东西正烦呢。

13

主题

832

帖子

1875

积分

金牌会员

空想家

Rank: 6Rank: 6

积分
1875
发表于 2010-5-11 12:02:00 | 显示全部楼层

Re:分享一个模拟怪物掉落的vba自定义函数

可以直接以行坐标循环weight_subsum的判断,不用array和i。

15

主题

207

帖子

283

积分

中级会员

Rank: 3Rank: 3

积分
283
 楼主| 发表于 2010-5-11 23:53:00 | 显示全部楼层

Re: Re:分享一个模拟怪物掉落的vba自定义函数

Mr_I: Re:分享一个模拟怪物掉落的vba自定义函数

可以直接以行坐标循环weight_subsum的判断,不用array和i。

用array,只是因为听说array操作的效率高一点

我生成几千个固定属性物品的时候,还是会有点慢

不过我也没去测试你的方法。还不敢肯定到底哪个好
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-8-15 12:45

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表