Project1

标题: 最简单的解决RM的10s问题 [打印本页]

作者: 雷欧纳德    时间: 2007-6-2 19:39
标题: 最简单的解决RM的10s问题
唔 ms RM里10s米刷新就会挂掉的问题,这里写个最简单且效率最高的的解决方法。= =b

在RM的脚本里加上这句就行鸟- -bbb

Thread.new{loop{Graphics.update;sleep(9)}}

现在弄什么就米那10s的限制了,随便使用- -bbbbbbbbbbbbbbbbb

以下测试:

1、先不加上面那句,直接在脚本里sleep(10),等10s后程序出现脚本已备份,挂掉= =b

2、我们把这句加进去之后再测试,sleep(20),依然不会挂掉,ok,成功= =bbbb

ps:如果只加上面那句的话有一点问题的,现在改下:

@update_thread = Thread.new{loop{Graphics.update;sleep(9)}} if @update_thread.nil?

 至于前面那句有什么问题,大家想吧……= =bbb

以下这段补充:
  1. class << Graphics

  2. alias origin_update update unless method_defined? :origin_update

  3. def update
  4.    Thread.critical = true
  5.    origin_update
  6.    Thread.critical = false
  7. end

  8. end
复制代码


              [本贴由 叶舞枫 于 2007-6-5 21:18:35 进行了编辑]
作者: 雷欧纳德    时间: 2007-6-2 19:39
标题: 最简单的解决RM的10s问题
唔 ms RM里10s米刷新就会挂掉的问题,这里写个最简单且效率最高的的解决方法。= =b

在RM的脚本里加上这句就行鸟- -bbb

Thread.new{loop{Graphics.update;sleep(9)}}

现在弄什么就米那10s的限制了,随便使用- -bbbbbbbbbbbbbbbbb

以下测试:

1、先不加上面那句,直接在脚本里sleep(10),等10s后程序出现脚本已备份,挂掉= =b

2、我们把这句加进去之后再测试,sleep(20),依然不会挂掉,ok,成功= =bbbb

ps:如果只加上面那句的话有一点问题的,现在改下:

@update_thread = Thread.new{loop{Graphics.update;sleep(9)}} if @update_thread.nil?

 至于前面那句有什么问题,大家想吧……= =bbb

以下这段补充:
  1. class << Graphics

  2. alias origin_update update unless method_defined? :origin_update

  3. def update
  4.    Thread.critical = true
  5.    origin_update
  6.    Thread.critical = false
  7. end

  8. end
复制代码


              [本贴由 叶舞枫 于 2007-6-5 21:18:35 进行了编辑]
作者: 尤莉斯    时间: 2007-6-2 19:42
有错有错…… = =bbbb
作者: 雷欧纳德    时间: 2007-6-2 19:45
谁让你那个是用图片来的。。刚才都米发现。。。好麻烦。。暴-v-
作者: 尤莉斯    时间: 2007-6-2 19:47
啊呃 米注意就成图片了嘛= =bbbb 查收通知- -
作者: 精灵使者    时间: 2007-6-2 20:15
我知道了……第一句每次重启都会新建线程,会越来越卡……而且还容易出现call stack error错误……似乎美兽修改阴影脚本就是用的第二句那种方法,可以保持只有一个进程……我想在游戏里使用的话我想添加到这里,你看行不行,夏娜?
#==============================================================================
# ■ Main
#------------------------------------------------------------------------------
#  各定义结束后、从这里开始实际处理。
#==============================================================================
  begin
  # 准备过渡
  # 设置系统默认字体
  Font.default_name = (["方正舒体简体"])
  Graphics.freeze
  # 生成场景对像 (标题画面)
  $scene = Scene_Title.new
  # $scene 为有效的情况下调用 main 过程
  while $scene != nil
  $scene.main
@update_thread = Thread.new{loop{Graphics.update;sleep(9)}} if @update_thread.nil?
  end
  # 淡入淡出
  Graphics.transition(20)
rescue Errno::ENOENT
  # 补充 Errno::ENOENT 以外错误
  # 无法打开文件的情况下、显示信息后结束
  filename = $!.message.sub("No such file or directory - ", "")
  File.open(filename, "w")
  while $scene != nil
   $scene.main
end

end

作者: 尤莉斯    时间: 2007-6-2 20:20
咳咳.... 谁说的每隔9s就添加个新的进程= =bbbb ~~~

