Project1

标题: 一款VX制作的游戏暴风书店,FPS就7帧!?事件执行拖慢 [打印本页]

作者: 流川枫    时间: 2008-10-19 21:15
标题: 一款VX制作的游戏暴风书店,FPS就7帧!?事件执行拖慢
昨天玩了一款RMVX制作的游戏 暴风书店,FPS就7帧!?事件执行拖慢

我看了一下这个游戏的project,怪不得这么卡,原来这个 游戏系统 是用并行事件拼凑出来的。为什么事件执行的效率会这么低呢?我想如果全用脚本写系统的话,应该不会慢到这种地步。后来我又看了一下Interpreter的代码,发现在解释指令的时候 用了几十个when来判断。也就是说,每执行一个指令就需要做N个判断后才能执行对应的函数,对于一帧里要求同时执行多个事件指令的游戏来说,无疑是致命的。虽说when是按常用到的指令的顺序依次检测的,但是其中条件分歧需要经过6个关卡,而变数操作则需要经过16个关卡。

对于这种情况,可以用 数组直接寻址 的方法来优化,此方法一次性就能完成关卡检测。

伪代码如下:

  1. commands=[command_101,command_102,……]
  2. call commands[@list[@index].code]
复制代码


不过RGSS2里的code竟然不是连续的序列,这样一来就需要多余的分支来处理序列的关系了。
作者: 流川枫    时间: 2008-10-19 21:18
貌似我好像发错区了···{/gg}
作者: 虚幻死神    时间: 2008-10-19 21:19
這裡是XP.....
作者: 八云紫    时间: 2008-10-19 21:20
那啥, 是不是 眼花了, 直接寻址? 汇编??

事件不行就用 脚本, 需要动用 汇编 么?
作者: 流川枫    时间: 2008-10-19 21:20
以下引用虚幻死神于2008-10-19 13:19:35的发言:

這裡是XP.....

我错了!!!{/ll}
作者: 流川枫    时间: 2008-10-19 21:21
不过XP也有同样的问题,事件拖慢问题····
作者: 虚幻死神    时间: 2008-10-19 21:21
版主.......有任務了.....
(怎么還不來...)
作者: 八云紫    时间: 2008-10-19 21:23
以下引用流川枫于2008-10-19 13:21:20的发言:

不过XP也有同样的问题,事件拖慢问题····


脚本才是最好的。 不过 有时候,刷新没弄好的话, FPS 掉的更快。
作者: 流川枫    时间: 2008-10-19 21:23
以下引用八云蓝于2008-10-19 13:20:28的发言:

那啥, 是不是 眼花了, 直接寻址? 汇编??

事件不行就用 脚本, 需要动用 汇编 么?

不是汇编,就是 利用函数指针数组下标索引的方式直接调用函数
作者: 八云紫    时间: 2008-10-19 21:25
记得 Ruby 没有 指针 这个数据类型吧。

有 指针 该多好.
作者: ONEWateR    时间: 2008-10-19 21:26
标签用错了 - -
帖子错区了 - -
作者: 流川枫    时间: 2008-10-19 21:31
以下引用ONEWateR于2008-10-19 13:26:09的发言:

标签用错了 - -
帖子错区了 - -

火星人都知道····{/pz}
作者: link006007    时间: 2008-10-20 01:23
以下引用流川枫于2008-10-19 13:15:39的发言:
发现在解释指令的时候 用了几十个when来判断。也就是说,每执行一个指令就需要做N个判断后才能执行对应的函数


你想太多了  逐个判断, 那是 if elsif  才是
switch做的是内存地址的偏移, 然后得到正确的函数代码段, 效率比if elsif 高 (C++中... ...
                                                                                                    ruby未完全了解诶)
其实RM默认的很多脚本效率都不行...  
比如角色和NPC的passable?函数(完全可以在每一帧建立一个passable的图缓存,就不要每个事件都要遍历全部事件), 地图事件的更新函数(基本上, 很多事件当前或长久更本不需要执行)..
说道函数指针  RM可以使用Proc...
比如
EVENT_1   = 0, EVENT_2 = 1, ... ..., EVENT_N = n-1;
EVENT_FUNC = [
    Proc.new{
      // EVENT_1 函数
    },
    Proc.new{
    },
    ... ...
]

