赞 | 32 |
VIP | 44 |
好人卡 | 11 |
积分 | 17 |
经验 | 81722 |
最后登录 | 2020-3-23 |
在线时间 | 1357 小时 |
Lv3.寻梦者 唯一的信徒
- 梦石
- 0
- 星屑
- 1665
- 在线时间
- 1357 小时
- 注册时间
- 2013-1-29
- 帖子
- 1637
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 LBQ 于 2017-3-10 12:11 编辑
Note:这个主要是 Love2d 的 Hump.timer 库的 Ruby 版本,目标是用给自己写脚本无脑直接用渐变移动之类的,主要是“工具库”而不是“轮子库”,但是 Fiber 干这种事情很多人都知道所以,这个很大程度上只是方便用的工具,所以也会带补间功能之类的……
问题:
从事件转移过来的写法
1. 做 A 这件事
2. 等 30 帧
3. 做 B 这件事
写成了
do_a 30.times {Graphics.update} do_b
do_a
30.times {Graphics.update}
do_b
从这里引申出来的问题
- 25.times do |x|
- xxx.opacity -= 10
- Graphics.update
- end
复制代码
这些都是从长远来看不合适的,而最自然的解决方式就是用 Fiber 了,这也是事件解析器中能够制造出“等待”效果的原理。
那么 Blockade 就是以 Fiber 为原材料制造的一个小的库,顺便借用了别人写的 tween 让 RM 也容易写出一些非线性的补间帧。
使用说明:
生成 Blockade 实例之后,对实例需要在每帧使用 update。
例子:
class Scene_Demo < Scene_Base def start super @blockade = Blockade.new @blockade.schedule do |f| msgbox "player enters" wait 60 # 等待 60 帧 msgbox "player dies" wait 80 # 等待 80 帧 SceneManager.return end end def update super @blockade.update end end
class Scene_Demo < Scene_Base
def start
super
@blockade = Blockade.new
@blockade.schedule do |f|
msgbox "player enters"
wait 60 # 等待 60 帧
msgbox "player dies"
wait 80 # 等待 80 帧
SceneManager.return
end
end
def update
super
@blockade.update
end
end
好处就是可以同时运行多个,随意嵌套。
@blockade.schedule do |f| msgbox "player enters" wait 60 # 等待 60 帧 @blockade.schedule do |f| msgbox "haha" wait 40 msgbox "bye" end wait 80 # 等待 80 帧 SceneManager.return end
@blockade.schedule do |f|
msgbox "player enters"
wait 60 # 等待 60 帧
@blockade.schedule do |f|
msgbox "haha"
wait 40
msgbox "bye"
end
wait 80 # 等待 80 帧
SceneManager.return
end
还有其他一些方法:
@blockade.every(60) {|f| puts "hi"} # 每 60 帧执行 @blockade.after(120) {|f| puts "hi"} # 在 120 帧之后执行 @blockade.during(20..30) {|f| puts "hi"} #只在 20 到 30 帧时执行
@blockade.every(60) {|f| puts "hi"} # 每 60 帧执行
@blockade.after(120) {|f| puts "hi"} # 在 120 帧之后执行
@blockade.during(20..30) {|f| puts "hi"} #只在 20 到 30 帧时执行
补间:
直接盗用了别人写的 Easing 库,在这里可以这么用:
# 在 Scene 初始化时候 @sprite = Sprite.new @sprite.bitmap = Cache.battler("Slime",0 ) @sprite.opacity = 0 @blockade.tween(:ease_in_quad, @sprite, [:x, :y, :opacity], [200, 300, 255], 60) # 用 ease_in_quad 补间函数,将 @sprite 的 x, y, opacity 属性分别在 60 帧内补间到 200, 300, 255 的值
# 在 Scene 初始化时候
@sprite = Sprite.new
@sprite.bitmap = Cache.battler("Slime",0 )
@sprite.opacity = 0
@blockade.tween(:ease_in_quad, @sprite, [:x, :y, :opacity], [200, 300, 255], 60) # 用 ease_in_quad 补间函数,将 @sprite 的 x, y, opacity 属性分别在 60 帧内补间到 200, 300, 255 的值
所有补间函数为了方便放在下面:
- def linear_tween(t, b, c, d)
- def ease_in_quad(t, b, c, d)
- def ease_out_quad(t, b, c, d)
- def ease_in_out_quad(t, b, c, d)
- def ease_in_cubic(t, b, c, d)
- def ease_out_cubic(t, b, c, d)
- def ease_in_out_cubic(t, b, c, d)
- def ease_in_quart(t, b, c, d)
- def ease_out_quart(t, b, c, d)
- def ease_in_out_quart(t, b, c, d)
- def ease_in_quint(t, b, c, d)
- def ease_out_quint(t, b, c, d)
- def ease_in_out_quint(t, b, c, d)
- def ease_in_sine(t, b, c, d)
- def ease_out_sine(t, b, c, d)
- def ease_in_out_sine(t, b, c, d)
- def ease_in_expo(t, b, c, d)
- def ease_out_expo(t, b, c, d)
- def ease_in_out_expo(t, b, c, d)
- def ease_in_circ(t, b, c, d)
- def ease_out_circ(t, b, c, d)
- def ease_in_out_circ(t, b, c, d)
复制代码
Block 参数
所给出的 f 参数是用于进行多线程的 fiber 实例,有 timer 属性可以用来计时。
@blockade.schedule do |f| p f.timer #0 wait(60) p f.timer #60 end
@blockade.schedule do |f|
p f.timer #0
wait(60)
p f.timer #60
end
脚本如下:
=begin Blockade.rb | Port of HUMP.timer to Ruby, aimed at RGSS === Early Beta v0.1.0 ============================================================ It is essentially my attempt to abstract away fiber for async code. This script is licensed under the APACHE-2.0 license, see http://www.apache.org/licenses/LICENSE-2.0.html for more details. Heavily inspired by HUMP.timer for Love2d: http://hump.readthedocs.io/en/latest/timer.html =end =begin This script uses the easying methods ported to Ruby by Damián Silvani, see https://github.com/munshkr/easing-ruby for more details =end =begin Copyright (c) 2013 Damián Silvani MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =end module Easing extend self def linear_tween(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f c * t / d + b end def ease_in_quad(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c*(t/=d)*t + b; end def ease_out_quad(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return -c *(t/=d)*(t-2) + b; end def ease_in_out_quad(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f t /= d / 2 return c / 2*t*t + b if (t < 1) t -= 1 return -c/2 * (t*(t-2) - 1) + b end def ease_in_cubic(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c*(t/=d)*t*t + b end def ease_out_cubic(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c*((t=t/d-1)*t*t + 1) + b end def ease_in_out_cubic(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c/2*t*t*t + b if ((t/=d/2) < 1) return c/2*((t-=2)*t*t + 2) + b end def ease_in_quart(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c*(t/=d)*t*t*t + b end def ease_out_quart(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return -c * ((t=t/d-1)*t*t*t - 1) + b end def ease_in_out_quart(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c/2*t*t*t*t + b if ((t/=d/2) < 1) return -c/2 * ((t-=2)*t*t*t - 2) + b end def ease_in_quint(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c*(t/=d)*t*t*t*t + b end def ease_out_quint(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c*((t=t/d-1)*t*t*t*t + 1) + b end def ease_in_out_quint(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c/2*t*t*t*t*t + b if ((t/=d/2) < 1) return c/2*((t-=2)*t*t*t*t + 2) + b end def ease_in_sine(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return -c * Math.cos(t/d * (Math::PI/2)) + c + b end def ease_out_sine(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c * Math.sin(t/d * (Math::PI/2)) + b end def ease_in_out_sine(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return -c/2 * (Math.cos(Math::PI*t/d) - 1) + b end def ease_in_expo(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return (t==0) ? b : c * (2 ** (10 * (t/d - 1))) + b end def ease_out_expo(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return (t==d) ? b+c : c * (-2**(-10 * t/d) + 1) + b end def ease_in_out_expo(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return b if t == 0 return b + c if t == d return (c/2) * 2**(10 * (t-1)) + b if ((t /= d/2) < 1) return (c/2) * (-2**(-10 * t-=1) + 2) + b end def ease_in_circ(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b end def ease_out_circ(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return c * Math.sqrt(1 - (t=t/d-1)*t) + b end def ease_in_out_circ(t, b, c, d) t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f return -c/2 * (Math.sqrt(1 - t*t) - 1) + b if ((t/=d/2) < 1) return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b end end class Blockade class BFiber < Fiber attr_reader :alive attr_accessor :timer def initialize *args super *args @alive = true @timer = 0 end def resume *args begin super *args @timer += 1 rescue FiberError => e @alive = false end end def alive? @alive end end def initialize @pool = [] @timer = 0 end def fmap_tween meth, start, final, t, d delta = start.zip(final).map{|e| e[1] - e[0]} start.zip(delta).map do |tup| Easing.send(meth, t, *tup, d) end end def tween meth, obj, attrs, final, d initial = attrs.map{|sym| obj.send(sym)} schedule do |f| loop do timer = f.timer ans = fmap_tween meth, initial, final, timer, d ans.each_with_index do |rhs, i| obj.send("#{attrs[i]}=", rhs) end yield f, obj if block_given? if timer < d wait else break end end end end def schedule &b b.binding.eval <<-HERE def wait t=1 t.times {Fiber.yield} end HERE ele = BFiber.new(&b) @pool << ele return ele.hash end def after time, &b schedule do |tick| wait(time) tick.timer -= time b[tick] end end def every time, &b schedule do |tick| loop do if tick.timer % time == 0 instance_exec tick, &b end Fiber.yield end end end def during range, &b schedule do |tick| loop do if range.include?(tick.timer) instance_exec tick, &b end Fiber.yield end end end def cancel k @pool.delete_if{|it| it.hash == k} end def clear @pool = [] end def update @pool.each do |f| f.resume(f) end @pool.keep_if{|v| v.alive?} @timer += 1 end end
=begin
Blockade.rb | Port of HUMP.timer to Ruby, aimed at RGSS
===
Early Beta v0.1.0
============================================================
It is essentially my attempt to abstract away fiber for async code.
This script is licensed under the APACHE-2.0 license, see
http://www.apache.org/licenses/LICENSE-2.0.html for more details.
Heavily inspired by HUMP.timer for Love2d:
http://hump.readthedocs.io/en/latest/timer.html
=end
=begin
This script uses the easying methods ported to Ruby by Damián Silvani,
see https://github.com/munshkr/easing-ruby for more details
=end
=begin
Copyright (c) 2013 Damián Silvani
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=end
module Easing
extend self
def linear_tween(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
c * t / d + b
end
def ease_in_quad(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c*(t/=d)*t + b;
end
def ease_out_quad(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return -c *(t/=d)*(t-2) + b;
end
def ease_in_out_quad(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
t /= d / 2
return c / 2*t*t + b if (t < 1)
t -= 1
return -c/2 * (t*(t-2) - 1) + b
end
def ease_in_cubic(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c*(t/=d)*t*t + b
end
def ease_out_cubic(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c*((t=t/d-1)*t*t + 1) + b
end
def ease_in_out_cubic(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c/2*t*t*t + b if ((t/=d/2) < 1)
return c/2*((t-=2)*t*t + 2) + b
end
def ease_in_quart(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c*(t/=d)*t*t*t + b
end
def ease_out_quart(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return -c * ((t=t/d-1)*t*t*t - 1) + b
end
def ease_in_out_quart(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c/2*t*t*t*t + b if ((t/=d/2) < 1)
return -c/2 * ((t-=2)*t*t*t - 2) + b
end
def ease_in_quint(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c*(t/=d)*t*t*t*t + b
end
def ease_out_quint(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c*((t=t/d-1)*t*t*t*t + 1) + b
end
def ease_in_out_quint(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c/2*t*t*t*t*t + b if ((t/=d/2) < 1)
return c/2*((t-=2)*t*t*t*t + 2) + b
end
def ease_in_sine(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return -c * Math.cos(t/d * (Math::PI/2)) + c + b
end
def ease_out_sine(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c * Math.sin(t/d * (Math::PI/2)) + b
end
def ease_in_out_sine(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return -c/2 * (Math.cos(Math::PI*t/d) - 1) + b
end
def ease_in_expo(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return (t==0) ? b : c * (2 ** (10 * (t/d - 1))) + b
end
def ease_out_expo(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return (t==d) ? b+c : c * (-2**(-10 * t/d) + 1) + b
end
def ease_in_out_expo(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return b if t == 0
return b + c if t == d
return (c/2) * 2**(10 * (t-1)) + b if ((t /= d/2) < 1)
return (c/2) * (-2**(-10 * t-=1) + 2) + b
end
def ease_in_circ(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b
end
def ease_out_circ(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return c * Math.sqrt(1 - (t=t/d-1)*t) + b
end
def ease_in_out_circ(t, b, c, d)
t = t.to_f; b = b.to_f; c = c.to_f; d = d.to_f
return -c/2 * (Math.sqrt(1 - t*t) - 1) + b if ((t/=d/2) < 1)
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b
end
end
class Blockade
class BFiber < Fiber
attr_reader :alive
attr_accessor :timer
def initialize *args
super *args
@alive = true
@timer = 0
end
def resume *args
begin
super *args
@timer += 1
rescue FiberError => e
@alive = false
end
end
def alive?
@alive
end
end
def initialize
@pool = []
@timer = 0
end
def fmap_tween meth, start, final, t, d
delta = start.zip(final).map{|e| e[1] - e[0]}
start.zip(delta).map do |tup|
Easing.send(meth, t, *tup, d)
end
end
def tween meth, obj, attrs, final, d
initial = attrs.map{|sym| obj.send(sym)}
schedule do |f|
loop do
timer = f.timer
ans = fmap_tween meth, initial, final, timer, d
ans.each_with_index do |rhs, i|
obj.send("#{attrs[i]}=", rhs)
end
yield f, obj if block_given?
if timer < d
wait
else
break
end
end
end
end
def schedule &b
b.binding.eval <<-HERE
def wait t=1
t.times {Fiber.yield}
end
HERE
ele = BFiber.new(&b)
@pool << ele
return ele.hash
end
def after time, &b
schedule do |tick|
wait(time)
tick.timer -= time
b[tick]
end
end
def every time, &b
schedule do |tick|
loop do
if tick.timer % time == 0
instance_exec tick, &b
end
Fiber.yield
end
end
end
def during range, &b
schedule do |tick|
loop do
if range.include?(tick.timer)
instance_exec tick, &b
end
Fiber.yield
end
end
end
def cancel k
@pool.delete_if{|it| it.hash == k}
end
def clear
@pool = []
end
def update
@pool.each do |f|
f.resume(f)
end
@pool.keep_if{|v| v.alive?}
@timer += 1
end
end
觉得也许每个 Fiber 都应该是一个类 Blockade 的实例…… 但是现在大脑不清醒于是先这么写着,出了 bug 有问题希望告诉我一下这样我能修改…… |
评分
-
查看全部评分
|