添加到F11里的最前面就行了= = 咳咳
作者: 亿万星辰    时间: 2007-6-2 20:22
第一句的问题是每次F12都会有一个新线程出现
作者: 精灵使者    时间: 2007-6-2 20:22
我说错了-_-
应该是不能反复调用的吧……每次调用一次都添加进程的说……
下面一句可以反复调用……因为如果里面判断没有这个进程才会添加的说……
最前面这个我不懂……是靠近main还是远离main的……还是在我说的那个main中间
作者: 尤莉斯    时间: 2007-6-2 20:31
远离main的好了 F11里的最上面新建 然后插入这句…… = =bb

ps:素创建线程 8素进程- -b
作者: 七夕小雨    时间: 2007-6-2 20:33
{/gg}完全不明白………………

怎么后台会开一个新的线程……

作者: 尤莉斯    时间: 2007-6-2 20:37
8明白8要紧,有用就行……{/gg}

你用rm新建个工程然后在脚本里写
while true
end
试试= = 10s就挂掉

把这句加上后再试就米问题鸟- -{/gg}{/gg}
作者: 精灵使者    时间: 2007-6-2 20:38
- -进程和线程搞混了……不过我已经明白了,谢谢{/cy}
我的游戏就是有些慢,立刻应用上……虽然慢了一点但也不会挂掉{/qiang}
作者: Benavii    时间: 2007-6-2 21:15
以下引用精灵使者于2007-6-2 12:15:10的发言:

我知道了……第一句每次重启都会新建线程,会越来越卡……而且还容易出现call stack error错误……似乎美兽修改阴影脚本就是用的第二句那种方法,可以保持只有一个进程……我想在游戏里使用的话我想添加到这里,你看行不行,夏娜?

#==============================================================================
# ■ Main
#------------------------------------------------------------------------------
#  各定义结束后、从这里开始实际处理。
#==============================================================================
begin
# 准备过渡
# 设置系统默认字体
Font.default_name = (["方正舒体简体"])
Graphics.freeze
# 生成场景对像 (标题画面)
$scene = Scene_Title.new
# $scene 为有效的情况下调用 main 过程
while $scene != nil
$scene.main
@update_thread = Thread.new{loop{Graphics.update;sleep(9)}} if @update_thread.nil?
end
# 淡入淡出
Graphics.transition(20)
rescue Errno::ENOENT
# 补充 Errno::ENOENT 以外错误
# 无法打开文件的情况下、显示信息后结束
filename = $!.message.sub("No such file or directory - ", "")
File.open(filename, "w")
while $scene != nil
  $scene.main
end

end




[本贴由作者于 2007-6-2 12:26:27 最后编辑]

放那里灭有用,程序已经在调用每个$scene的Main方法了=v=
作者: 亿万星辰    时间: 2007-6-2 21:36
以下引用七夕小雨于2007-6-2 12:33:36的发言:

完全不明白………………

怎么后台会开一个新的线程……

你就当每个线程是RM的一只手,这样只不过多给他加了一只而已
作者: 七夕小雨    时间: 2007-6-2 21:43
以下引用亿万星辰于2007-6-2 13:36:42的发言:


以下引用七夕小雨于2007-6-2 12:33:36的发言:

完全不明白………………

怎么后台会开一个新的线程……


你就当每个线程是RM的一只手,这样只不过多给他加了一只而已


{/se}{/se}
这就是说RM最后可以变成八抓章鱼了{/hx}
作者: 精灵使者    时间: 2007-6-2 22:06
以下引用Benavii于2007-6-2 13:15:41的发言:
放那里灭有用,程序已经在调用每个$scene的Main方法了=v=

那……我还是放最前面吧……最上边嘛?我记得一般的脚本放最下边的……
作者: 美兽    时间: 2007-6-2 22:43
白写了- -

只要有一个线程使用过Graphics.update就没问题,

话说这还是在看JAVA的Thread时忽然想到的...

所谓后台缓冲不过也是利用新建线程读取罢了,

但有个优先级影响传输效率。
作者: 亿万星辰    时间: 2007-6-2 22:53
以下引用美兽于2007-6-2 14:43:47的发言:

白写了- -

只要有一个线程使用过Graphics.update就没问题,

话说这还是在看JAVA的Thread时忽然想到的...

所谓后台缓冲不过也是利用新建线程读取罢了,

但有个优先级影响传输效率。

能想到就不错了,我现在只是在脑子里有线程的概念,至于用,从来没用过
作者: 尤莉斯    时间: 2007-6-2 23:23
以下引用美兽于2007-6-2 14:43:47的发言:

