Skip to content

Commit

Permalink
refactored timer into timer and timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
eilara committed Jun 26, 2011
1 parent e9e5951 commit bef140e
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 85 deletions.
111 changes: 37 additions & 74 deletions lib/GameFrame/Animation.pm
Expand Up @@ -16,59 +16,35 @@ package GameFrame::Animation;
# - bounce - switch from/to on cycle repeat
# - curve - easing function defines progress on path vs. time
#
# an animation is built from:
# - Timeline: for which the animation is the provider
# it calls the methods timer_tick and cycle_complete
# on this animation
# it is create with$ a cycle_limit built from the duration
# - Proxy: the connection to the target, on it we get/set the
# animated value, and consult it concerning the animation
# resolution for int optimization
#
# all the animation does is create the 2 correctly, and then
# convert calls from the timeline into values on the proxy

use Moose;
use Scalar::Util qw(weaken);
use MooseX::Types::Moose qw(Bool Num Int Str ArrayRef);
use GameFrame::MooseX;
use aliased 'GameFrame::Animation::Timer';
use aliased 'GameFrame::Animation::Timeline';
use aliased 'GameFrame::Animation::CycleLimit';
use aliased 'GameFrame::Animation::Curve';
use aliased 'GameFrame::Animation::Proxy::Factory' => 'ProxyFactory';
use aliased 'GameFrame::Animation::Proxy';

use aliased 'Coro::Signal';

has duration => (is => 'ro', isa => 'Num' , lazy_build => 1);
has from_to => (is => 'ro', isa => 'ArrayRef', lazy_build => 1);

has speed => (is => 'ro', isa => 'Num'); # optional instead of duration
has to => (is => 'ro'); # optional instead of from_to

has forever => (is => 'ro', isa => Bool, default => 0);
has bounce => (is => 'ro', isa => Bool, default => 0);
has curve => (is => 'ro', isa => Str , default => 'linear');

# filpped on bounce
has is_reversed_dir => (
traits => ['Bool'],
is => 'rw',
isa => 'Bool',
default => 0,
handles => {toggle_dir => 'toggle', set_forward_dir => 'unset'},
);

# repeat counter decreases by 1 every cycle iteration
has repeat => (
traits => ['Counter'],
is => 'ro',
isa => 'Int',
default => 1,
handles => {dec_repeat => 'dec'},
);

# signals to outside listener of animation complete, wait for it with
# wait_for_animation_complete() method
has animation_complete_signal => (
is => 'ro',
default => sub { Signal->new },
handles => {
wait_for_animation_complete => 'wait',
broadcast_animation_complete => 'broadcast',
},
);
has duration => (is => 'ro', isa => 'Num' , lazy_build => 1);
has from_to => (is => 'ro', isa => 'ArrayRef', lazy_build => 1);
has speed => (is => 'ro', isa => 'Num'); # optional instead of duration
has to => (is => 'ro'); # optional instead of from_to
has curve => (is => 'ro', isa => Str , default => 'linear');

compose_from Timer,
compose_from Timeline,
inject => sub {
my $self = shift;
weaken $self; # don't want args to hold strong ref to self
Expand All @@ -79,12 +55,14 @@ compose_from Timer,
);
},
has => {handles => {
start_animation => 'start',
restart_animation => 'restart',
stop_timer => 'stop',
pause_animation => 'pause',
resume_animation => 'resume',
is_animation_started => 'is_timer_active',
start_animation => 'start',
restart_animation => 'restart',
stop_animation => 'stop',
pause_animation => 'pause',
resume_animation => 'resume',
is_animation_started => 'is_timer_active',
wait_for_animation_complete => 'wait_for_animation_complete',
is_reversed_dir => 'is_reversed_dir',
},
};

Expand Down Expand Up @@ -128,12 +106,6 @@ sub _compute_timer_sleep {
return $self->compute_timer_sleep($self->_compute_speed);
}

sub stop_animation {
my $self = shift;
$self->stop_timer;
$self->_animation_complete;
}

