设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 1445|回复: 6
打印 上一主题 下一主题

[RMVA发布] 【轮子】Blockade,Fiber 的无脑抽象

[复制链接]

Lv3.寻梦者

唯一的信徒

梦石
0
星屑
1665
在线时间
1357 小时
注册时间
2013-1-29
帖子
1637
跳转到指定楼层
1
发表于 2017-3-10 08:20:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 LBQ 于 2017-3-10 12:11 编辑

Note:这个主要是 Love2d 的 Hump.timer 库的 Ruby 版本,目标是用给自己写脚本无脑直接用渐变移动之类的,主要是“工具库”而不是“轮子库”,但是 Fiber 干这种事情很多人都知道所以,这个很大程度上只是方便用的工具,所以也会带补间功能之类的……
问题:
从事件转移过来的写法
1. 做 A 这件事
2. 等 30 帧
3. 做 B 这件事

写成了
RUBY 代码复制
  1. do_a
  2. 30.times {Graphics.update}
  3. do_b


从这里引申出来的问题
  1. 25.times do |x|
  2.   xxx.opacity -= 10
  3.   Graphics.update
  4. end
复制代码


这些都是从长远来看不合适的,而最自然的解决方式就是用 Fiber 了,这也是事件解析器中能够制造出“等待”效果的原理。

那么 Blockade 就是以 Fiber 为原材料制造的一个小的库,顺便借用了别人写的 tween 让 RM 也容易写出一些非线性的补间帧。

使用说明:
生成 Blockade 实例之后,对实例需要在每帧使用 update。
例子:

RUBY 代码复制
  1. class Scene_Demo < Scene_Base
  2.   def start
  3.     super
  4.     @blockade = Blockade.new
  5.     @blockade.schedule do |f|
  6.       msgbox "player enters"
  7.       wait 60 # 等待 60 帧
  8.       msgbox "player dies"
  9.       wait 80 # 等待 80 帧
  10.       SceneManager.return
  11.     end
  12.   end
  13.  
  14.   def update
  15.     super
  16.     @blockade.update
  17.   end
  18. end


好处就是可以同时运行多个,随意嵌套。
RUBY 代码复制
  1. @blockade.schedule do |f|
  2.       msgbox "player enters"
  3.       wait 60 # 等待 60 帧
  4.       @blockade.schedule do |f|
  5.         msgbox "haha"
  6.         wait 40
  7.         msgbox "bye"
  8.       end
  9.       wait 80 # 等待 80 帧
  10.       SceneManager.return
  11.     end


还有其他一些方法:
RUBY 代码复制
  1. @blockade.every(60) {|f| puts "hi"} # 每 60 帧执行
  2. @blockade.after(120) {|f| puts "hi"} # 在 120 帧之后执行
  3. @blockade.during(20..30) {|f| puts "hi"} #只在 20 到 30 帧时执行


补间:
直接盗用了别人写的 Easing 库,在这里可以这么用:
RUBY 代码复制
  1. # 在 Scene 初始化时候
  2. @sprite = Sprite.new
  3. @sprite.bitmap = Cache.battler("Slime",0 )
  4. @sprite.opacity = 0
  5. @blockade.tween(:ease_in_quad, @sprite, [:x, :y, :opacity], [200, 300, 255], 60) # 用 ease_in_quad 补间函数,将 @sprite 的 x, y, opacity 属性分别在 60 帧内补间到 200, 300, 255 的值


所有补间函数为了方便放在下面:

  1.   def linear_tween(t, b, c, d)
  2.   def ease_in_quad(t, b, c, d)
  3.   def ease_out_quad(t, b, c, d)
  4.   def ease_in_out_quad(t, b, c, d)
  5.   def ease_in_cubic(t, b, c, d)
  6.   def ease_out_cubic(t, b, c, d)
  7.   def ease_in_out_cubic(t, b, c, d)
  8.   def ease_in_quart(t, b, c, d)
  9.   def ease_out_quart(t, b, c, d)
  10.   def ease_in_out_quart(t, b, c, d)
  11.   def ease_in_quint(t, b, c, d)
  12.   def ease_out_quint(t, b, c, d)
  13.   def ease_in_out_quint(t, b, c, d)
  14.   def ease_in_sine(t, b, c, d)
  15.   def ease_out_sine(t, b, c, d)
  16.   def ease_in_out_sine(t, b, c, d)
  17.   def ease_in_expo(t, b, c, d)
  18.   def ease_out_expo(t, b, c, d)
  19.   def ease_in_out_expo(t, b, c, d)
  20.   def ease_in_circ(t, b, c, d)
  21.   def ease_out_circ(t, b, c, d)
  22.   def ease_in_out_circ(t, b, c, d)