白写了- -

只要有一个线程使用过Graphics.update就没问题,

话说这还是在看JAVA的Thread时忽然想到的...

所谓后台缓冲不过也是利用新建线程读取罢了,

但有个优先级影响传输效率。


优先级会影响传输效率- -?{/gg}
作者: 美兽    时间: 2007-6-2 23:28
以下引用尤莉斯于2007-6-2 15:23:26的发言:


以下引用美兽于2007-6-2 14:43:47的发言:

白写了- -

只要有一个线程使用过Graphics.update就没问题,

话说这还是在看JAVA的Thread时忽然想到的...

所谓后台缓冲不过也是利用新建线程读取罢了,

但有个优先级影响传输效率。



优先级会影响传输效率- -?


可以试下,
后建的线程比原来的主线效率低的多,
接触的还不多,
不知是否是那么叫.


作者: 尤莉斯    时间: 2007-6-2 23:31
{/gg}新建的线成效率当然更低的吧
执行过程

主线程
后台线程
主线程
后台线程
主线程
后台线程
.....

以前就一个主线程效率是更高咯= ={/gg}

作者: 精灵使者    时间: 2007-6-2 23:35
还好我的机器是双核的- -MS看不出有多大区别
作者: 美兽    时间: 2007-6-2 23:41
以下引用尤莉斯于2007-6-2 15:31:29的发言:

新建的线成效率当然更低的吧
执行过程

主线程
后台线程
主线程
后台线程
主线程
后台线程
.....

以前就一个主线程效率是更高咯= =


看书时,
说到当线程优先级高时,会得到更多的CPU时间,
难道又理解错误了……
作者: 尤莉斯    时间: 2007-6-2 23:45
呃 米~~ 对于java优先级高的确实可以得到更多的cpu时间
就大概成这样

主线程
主线程
主线程
后台线程
主线程
主线程
主线程
后台线程
主线程
主线程
主线程
后台线程
.....

大概这样的 = =

但不是固定的 当一个线程执行的时间到了之后要选择其他线程继续运行,当到底选择哪个线程不是固定的,有的会挑选优先级高的进行运行,而有的还与操作系统相关- -

你可以在rm里把主线程的优先级设置高一些看看其他线程的运行情况= =bbb
作者: 3535    时间: 2007-6-3 21:36
放在哪?{/pz}
作者: 尤莉斯    时间: 2007-6-3 21:46
放到脚本里在最上面最上面最上面好了……{/gg}{/gg}
作者: 精灵使者    时间: 2007-6-3 21:58
以下引用尤莉斯于2007-6-2 12:31:41的发言:

远离main的好了 F11里的最上面新建 然后插入这句…… = =bb

某些人已经说过了……
作者: xgm    时间: 2007-6-4 18:21
提示: 作者被禁止或删除 内容自动屏蔽
作者: 星之云    时间: 2007-6-5 08:05
提示: 作者被禁止或删除 内容自动屏蔽
作者: FDR    时间: 2007-6-5 10:21
10s不调用Update,系统会抛出异常,捕获异常即可。另开一个线程的话,可能会造成屏幕的闪烁。

比如游戏正在重新绘制场景,画了一半,这个线程里面的update触发了,那么画一半的场景就会显示在屏幕上。另外一半画完系统原来的update触发,就可能会造成画面“闪烁”。

加在脚本最前面不是很安全,实在需要这样的处理,临时插入然后用完终止这个线程才是比较安全的使用方法。绝大多数情况可以使用捕获异常这种安全又正常的方法……

不过这只是理论上的分析,也许这样做并没有实际的影响,就好像10s抛出Hangup异常并不是什么大麻烦一样。
作者: 尤莉斯    时间: 2007-6-5 14:55
以下引用FDR于2007-6-5 2:21:53的发言:

10s不调用Update,系统会抛出异常,捕获异常即可。另开一个线程的话,可能会造成屏幕的闪烁。

比如游戏正在重新绘制场景,画了一半,这个线程里面的update触发了,那么画一半的场景就会显示在屏幕上。另外一半画完系统原来的update触发,就可能会造成画面“闪烁”。

加在脚本最前面不是很安全,实在需要这样的处理,临时插入然后用完终止这个线程才是比较安全的使用方法。绝大多数情况可以使用捕获异常这种安全又正常的方法……

不过这只是理论上的分析,也许这样做并没有实际的影响,就好像10s抛出Hangup异常并不是什么大麻烦一样。


