加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 Cichol 于 2015-10-3 18:16 编辑
刚接触RGSS,近日在啃RM的自带脚本,对Fiber还是比较困惑的,好像论坛里搜不到Fiber的文章,就写(shui)一些我对Fiber的理解
我比较关注的是Window_Message这个类,这个类的本体就是在update里不断监听$game_message的改动,然后新建Fiber以及执行@fiber,不过暂时不管它,先用个简单点的模型去观察Fiber。
RM的体系里每帧会调用一次update,在update里做一些数据的读取和更新,重新绘图之类的事情。如果整个update只做一件事,例如:
def update recalculate_x end
def update
recalculate_x
end
每次update时重新计算x值,这是每次update都需要考虑的,可以认为是一种单一的状态,直接写在update里也十分自然。
那么非单一状态是什么样的呢?假如我们需要等待用户的输入,获取到输入的时候重绘,但是update一秒钟会调用60次,它怎么知道这次调用应该做什么呢?是应该等待输入,还是用户已经输入了,应该处理重绘?
这时候我们可以用一个状态机去处理:
def update case @current_state when :wait_input wait_input when :redraw redraw end end def wait_input if have_input? @current_state = :redraw end end def redraw draw_process @current_state = :wait_input end
def update
case @current_state
when :wait_input
wait_input
when :redraw
redraw
end
end
def wait_input
if have_input?
@current_state = :redraw
end
end
def redraw
draw_process
@current_state = :wait_input
end
等待input的时候,不改变状态,直到监听到input,把状态改为redraw,然后下一次update进行redraw。
在draw_process完成之后,把状态改回wait_input,继续监听input。
这本质上是一个input-redraw流程的循环,这个流程有wait_input和redraw两个状态。
那么看看Fiber可以如何简化这些状态,使用Fiber的等价代码:
def update if @fiber @fiber.resume else @fiber = Fiber.new {fiber_main} end end def fiber_main loop do Fiber.yield until have_input? draw_process end end
def update
if @fiber
@fiber.resume
else
@fiber = Fiber.new {fiber_main}
end
end
def fiber_main
loop do
Fiber.yield until have_input?
draw_process
end
end
除了第一次调用到update会生成一个@fiber,之后的每次调用都是简单地唤醒@fiber。
而@fiber的代码,fiber_main,描述的就是我们的input-redraw流程。等待input然后直接redraw,由于原来的程序是一个循环,这里就直接塞在loop{}里。
如果have_input?为false,Fiber的控制权会移交到外部,等待下一次resume。而下一次update的@fiber.resume又会让执行的步骤回到until have_input?这一行,再次判断有无输入。
Fiber(Coroutine)这种风格可以简化代码的逻辑,让异步的代码写起来像同步的一样,在fiber_main里代码逻辑和直观的操作流程是一样的,可以连续地写出来,不需要我们去考虑每次调用update的时候是什么状态。
理解了这些之后,再去看Window_Message的代码应该就不会迷糊了,而且也更能感受到Fiber是如何使代码简化的(参考Window_Message的fiber_main)。 |