到时直接 EVENT_FUNC[index].call
当然了  如果事件的类型不是很多的话 proc.call 反而降低性能... ...
作者: dbshy    时间: 2008-10-20 01:29
以下引用八云紫于2008-10-19 13:25:18的发言:

记得 Ruby 没有 指针 这个数据类型吧。

有 指针 该多好.


个人认为指针不好,提高编程复杂度(54我吧,平时不喜欢上机,数据结构一个半吊子) = =



作者: link006007    时间: 2008-10-20 01:34
指针   ruby全部都是指针!!!
Ruby绑定C\C++数据用指针, ruby类的函数绑定也是用指针
ruby对象的操作是指针...
所以ruby没有类型, 应为全部是内存地址的交换...  较小的整型值就不是了
作者: 八云紫    时间: 2008-10-20 01:34
以下引用link006007于2008-10-19 17:34:02的发言:

指针   ruby全部都是指针!!!
Ruby绑定C\C++数据用指针, ruby类的函数绑定也是用指针
ruby对象的操作是指针...
所以ruby没有类型, 应为全部是内存地址的交换...  较小的整型值就不是了


但是不能显示的调用指针。
作者: 地龙    时间: 2008-10-20 01:36
提示: 作者被禁止或删除 内容自动屏蔽
作者: 流川枫    时间: 2008-10-20 03:55
以下引用link006007于2008-10-19 17:23:59的发言:


以下引用流川枫于2008-10-19 13:15:39的发言:
发现在解释指令的时候 用了几十个when来判断。也就是说,每执行一个指令就需要做N个判断后才能执行对应的函数



你想太多了  逐个判断, 那是 if elsif  才是
switch做的是内存地址的偏移, 然后得到正确的函数代码段, 效率比if elsif 高 (C++中... ...
                                                                                                   ruby未完全了解诶)
其实RM默认的很多脚本效率都不行...  
比如角色和NPC的passable?函数(完全可以在每一帧建立一个passable的图缓存,就不要每个事件都要遍历全部事件), 地图事件的更新函数(基本上, 很多事件当前或长久更本不需要执行)..
说道函数指针  RM可以使用Proc...
比如
EVENT_1   = 0, EVENT_2 = 1, ... ..., EVENT_N = n-1;
EVENT_FUNC = [
   Proc.new{
     // EVENT_1 函数
   },
   Proc.new{
   },
   ... ...
]

到时直接 EVENT_FUNC[index].call
当然了  如果事件的类型不是很多的话 proc.call 反而降低性能... ...


"switch做的是内存地址的偏移" 不会吧?
我反汇编C的switch,结果却是:

or ax,ax //case 0:
je  XXX
cmp ax,3 //case 3:
je  XXX
jmp XXX //default:

C++的我没测试过···

但是也不可能偏移吧?偏移的话要求index是一个有序的value饿,
但是比如 case 0 case 3 case 15 的话,要怎么偏移?
作者: 流川枫    时间: 2008-10-20 03:57
以下引用地龙于2008-10-19 17:36:50的发言:

你把事件删了被
然后再玩

那相当于在玩一个没有 系统 的游戏了。。。根本玩不来滴
作者: link006007    时间: 2008-10-20 04:36
一定要有序的才能偏移吗?  在编译阶段 switch的case内容被编译成一段地址的跳转表,
然后在执行时,更具当前的值直接跳转到对应的代码段中, 也就是说  不管你case的内容有几条
在switch语句中, 都只有执行一次跳转(这可能也是switch要自己break的原因).  而if elsif 则是根据当前逻辑逐个判断...   
我不知道你是在什么环境下编译的, 不同的编译器, 在解释上是有一些差距的
case 0, 3, 5 直接就被编译成一张跳转表  和case值的有序,连续没太大关系

以上  我也不敢确定= =
不过Ruby的switch可能和C的不一样... C的只能是数值, 而RM可以case字符串= =
应该实现上不一样{/gg}
作者: 越前リョーマ    时间: 2008-10-20 04:45
没办法,事件就这效率。
RM就是有事件才有用。
作者: 流川枫    时间: 2008-10-20 04:53
以下引用link006007于2008-10-19 20:36:57的发言:

一定要有序的才能偏移吗?  在编译阶段 switch的case内容被编译成一段地址的跳转表,
然后在执行时,更具当前的值直接跳转到对应的代码段中, 也就是说  不管你case的内容有几条
在switch语句中, 都只有执行一次跳转(这可能也是switch要自己break的原因).  而if elsif 则是根据当前逻辑逐个判断...   
我不知道你是在什么环境下编译的, 不同的编译器, 在解释上是有一些差距的
case 0, 3, 5 直接就被编译成一张跳转表  和case值的有序,连续没太大关系

以上  我也不敢确定= =
不过Ruby的switch可能和C的不一样... C的只能是数值, 而RM可以case字符串= =
应该实现上不一样


[本贴由作者于 2008-10-19 20:39:42 最后编辑]

东灿 20:48:45
语法跟CPU指令不要混为一谈。。。。
东灿 20:49:00
语法是纯理想状态。

"这可能也是switch要自己break的原因"  
貌似不然,反汇编C的SWITCH代码段:


or ax,ax //case 0:
je  A
cmp ax,3 //case 3:
je  B
jmp C //default:

A: mov di,1 //variable=1
jmp D //break
B: mov di,2 //variable=2
jmp D //break
C: mov di,3 //variable=3
D: xor ax,ax
ret

break更像是 jmp X
作者: 流川枫    时间: 2008-10-20 05:06
link006007、你能不能把C++中switch反汇编贴出来哈
我不是很明白你说的 表格 是怎么构造出来的饿
作者: zh99998    时间: 2008-10-20 05:08
为什么不eval("command_" + 代码对应的数字) ?
作者: 流川枫    时间: 2008-10-20 05:13
以下引用zh99998于2008-10-19 21:08:31的发言:

为什么不eval("command_" + 代码对应的数字) ?


我想这个问题应该去问Enterbrain公司开发RM的程序员饿。。。
作者: link006007    时间: 2008-10-20 05:22
不同的编译器, 在解释上是有一些差距的, 有些编译器
你在编译的时候, 是需要手动输入优化指令才能把switch的case编译成地址跳转表的