呃 画面闪烁这问题确实一时米想那么复杂没考虑的 那补充一部分

  1. class << Graphics
  2.   
  3.   alias origin_update update unless method_defined? :origin_update
  4.   
  5.   def update
  6.     Thread.critical = true
  7.     origin_update
  8.     Thread.critical = false
  9.   end
  10.   
  11. end
复制代码


这样就不会造成闪烁了吧。。。

虽然可以捕获异常,但有的情况下还是不捕获的好吧…… = =

比如一个计算很大的循环、计算到10s你捕获到异常 难道retry重新计算还素怎么处理呢?
还有下载东西什么的= =bbb
作者: 叶舞枫    时间: 2007-6-6 05:17
发布上去 ==b
http://rpg.blue/web/htm/news747.htm
VIP 2
作者: FDR    时间: 2007-6-6 07:54
以下引用尤莉斯于2007-6-5 6:55:58的发言:
class << Graphics

alias origin_update update unless method_defined? :origin_update

def update
   Thread.critical = true
   origin_update
   Thread.critical = false
end

end
这样就不会造成闪烁了吧。。。

没细看前面的代码,如果原来没有这个保护的话那必须加上.因为如果没的话,就不仅仅是画面闪烁的问题,而是个错误.毕竟原来的update没有说明是线程安全的,多开一个线程调用它的话,很有可能在update的内部冲突.(想像update执行到一半另外一个update执行)

我说的画面闪烁来自于RGSS层面,不是update自身重复,考虑下面的步骤:

绘制近景
绘制人物
update

本来是都画好才显示到屏幕的,如果还有线程在update,那么可能刚刚绘制完近景就显示在屏幕上了.也有可能近景绘制了一半...这样的画面比起原来就是带点"闪烁"的

个人觉得Hangup异常是可以处理的.比起可能的画面破坏,自己多写点代码处理下异常没什么.异常本来就是让你处理而不是让你回避的.至于RGSS上用多线程,感觉在网络方面的应用要比这样的trick实在.
作者: 铅笔    时间: 2007-6-6 17:22
提示: 作者被禁止或删除 内容自动屏蔽
作者: 尤莉斯    时间: 2007-6-6 19:50
以下引用FDR于2007-6-5 23:54:41的发言:
没细看前面的代码,如果原来没有这个保护的话那必须加上.因为如果没的话,就不仅仅是画面闪烁的问题,而是个错误.毕竟原来的update没有说明是线程安全的,多开一个线程调用它的话,很有可能在update的内部冲突.(想像update执行到一半另外一个update执行)

我说的画面闪烁来自于RGSS层面,不是update自身重复,考虑下面的步骤:

绘制近景
绘制人物
update

本来是都画好才显示到屏幕的,如果还有线程在update,那么可能刚刚绘制完近景就显示在屏幕上了.也有可能近景绘制了一半...这样的画面比起原来就是带点"闪烁"的

个人觉得Hangup异常是可以处理的.比起可能的画面破坏,自己多写点代码处理下异常没什么.异常本来就是让你处理而不是让你回避的.至于RGSS上用多线程,感觉在网络方面的应用要比这样的trick实在.


一般游戏更新顺序是:

更新游戏数据
更新画面
sleep
跳帧处理什么的

这样的步骤的吧

而rm里更新画面就是 Graphics.update 负责处理的吧
那么不管 是否先绘制近景、然后绘制远景 然后在把缓冲区显示到屏幕上,这些都是在
Graphics.update内部完成的吧~~
那么
  Thread.critical = true
  origin_update
  Thread.critical = false
这样在进入更新画面之间就把临界区标记设置为真了,也就不会进行线程的切换,所以必须更新画面全部完成后退出临界区之后才可以切换画面的了,这样会有闪烁么?
即使在没进入临时区前发生现成切换那么也不会有影响的吧。。

至于处理那个异常,其实很多地方都不太好处理的,比如load的地图过大挂掉怎么处理?这只是一个问题而已,一般许多人又8弄脚本的就更不会去处理了= =

ps:想问问fdr一个问题,在rm里调用 Win32API 然后 网络 accept这个函数的时候~~ 它直接阻塞进程了,而不是阻塞的线程,是因为。。?


作者: FDR    时间: 2007-6-6 21:16
显示是由update完成的,但游戏画面的处理是由RGSS完成的。我说的问题出现在RGSS层面的游戏逻辑里面,不是update本身。

