Skip to content

Commit

Permalink
PMTweenPhysicsUnit changed to use fixed timestep
Browse files Browse the repository at this point in the history
- PMTweenPhysicsSystem now uses a fixed timestep, which though rudimentary, helps smooth out tween value jittering
- Fixed bug with physics timer not properly removed
  • Loading branch information
poetmountain committed Mar 2, 2016
1 parent 6d90979 commit 8262993
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 24 deletions.
42 changes: 31 additions & 11 deletions Classes/PMTweenPhysicsSystem.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#import "PMTweenPhysicsSystem.h"
#import "PMTween.h"

double static const PMTWEEN_TIMESTEP = 0.0001;

@interface PMTweenPhysicsSystem ()

// The initial velocity value set. Used when resetting the system.
Expand Down Expand Up @@ -48,24 +50,42 @@ - (instancetype)initWithVelocity:(double)velocity friction:(double)friction {
#pragma mark - PMTweenPhysicsSolving protocol methods

- (double)solveForPosition:(double)position currentTime:(NSTimeInterval)elapsedTime {
//NSLog(@"======================================================");
//NSLog(@"====solveForPosition==================================================");
//NSLog(@"last %f -- elapsed %f", _lastTimestamp, elapsedTime);

double new_position = position;
double previous_position = new_position;

NSTimeInterval time_delta = elapsedTime - _lastTimestamp;
time_delta = MAX(0.0, time_delta);
if (time_delta > 0.2) { time_delta = 0.2; }
//NSLog(@"time ∆ %f", time_delta);

if (!_paused) {
if (_lastTimestamp > 0) {
NSTimeInterval time_delta = elapsedTime - _lastTimestamp;
time_delta = MAX(0.0, time_delta);
//NSLog(@"time ∆ %f", time_delta);
if (!_paused && time_delta > 0.0) {
if (_lastTimestamp > 0.0) {
double accumulator = 0.0;

// use pow here to compensate for floating point errors over time
double friction_multiplier = pow(1-_friction, time_delta);
accumulator += time_delta;

_velocity *= friction_multiplier;
// in this loop we apply a fixed timestep to the position solver as long as there are steps left in the time delta
while (accumulator >= PMTWEEN_TIMESTEP) {
previous_position = new_position;

// use pow here to compensate for floating point errors over time
double friction_multiplier = pow(1-_friction, PMTWEEN_TIMESTEP);

_velocity *= friction_multiplier;

// add just the portion of current velocity that occurred during this time delta
new_position += (_velocity * PMTWEEN_TIMESTEP);

// decrement the accumulator by the fixed timestep amount
accumulator -= PMTWEEN_TIMESTEP;
}

// add just the portion of current velocity that occurred during this time delta
new_position += (_velocity * time_delta);
// interpolate the remaining time delta to get the final state of position value
double blending = accumulator / PMTWEEN_TIMESTEP;
new_position = new_position * blending + (previous_position * (1.0 - blending));

}
_lastTimestamp = elapsedTime;
Expand Down
31 changes: 20 additions & 11 deletions Classes/PMTweenPhysicsUnit.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#import "PMTweenCATempo.h"
#import "PMTweenSupport.h"

double static const PMTWEEN_DECAY_LIMIT = 1.0;
double static const PMTWEEN_DECAY_LIMIT = 0.96;

@interface PMTweenPhysicsUnit ()

Expand Down Expand Up @@ -76,6 +76,7 @@ - (void)reverseTweenDirection;
- (void)resetTween;

- (void)setupPhysicsTimer;
- (void)removePhysicsTimer;

// Updates the physics system
- (void)updatePhysicsSystem;
Expand Down Expand Up @@ -242,26 +243,33 @@ - (void)setupTweenForProperty:(NSObject *)property startingValue:(double)startin

- (void)setupPhysicsTimer {

self.physicsTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0));
if (self.physicsTimer) {
[self removePhysicsTimer];
}

self.physicsTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0));
dispatch_source_set_timer(self.physicsTimer, DISPATCH_TIME_NOW, self.physicsTimerInterval * NSEC_PER_SEC, 0.001 * NSEC_PER_SEC);
__weak typeof(self) weak_self = self;
dispatch_source_set_event_handler(self.physicsTimer, ^{
__strong typeof(self) strong_self = weak_self;

[strong_self updatePhysicsSystem];
});
dispatch_source_set_cancel_handler(self.physicsTimer, ^{
__strong typeof(self) strong_self = weak_self;
strong_self.physicsTimer = nil;
});
}


- (void)removePhysicsTimer {
if (self.physicsTimer) {
dispatch_cancel(self.physicsTimer);
self.physicsTimer = nil;
}
}


- (void)updatePhysicsSystem {

// update physics system
NSTimeInterval current_time = CACurrentMediaTime();
self.currentValue = [self.physicsSystem solveForPosition:_currentValue currentTime:current_time];
// use the last timestamp recorded during updateWithTimeInterval for the physics solver
self.currentValue = [self.physicsSystem solveForPosition:self.currentValue currentTime:self.currentTime];

self.tweenProgress = (_initialVelocity - fabs(self.physicsSystem.velocity)) / _initialVelocity;

Expand Down Expand Up @@ -638,6 +646,8 @@ - (void)tempoBeatWithTimestamp:(NSTimeInterval)timestamp {

- (void)updateWithTimeInterval:(NSTimeInterval)currentTime {

_currentTime = currentTime;

if (_tweenState == PMTweenStateTweening) {

// call update block
Expand Down Expand Up @@ -673,7 +683,6 @@ - (void)updateWithTimeInterval:(NSTimeInterval)currentTime {

} else if (_tweenState == PMTweenStateDelayed) {

_currentTime = currentTime;

if (_startTime == 0) {
// a start time of 0 means we need to initialize the tween times
Expand Down Expand Up @@ -726,7 +735,7 @@ - (void)startTween {
- (void)stopTween {
if (_tweenState == PMTweenStateTweening || _tweenState == PMTweenStatePaused || _tweenState == PMTweenStateDelayed) {
self.tweenState = PMTweenStateStopped;
dispatch_cancel(self.physicsTimer);
[self removePhysicsTimer];
_startTime = 0;
_currentTime = 0;
_tweenProgress = 0;
Expand Down
3 changes: 2 additions & 1 deletion Examples/PMTweenExamples/Classes/BasicPhysicsTweenVC.m
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ - (void)setupUI {

- (void)setupEasing {

self.tween = [[PMTweenPhysicsUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x velocity:60 friction:0.25 options:PMTweenOptionNone];
self.tween = [[PMTweenPhysicsUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x velocity:100 friction:0.4 options:PMTweenOptionNone];

__weak typeof(self) weak_self = self;
self.tween.updateBlock = ^void(NSObject<PMTweening> *tween) {
//DLog(@"TWEEN 1 COMPLETE!");
Expand Down
2 changes: 1 addition & 1 deletion PMTween.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'PMTween'
s.version = '1.3.2'
s.version = '1.3.3'
s.license = { :type => 'MIT' }
s.summary = 'An elegant and flexible tweening library for iOS.'
s.ios.deployment_target = '7.0'
Expand Down

0 comments on commit 8262993

Please sign in to comment.