sub timer_tick {
my ($self, $elapsed) = @_;
my $new_value = $self->compute_value_at($elapsed);
Expand All @@ -143,31 +115,13 @@ sub timer_tick {
sub cycle_complete {
my $self = shift;
$self->set_attribute_final_value;
if ($self->forever or $self->repeat > 1) {
$self->dec_repeat unless $self->forever;
$self->reverse_dir if $self->bounce;
$self->restart_animation;
} else {
$self->_animation_complete;
}
}

sub _animation_complete {
my $self = shift;
$self->set_forward_dir;
$self->broadcast_animation_complete;
}

sub set_attribute_final_value {
my $self = shift;
$self->set_attribute_value($self->compute_final_value);
}

sub reverse_dir {
my $self = shift;
$self->toggle_dir;
}

sub compute_final_value {
my $self = shift;
return $self->from_to->[$self->is_reversed_dir? 0: 1];
Expand All @@ -192,9 +146,18 @@ around BUILDARGS => sub {
($args{proxy_args} || ()),
];

$args{proxy_class} = ProxyFactory->find_proxy(@{ $args{proxy_args} });
$args{proxy_class} = ProxyFactory->find_proxy
(@{ $args{proxy_args} });

$args{timeline_args} = [ $args{timeline_args} || () ];
for my $att (qw(repeat bounce forever)) {
if (exists $args{$att}) {
my $val = delete $args{$att};
push @{$args{timeline_args}}, $att, $val;
}
}

return $class->$orig(%args);
return $class->$orig(%args);
};

1;
Expand Down
3 changes: 1 addition & 2 deletions lib/GameFrame/Animation/Composite.pm
Expand Up @@ -33,8 +33,7 @@ sub is_animation_started {

sub wait_for_animation_complete {
my $self = shift;
$_->wait_for_animation_complete
for grep { $_->is_animation_started } $self->animations;
$_->wait_for_animation_complete for $self->animations;
}

sub _for_children {
Expand Down
2 changes: 2 additions & 0 deletions lib/GameFrame/Animation/Curve.pm
@@ -1,5 +1,7 @@
package GameFrame::Animation::Curve;

# TODO do these have to take $from?

use Moose;
use Math::Trig;

Expand Down
27 changes: 19 additions & 8 deletions lib/GameFrame/Animation/Timer.pm
Expand Up @@ -96,7 +96,7 @@ sub _build_timer {
);
}

# if cycle sleep is given, default it in case it is too small
# if cycle sleep is given, default it, in case it is too small
around BUILDARGS => sub {
my ($orig, $class, %args) = @_;
my $cycle_sleep = $args{cycle_sleep};
Expand Down Expand Up @@ -136,19 +136,21 @@ sub _on_timer_tick {
my $elapsed = $self->now - $self->cycle_start_time;

if ($self->is_cycle_complete($elapsed)) {
$self->stop;
$self->_stop;
$self->cycle_complete;
$self->_on_final_timer_tick;
return;
}

$self->timer_tick($elapsed);
}

sub _on_final_timer_tick {}

# TODO should take optional start time to avoid drift
# in animation chaining, or when parallel animating
sub start {
my $self = shift;
#print "Starting\n";
$self->_on_first_timer_tick;
$self->start_timer;
}
Expand All @@ -158,18 +160,29 @@ sub restart {
my $last_cycle_start_time = $self->last_cycle_start_time;
die "Can't restart since we have no yet been started and stopped"
unless $last_cycle_start_time;
#print "Restarting\n";
$self->_on_first_timer_tick($last_cycle_start_time);
$self->start_timer;
}

# stop is split into _stop and stop: _stop is called when cycle
# is complete, stop is called when timer is to be stopped in middle
# of cycle
sub stop {
my $self = shift;
#print "Stopping\n";
$self->_stop;
}

sub _stop {
my $self = shift;
$self->_reset;
$self->stop_timer;
}

sub _reset {
my $self = shift;
# remember the last cycle start time in case we want to restart
# and avoid timer drift
$self->last_cycle_start_time($self->cycle_start_time + $self->total_sleep_computed);
$self->stop_timer;
$self->$_(0) for qw(
cycle_start_time
total_sleep_computed
Expand All @@ -185,7 +198,6 @@ sub pause {
my $actual_sleep = $now - $self->cycle_start_time - $self->total_cycle_pause;
$self->sleep_after_resume( $self->total_sleep_computed - $actual_sleep );
$self->pause_start_time($now);
#print "P> now=${\( sprintf('%.3f',EV::time() - $self->cycle_start_time) )}, sleep_after_resume=${\( sprintf('%.3f', $self->sleep_after_resume) )}, actual_sleep=${\( sprintf('%.3f', $actual_sleep) )} \n";
$self->stop_timer;
}

Expand All @@ -195,7 +207,6 @@ sub resume {
my $pause_time = $self->now - $self->pause_start_time;
$self->total_cycle_pause( $self->total_cycle_pause + $pause_time );
$self->pause_start_time(undef);
#print "R> now=${\( sprintf('%.3f',EV::time() - $self->cycle_start_time) )}\n";
$self->start_timer;
}

Expand Down
9 changes: 8 additions & 1 deletion lib/GameFrame/Role/Active.pm
Expand Up @@ -2,6 +2,7 @@ package GameFrame::Role::Active;

use Moose::Role;
use Coro;
use Scalar::Util qw(weaken);
#use GameFrame::Time qw(cleanup_thread);

# active objects have a coro and must implement start()
Expand All @@ -14,7 +15,13 @@ has coro => (
isa => 'Coro',
required => 1,
weak_ref => 1, # coro scheduler has a ref to the coro, if it lives
default => sub { my $self = shift; return async { $self->start } },
default => sub {
my $self = shift;
return async {
$self->start;
return; # ?!?!
};
},
);

# should only be called from a different thread than the one deactivated
Expand Down

0 comments on commit bef140e

Please sign in to comment.