说得再具体一点,是Ruby脚本,脚本里面在控制游戏人物走在什么位置。单举人物的例子好了:
本来有两个sprite,下一帧需要它们平移5个单位,玩家应该看到的是两个sprite“同时”移动,本来我这样写就可以保证这个效果:

sprite1.x += 5
sprite2.x += 5
update

但是另外还有线程在update的话,sprite1移动之后就显示在屏幕上了,玩家看到了,接下来才是sprite2的移动。假如某个游戏物件是由多个sprite组合成的,譬如一个人的头和身体……这样就是错位了。我说“闪烁”是因为此过程很快,看起来像闪了一下。

除非你把整个需要update的地方包裹在critical中,这需要改动很多。你的那段代码只是保证update自身属于critical section,而不是"update游戏画面"的整个过程。

我只是要说lz帖子里面的方法可能会造成问题,理论上的,实际中要看情况。这是一个trick而不是bug fix,因此有必要让使用者了解可能的后果。对于一个游戏来说,任何时候画面都应该是不停的更新的,即使load地图也一样。这个时候load地图倒更应该作为新开的线程...

但为了简化修改,可以在load地图的时候加上这句。在load的时候保证画面的更新,在load之后要去掉。而不是在脚本的最前面加上这句,造成整个游戏过程中不可预知的问题。


===============================
至于win32api的问题我不是很清除,猜测一下……可能是因为Ruby本身解释器运行中只有1个线程负责脚本的运行,调用win32api的时候解释器本身的线程被阻塞了,因此整个都停了下来。可能Ruby的多线程不是靠OS来调度,而是靠Ruby自身的解释器,因此要停就都停下来……个人猜想而已。
作者: 尤莉斯    时间: 2007-6-6 22:44
其实我想说的是 我说不会出现闪烁是指你前面提到的那个画面绘制了一半而另外个线程update了造成闪烁的情况而已。对于你说的游戏画面由RGSS进行处理的问题,我觉得是RM根本无法避免的问题,不仅是我的那个方法而已。
我们可以把Graphics里的update方法简单分析为这样:
def update
  1、在后台缓冲区绘制画面 # 在RM里也就是所有viewport里的内容
  2、然后把后台缓冲区显示到屏幕上 # 当然这里不管是直接copy到前台还是页面翻转
end
而改为
def update
  设置临近区 # 也就是禁止线程的切换
  1、在后台缓冲区绘制画面
  2、然后把后台缓冲区显示到屏幕上
  关闭临界区 # 允许线程的切换
end
之后,那么就算是两个线程都可以update,那么也不会存在画面只描绘了一半而出现的那种闪烁的情况。

你说的那个闪烁我觉得是无法避免的:游戏的运行过程我们这里这样认为:

主循环
  A、更新游戏数据
  B、Graphics.update
  C、线程休眠限制帧率
循环结束
那么你说的那个sprite1.x += 5 sprite2.x += 5就属于A阶段里面处理的内容,加入此时顺序为

sprite1.x += 5
另外线程插入执行然后调用B运行  # 此时也只是造成了看着1比2先显示这点而已,不会出现画面部分显示的情况
sprite2.x += 5

我说RM无法避免这个情况是这样的:

sprite1.x += 5
sprite2.x += 5
sprite3.x += 5

假设在执行sprite2.x += 5这条语句后已经10秒了 但前面的 A部分还没完,也就是没执行的刷新画面的B处,那么RM这样必然挂掉。所以我们就采用了那个极端的方法在这里强制刷新一次画面而已。在RM里不是也有这样情况么:
一般采用一个计数count

if count > 一个定值
  Graphics.update
  count = 0
end

也就是防止挂掉而已,其实和线程处理的功效是一样的。

再如果我们也这样处理:

设置临界区
sprite1.x += 5
sprite2.x += 5
退出临界区

这样可以保证在执行这两条语句之间另外的线程不会执行,但我们不能保证在执行上一条后程序就挂掉了。。= = ,所以唯一能做的就是只能强制刷新一次画面而已。。= =



作者: 鐘聲    时间: 2007-6-9 21:27
提示: 作者被禁止或删除 内容自动屏蔽
作者: 轮回者    时间: 2007-6-9 23:48
夏大人...ORZ
作者: diao8    时间: 2007-7-26 23:27
提示: 作者被禁止或删除 内容自动屏蔽
作者: ky52879    时间: 2009-3-11 22:03
郁闷 完全听不懂 我是新手中的新手。。。{/dk}
作者: 是将赴各    时间: 2009-3-15 03:50
提示: 作者被禁止或删除 内容自动屏蔽




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