复制代码


Block 参数

  1. schedule {|f|
复制代码

所给出的 f 参数是用于进行多线程的 fiber 实例,有 timer 属性可以用来计时。

RUBY 代码复制
  1. @blockade.schedule do |f|
  2.   p f.timer #0
  3.   wait(60)
  4.   p f.timer #60
  5. end


脚本如下:
RUBY 代码复制
  1. =begin
  2. Blockade.rb | Port of HUMP.timer to Ruby, aimed at RGSS
  3. ===
  4. Early Beta v0.1.0
  5. ============================================================
  6. It is essentially my attempt to abstract away fiber for async code.
  7.  
  8. This script is licensed under the APACHE-2.0 license, see
  9. http://www.apache.org/licenses/LICENSE-2.0.html for more details.
  10.  
  11. Heavily inspired by HUMP.timer for Love2d:
  12. http://hump.readthedocs.io/en/latest/timer.html
  13. =end
  14.  
  15. =begin
  16. This script uses the easying methods ported to Ruby by Damián Silvani,
  17. see https://github.com/munshkr/easing-ruby for more details
  18. =end
  19.  
  20. =begin
  21. Copyright (c) 2013 Damián Silvani
  22.  
  23. MIT License
  24.  
  25. Permission is hereby granted, free of charge, to any person obtaining
  26. a copy of this software and associated documentation files (the
  27. "Software"), to deal in the Software without restriction, including
  28. without limitation the rights to use, copy, modify, merge, publish,
  29. distribute, sublicense, and/or sell copies of the Software, and to
  30. permit persons to whom the Software is furnished to do so, subject to
  31. the following conditions:
  32.  
  33. The above copyright notice and this permission notice shall be
  34. included in all copies or substantial portions of the Software.
  35.  
  36. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  37. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  38. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  39. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  40. LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  41. OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  42. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  43. =end
  44.  
  45. module Easing
  46.   extend self
  47.  
  48.   def linear_tween(t, b, c, d)
  49.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  50.  
  51.     c * t / d + b
  52.   end
  53.  
  54.   def ease_in_quad(t, b, c, d)
  55.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  56.  
  57.     return c*(t/=d)*t + b;
  58.   end
  59.  
  60.   def ease_out_quad(t, b, c, d)
  61.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  62.  
  63.     return -c *(t/=d)*(t-2) + b;
  64.   end
  65.  
  66.   def ease_in_out_quad(t, b, c, d)
  67.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  68.  
  69.     t /= d / 2
  70.     return c / 2*t*t + b if (t < 1)
  71.     t -= 1
  72.     return -c/2 * (t*(t-2) - 1) + b
  73.   end
  74.  
  75.   def ease_in_cubic(t, b, c, d)
  76.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  77.  
  78.     return c*(t/=d)*t*t + b
  79.   end
  80.  
  81.   def ease_out_cubic(t, b, c, d)
  82.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  83.  
  84.     return c*((t=t/d-1)*t*t + 1) + b
  85.   end
  86.  
  87.   def ease_in_out_cubic(t, b, c, d)
  88.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  89.  
  90.     return c/2*t*t*t + b if ((t/=d/2) < 1)
  91.     return c/2*((t-=2)*t*t + 2) + b
  92.   end
  93.  
  94.   def ease_in_quart(t, b, c, d)
  95.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  96.  
  97.     return c*(t/=d)*t*t*t + b
  98.   end
  99.  
  100.   def ease_out_quart(t, b, c, d)
  101.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  102.  
  103.     return -c * ((t=t/d-1)*t*t*t - 1) + b
  104.   end
  105.  
  106.   def ease_in_out_quart(t, b, c, d)
  107.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  108.  
  109.     return c/2*t*t*t*t + b if ((t/=d/2) < 1)
  110.     return -c/2 * ((t-=2)*t*t*t - 2) + b
  111.   end
  112.  
  113.   def ease_in_quint(t, b, c, d)
  114.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  115.  
  116.     return c*(t/=d)*t*t*t*t + b
  117.   end
  118.  
  119.   def ease_out_quint(t, b, c, d)
  120.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  121.  
  122.     return c*((t=t/d-1)*t*t*t*t + 1) + b
  123.   end
  124.  
  125.   def ease_in_out_quint(t, b, c, d)
  126.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  127.  
  128.     return c/2*t*t*t*t*t + b if ((t/=d/2) < 1)
  129.     return c/2*((t-=2)*t*t*t*t + 2) + b
  130.   end
  131.  
  132.   def ease_in_sine(t, b, c, d)
  133.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  134.  
  135.     return -c * Math.cos(t/d * (Math::PI/2)) + c + b
  136.   end
  137.  
  138.   def ease_out_sine(t, b, c, d)
  139.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  140.  
  141.     return c * Math.sin(t/d * (Math::PI/2)) + b
  142.   end
  143.  
  144.   def ease_in_out_sine(t, b, c, d)
  145.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  146.  
  147.     return -c/2 * (Math.cos(Math::PI*t/d) - 1) + b
  148.   end
  149.  
  150.   def ease_in_expo(t, b, c, d)
  151.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  152.  
  153.     return (t==0) ? b : c * (2 ** (10 * (t/d - 1))) + b
  154.   end
  155.  
  156.   def ease_out_expo(t, b, c, d)
  157.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  158.  
  159.     return (t==d) ? b+c : c * (-2**(-10 * t/d) + 1) + b
  160.   end
  161.  
  162.   def ease_in_out_expo(t, b, c, d)
  163.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  164.  
  165.     return b if t == 0
  166.     return b + c if t == d
  167.     return (c/2) * 2**(10 * (t-1)) + b if ((t /= d/2) < 1)
  168.     return (c/2) * (-2**(-10 * t-=1) + 2) + b
  169.   end
  170.  
  171.   def ease_in_circ(t, b, c, d)
  172.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  173.  
  174.     return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b
  175.   end
  176.  
  177.   def ease_out_circ(t, b, c, d)
  178.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  179.  
  180.     return c * Math.sqrt(1 - (t=t/d-1)*t) + b
  181.   end
  182.  
  183.   def ease_in_out_circ(t, b, c, d)
  184.     t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
  185.  
  186.     return -c/2 * (Math.sqrt(1 - t*t) - 1) + b if ((t/=d/2) < 1)
  187.     return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b
  188.   end
  189. end
  190.  
  191. class Blockade
  192.   class BFiber < Fiber
  193.     attr_reader :alive
  194.     attr_accessor :timer
  195.     def initialize *args
  196.       super *args
  197.       @alive = true
  198.       @timer = 0
  199.     end
  200.  
  201.     def resume *args
  202.       begin
  203.         super *args
  204.         @timer += 1
  205.       rescue FiberError => e
  206.         @alive = false
  207.       end
  208.     end
  209.  
  210.     def alive?
  211.       @alive
  212.     end
  213.   end
  214.  
  215.   def initialize
  216.     @pool = []
  217.     @timer = 0
  218.   end
  219.  
  220.   def fmap_tween meth, start, final, t, d
  221.     delta = start.zip(final).map{|e| e[1] - e[0]}
  222.     start.zip(delta).map do |tup|
  223.       Easing.send(meth, t, *tup, d)
  224.     end
  225.   end
  226.  
  227.   def tween meth, obj, attrs, final, d
  228.     initial = attrs.map{|sym| obj.send(sym)}
  229.     schedule do |f|
  230.       loop do
  231.         timer = f.timer
  232.         ans = fmap_tween meth, initial, final, timer, d
  233.         ans.each_with_index do |rhs, i|
  234.           obj.send("#{attrs[i]}=", rhs)
  235.         end
  236.         yield f, obj if block_given?
  237.         if timer < d
  238.           wait
  239.         else
  240.           break
  241.         end
  242.       end
  243.     end
  244.   end
  245.  
  246.   def schedule &b
  247.     b.binding.eval <<-HERE
  248.     def wait t=1
  249.       t.times {Fiber.yield}
  250.     end
  251.     HERE
  252.     ele = BFiber.new(&b)
  253.     @pool << ele
  254.     return ele.hash
  255.   end
  256.  
  257.   def after time, &b  
  258.     schedule do |tick|
  259.       wait(time)
  260.       tick.timer -= time
  261.       b[tick]
  262.     end
  263.   end
  264.  
  265.   def every time, &b
  266.     schedule do |tick|
  267.       loop do
  268.         if tick.timer % time == 0
  269.           instance_exec tick, &b
  270.         end
  271.         Fiber.yield
  272.       end
  273.     end
  274.   end
  275.  
  276.   def during range, &b
  277.     schedule do |tick|
  278.       loop do
  279.         if range.include?(tick.timer)
  280.           instance_exec tick, &b
  281.         end
  282.         Fiber.yield
  283.       end
  284.     end
  285.   end
  286.  
  287.   def cancel k
  288.     @pool.delete_if{|it| it.hash == k}
  289.   end
  290.  
  291.   def clear
  292.     @pool = []
  293.   end
  294.  
  295.   def update
  296.     @pool.each do |f|
  297.       f.resume(f)
  298.     end
  299.     @pool.keep_if{|v| v.alive?}
  300.     @timer += 1
  301.   end
  302. end


觉得也许每个 Fiber 都应该是一个类 Blockade 的实例…… 但是现在大脑不清醒于是先这么写着,出了 bug 有问题希望告诉我一下这样我能修改……

评分

参与人数 2星屑 +132 收起 理由
zaiy2863 + 66 轮子
fux2 + 66 不懂反正醋虾

查看全部评分

『我只是一个正在潜心修炼的渣乐师罢了』
Dear Time\(^o^)/~


假如上面的图片挂了的话麻烦各位去发个帖 @ 一下 orzFly 让他修复 deartime

Lv3.寻梦者

梦石
0
星屑
4583
在线时间
1205 小时
注册时间
2016-4-7
帖子
982

开拓者

2
发表于 2017-3-10 11:05:57 | 只看该作者
噗 和我的 svent 类似嘛
不过我那个是事件处理方向上的
还有 可以和大佬交流交流frp么

点评

是的,ES2015有和fiber类似的generator  发表于 2017-3-10 12:39
LBQ
那么我也不是特别清楚,我记得 ES2015 是有 Generator 的所以也许可以去用 Generator 写…… 但是我真的不清楚  发表于 2017-3-10 12:13
最近想做新的事件处理(主要是js没fiber囧)打算换到frp之类的。  发表于 2017-3-10 12:07
LBQ
唉果然写的人多(,frp 啥的找专门的 FP 群吧我也只是知道很少的东西……  发表于 2017-3-10 12:04
附庸的附庸不是我的附庸,女儿的女儿还是我的女儿。CK2沉迷ing
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
4583
在线时间
1205 小时
注册时间
2016-4-7
帖子
982

开拓者

3
发表于 2017-3-19 10:25:16 | 只看该作者
本帖最后由 shitake 于 2017-3-19 10:44 编辑

@喵呜喵5  @LBQ
然而 ES6 的 generator  设计我觉得是有很大问题。他不像 ruby 的 fiber 那么灵活和自由。我不知道是否有什么办法可以越过这个限制。
关于这点的细节我记得好像在这里有提到过: https://github.com/molingyu/blog/issues/1

不过用 FRP 的话可以直接写原生的 ES5,这可以和 RMMV 保持一致。所以如何用 generator 来解决这个问题我就没再钻研。

我在 ruby-fiber 的异步事件处理 API 大概如下:

RUBY 代码复制
  1. button.on('click') do |info, helper|
  2.   p info
  3.   helper.wait(3)
  4.   p 'wait 3sec'
  5. end


这里的 wait 内部就有个 fiber.yield。除此之外很有一些异步处理的 helper 方法(times、filter 之类)。原理大都是内部放个 fiber.yield,在必要的时候将这个回调函数挂起。

如果用 js-FRP 的话,API 差不多这样:

JAVASCRIPT 代码复制
  1. button.on('click', function(info){ console.log(info) }).wait(3, function(info){ console.log('wait 3sec') })


这里的 on、wait 返回的都是一个 eventStream。

感觉两者没多大差别。而且用 FRP 的话也不需要麻烦的每帧迭代了(wait 的话直接用 setTimeout)。
附庸的附庸不是我的附庸,女儿的女儿还是我的女儿。CK2沉迷ing
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-4-29 17:22

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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