This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def run( model, period ) | |
timer_id = UI.start_timer( period, true) do # for every beat of lub dub lub dub, run below block | |
@actions.delete_if do |a| | |
a.tick( period ) # move around instance | |
a.arrived? # remove if instance arrived at target | |
end | |
model.active_view.invalidate # update scene | |
if @actions.empty? then | |
UI.stop_timer timer_id # rest in peace if nothing left | |
end | |
end | |
end |
In the code, I said the heart beats at precise interval of 'period' and you can say that I should somehow replace the timer with atom clock to get that behaviour. Well, we may meet in the middle by measuring duration; We can't make consistent period but we can get elapsed time between consequent timer calls. Refer below code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
attr_reader :last_time | |
def initialize | |
@last_time = 0.0 | |
end | |
def run( model, period ) | |
timer_id = UI.start_timer( period, true) { | |
if @last_time == 0.0 | |
@last_time = Time.now.to_f | |
elapsed_time = period | |
else | |
current_time = Time.now.to_f | |
elapsed_time = current_time - @last_time | |
@last_time = current_time | |
end | |
@actions.delete_if do |a| | |
a.tick( elapsed_time ) | |
a.arrived? | |
end | |
# snip snip | |
end |
Below is code for object translation. 'entity.transform!' updates the position. This change will be shown by model.active_view.update - search up above code.
Updating position in the middle is easy as it is moving in constant speed. Difficulty lies at the boundaries - reminding me of studying differential equations, asking for great care on boundary condition at all times. In here I have to figure out where and when to stop. I am just checking the remaining distance to the target position in here.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Translation < Action | |
attr_accessor :target, :velocity, :speed | |
def tick( duration ) | |
return if arrived? | |
ds = @speed * duration # distance to cover in this tick | |
d = (@target - @entity.transformation.origin).normalize # direction vector in unit distance | |
d.x, d.y, d.z = d.x*ds, d.y*ds, d.z*ds # distance vector toward target | |
s = @entity.transformation.origin # start position | |
e = s + d # end position | |
dt = s.distance @target # distance between start and end | |
if ds >= dt # if reached the target | |
d = @target - s; # update distance vector with remaining distance | |
@is_arrived = true | |
end | |
@entity.transform! Geom::Transformation.new( d ) # update position of SketchUp instance | |
... | |
end | |
end |
This simple approach - constant speed - makes unrealistic movements at the boundaries. It starts and stops in infinite acceleration. Better simulation should involve smooth change at ends.
Reference : Animate Yo' Cheese