Project1

标题: RGSS1 的「又一」坑人 BUG [打印本页]

作者: RyanBern    时间: 2019-8-25 10:41
标题: RGSS1 的「又一」坑人 BUG
本帖最后由 RyanBern 于 2019-8-25 10:44 编辑

不知道大家在战斗中触发事件时有没有这样的体会:整个游戏会出现明显卡顿。按 F2 查看 FPS 时发现帧率只有 10 左右甚至个位数。
例如使用 Steam 版的最新 RMXP 创建新工程,在没有额外添加任何脚本的情况下就会出现下面这个图的情况。



这个图中我只是用了一个显示文章的命令,游戏的帧率直接跌到个位数。如果是显示战斗动画这种肉眼可见的事件那么体感会更加明显。在事件执行完成之后,游戏的 FPS 立即恢复了正常的水平。而在地图上执行各种事件我们并没有发现什么异常。

问题就出现在 Scene_Battle 刷新机制上。打开脚本编辑器后找到 Scene_Battle 1 可以看到定义 update 方法的部分:
RUBY 代码复制
  1. def update
  2.     # If battle event is running
  3.     if $game_system.battle_interpreter.running?
  4.       # Update interpreter
  5.       $game_system.battle_interpreter.update
  6.       # If a battler which is forcing actions doesn't exist
  7.       if $game_temp.forcing_battler == nil
  8.         # If battle event has finished running
  9.         unless $game_system.battle_interpreter.running?
  10.           # Rerun battle event set up if battle continues
  11.           unless judge
  12.             setup_battle_event
  13.           end
  14.         end
  15.         # If not after battle phase
  16.         if @phase != 5
  17.           # Refresh status window
  18.           @status_window.refresh
  19.         end
  20.       end
  21.     end

刷新机制表明如果战斗事件不为空就优先刷新事件解释器。但问题就出现在这个过程的最后一步。
RUBY 代码复制
  1. if @phase != 5
  2.   @status_window.refresh
  3. end

这句话的意思是说如果战斗的阶段不在第五阶段,那么就需要对 @status_window 的内容进行重新描绘。战斗的第五阶段实际上是显示战利品,获得 EXP 的阶段,除了这个阶段以外,只要事件解释器运行,每次 update 都会重新刷新一次状态窗口。这个 @status_window 就是显示角色 HP、SP、当前状态的那个窗口。

写过 RMXP 脚本的人都知道,refresh 操作一般是对 bitmap 进行各种绘制,里面调用的方法执行起来通常是比较慢的。反复调用 refresh 操作应该极力避免。这也就不难理解为什么在战斗中执行事件会导致帧率下降了。
为了测试是否真的是这个地方的锅,可以直接将这一段注释掉,结果发现帧率直接恢复正常。



那么为啥默认系统要放一个 @status_window.refresh 在这呢?当然是因为状态窗口有时候在执行事件指令之后是需要刷新的,例如:增减 HP/SP更改角色姓名更改角色状态。不过即使在这些场合需要刷新,也不需要每帧都进行检查。最简单的修复办法就是在刷新之前增加一个判定标志,当只有执行特定事件的一瞬间才去刷新状态窗口。我们先在 Game_Temp 中增加一个刷新战斗状态窗口的标志。
RUBY 代码复制
  1. class Game_Temp
  2.   attr_accessor :battle_status_need_refresh
  3.   def initialize
  4.     # ...
  5.     @battle_status_need_refresh = false
  6.   end
  7. end

然后再需要刷新窗口的时候打开这个标志。以增减 HP 为例,它的事件指令编号是 311。
RUBY 代码复制
  1. class Interpreter
  2.   def command_311
  3.     # Get operate value
  4.     value = operate_value(@parameters[1], @parameters[2], @parameters[3])
  5.     # Process with iterator
  6.     iterate_actor(@parameters[0]) do |actor|
  7.       # If HP are not 0
  8.       if actor.hp > 0
  9.         # Change HP (if death is not permitted, make HP 1)
  10.         if @parameters[4] == false and actor.hp + value <= 0
  11.           actor.hp = 1
  12.         else
  13.           actor.hp += value
  14.         end
  15.       end
  16.     end
  17.     # Determine game over
  18.     $game_temp.gameover = $game_party.all_dead?
  19.     # 刷新状态窗口
  20.     if $game_temp.in_battle
  21.       $game_temp.battle_status_need_refresh = true
  22.     end
  23.     # Continue
  24.     return true
  25.   end
  26. end

在这个指令增加一个开启刷新标志的命令。最后就是修改 Scene_Battle 1 的刷新判定了。
RUBY 代码复制
  1. if @phase != 5 && $game_temp.battle_status_need_refresh
  2.   @status_window.refresh
  3.   $game_temp.battle_status_need_refresh = false
  4. end

大功告成。
作者: guoxiaomi    时间: 2019-8-25 15:52
23333机智的我早就注释掉了
作者: hys111111    时间: 2019-8-25 19:24
本帖最后由 hys111111 于 2019-8-25 19:28 编辑

类似的,
做ARPG事件多的时候,地图很卡,因为Game_Character下的def passable?(x,y,d)会先调用Game_Map下的def passable?(x,y,d,self_event = nil),
然而这两个类下的passable?都会执行for event in $game_map.events.values循环,很不舒服。直接不要Game_Map下的passable?了(整合到Game_Character下了,循环执行两次变为一次)。
到后面直接连check_event_trigger_there也整合到passable?里面去了
作者: e900003    时间: 2019-8-26 20:18
本帖最后由 e900003 于 2019-8-26 21:36 编辑

我目前測試  套用此設定之後 用公共事件設定造成傷害的設定
不會在造成傷害時候刷新HP跟MP
只能等下一回合之後才會刷新一次
有什麼解決辦法可以解決這個沒有自動刷新HP跟MP的問題

更新: 找到方案了,謝謝
作者: Six_Fish    时间: 2019-8-26 21:41
e900003 发表于 2019-8-26 20:18
我目前測試  套用此設定之後 用公共事件設定造成傷害的設定
不會在造成傷害時候刷新HP跟MP
只能等下一回合 ...


公共事件里用事件脚本手动$game_temp.battle_status_need_refresh = true一下就好了
-
咳,不好意思没看到已经解决了
作者: fux2    时间: 2019-8-26 22:31
这,简直强到不可思议,我一直以为是fps统计方式有问题




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