而且  switch在case时使用地址跳转表必须要在case整数时才有效  其他的都和if else 没什么差别
//Windows XP + gcc v3.4.2 (mingw-special)  C不是C++
int tswitch_perf(int i) {
        int rv = i;
       switch (rv)
... ...
反:
_switch_perf:
    pushl    %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %ecx
    leal    -10(%ecx), %edx
    movl    %ecx, %eax
    cmpl    $5, %edx             // 偏移
    ja    L2
    jmp    *L10(,%edx,4)
    .section .rdata,"dr"
    .align 4
L10:
    .long      L3  // 跳转地址表
    .long   
    .long
    ... ...
L8:
//
L2:
... ...

http://c.chinaitlab.com/cc/ccjq/200805/748102_2.html
找到了   这人的blog.. 你去看吧  汇编我可玩不来 ..
http://bigwhite.blogbus.com/

关于switch性能的  他的blog里有... 我的是转载= =||
作者: 流川枫    时间: 2008-10-20 05:53
以下引用link006007于2008-10-19 21:22:36的发言:

不同的编译器, 在解释上是有一些差距的, 有些编译器
你在编译的时候, 是需要手动输入优化指令才能把switch的case编译成地址跳转表的

而且  switch在case时使用地址跳转表必须要在case整数时才有效  其他的都和if else 没什么差别
//Windows XP + gcc v3.4.2 (mingw-special)  C不是C++
int tswitch_perf(int i) {
       int rv = i;
      switch (rv)
... ...
反:
_switch_perf:
   pushl    %ebp
   movl    %esp, %ebp
   movl    8(%ebp), %ecx
   leal    -10(%ecx), %edx
   movl    %ecx, %eax
   cmpl    $5, %edx             // 偏移
   ja    L2
   jmp    *L10(,%edx,4)
   .section .rdata,"dr"
   .align 4
L10:
   .long      L3  // 跳转地址表
   .long   
   .long
   ... ...
L8:
//
L2:
... ...

http://c.chinaitlab.com/cc/ccjq/200805/748102_2.html
找到了   这人的blog.. 你去看吧  汇编我可玩不来 ..
http://bigwhite.blogbus.com/

关于switch性能的  他的blog里有... 我的是转载= =||


[本贴由作者于 2008-10-19 21:41:08 最后编辑]

在case中推荐使用小的连续的整数,因为在这种情况下,所有的编译器都可以把switch 转化成跳转表。


问题是 @list[@index].code 的序列中有几个甚至达到了600多。这样的序列也编译成跳转表?



作者: link006007    时间: 2008-10-20 06:01
不知道 =  =
ruby本身是怎么处理switch的我都不知道 {/gg}
作者: 流川枫    时间: 2008-10-20 06:04
以下引用zh99998于2008-10-19 21:08:31的发言:

为什么不eval("command_" + 代码对应的数字) ?

我试过了,不行的。

因为code有时候是一些没有预定义的值,比如 0
作者: 流川枫    时间: 2008-10-20 06:07
以下引用link006007于2008-10-19 22:01:19的发言:

不知道 =  =
ruby本身是怎么处理switch的我都不知道  

你贴出来的代码,我没看懂,不好意思哈。

不过我想所谓的 表格,应该就是构造了一个足够大的数组,
然后用 switch(x) 里的x直接寻址的吧?
那么如果case 999的话,不就需要分配999个成员了么?!{/pz}
作者: link006007    时间: 2008-10-20 06:19
case怎么分配  就简单的方法就是自己写一个switch  然后自己看.
我也是3脚猫的水平
做作业 = =    到现在一题都没有做出
作者: 流川枫    时间: 2008-10-20 06:25
以下引用link006007于2008-10-19 22:19:25的发言:

case怎么分配  就简单的方法就是自己写一个switch  然后自己看.
我也是3脚猫的水平
做作业 = =    到现在一题都没有做出

- -!
怎么说呢,就是老感觉开发RGSS的那位程序员比较“人才”。。{/cy}
作者: link006007    时间: 2008-10-20 19:08
不知道你说的是什么"人才"{/gg} (你加的双引号什么意思 - -)
个人觉得.  把C\C++代码翻译成ruby代码(RGSS)没有什么了不起
这就和会英文的把英文翻译成其他语言一样
你学过C  然后发1~2天的时间阅读ruby官方文档的Extending Ruby 基本也可以做了
还是那些写引擎的厉害  单纯翻译代码完全是机械的活动
作者: 流川枫    时间: 2008-10-20 22:14
以下引用link006007于2008-10-20 11:08:25的发言:

不知道你说的是什么"人才"
个人觉得.  把C\C++代码翻译成ruby代码(RGSS)没有什么了不起
你学过C  然后发1~2天的时间阅读ruby官方文档的Extending Ruby 基本也可以做了

我是说贬义的“人才”哈~~(真.他.妈.的.人才!){/cy} [LINE]1,#dddddd[/LINE]版主对此帖的评论:『请不要说脏话』,积分『-100』。这些被扣积分的一半会用于对本帖正确答案的悬赏。
作者: 八云紫    时间: 2008-10-21 00:46
LS 请不要说脏话
作者: 木葬枫    时间: 2008-10-21 00:54
我只知道真正有知识的人是不会自大的………
作者: link006007    时间: 2008-10-21 01:29
{/gg}{/gg}   挂了
在这里以后还是直接引用原话比较好  = =

误解随着打字数量的增加而增加
作者: 流川枫    时间: 2008-10-21 05:37
以下引用八云紫于2008-10-20 16:46:19的发言:

LS 请不要说脏话

呀~~~又被和谐掉了- -!
我知道错了啦!{/ll}
作者: 流川枫    时间: 2008-11-7 05:36
刚才我详细分析了导致 暴风书店 FPS严重拖慢的原因,答案另人意想不到。
并非是因为事件执行拖慢,就是 不执行 事件指令,FPS也只有11帧。

真正导致 暴风书店 FPS严重拖慢的最贵祸首,恰恰是某些人自认为最高效的脚本程序!
光是只载入游戏中 显示 ● 的窗口其他什么也不干,FPS就只有40了。。。

汗 {/pz}
作者: kissye    时间: 2008-11-7 05:58
提示: 作者被禁止或删除 内容自动屏蔽




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1