UIView Delta Time - objective-c

I'm animating a UIView by updating its anchorpoint 60 times a second using an NSTimer.
The location of the UIView changes depending on its angle, so it always appears to be down relative to the device...
However, the NSTimer doesn't fire precisely 60 times a second. It's always a little off, causing jerky animation. I've searched this a lot, I know a bit about delta time, but I don't know how to apply it to my situation.
Here's the movement code I'm using:
float rotation = 0;
if (leftSideIsBeingHeldDown) {
rotation += (0.05f/rotationFactor);
} else if (rightSideIsBeingHeldDown) {
rotation -= (0.05f/rotationFactor);
}
movementX += -sinf(rotation);
movementY += -cosf(rotation);
float finalX = 0.0001 * movementX;
float finalY = 0.0001 * movementY;
mapView.layer.anchorPoint = CGPointMake(finalX, finalY);
mapView.transform = CGAffineTransformMakeRotation(rotation);
Does anyone know how to apply delta time to this?

You might want to look into the CADisplayLink class which provides you a timer that is tied to the display refresh rate. It should be a better solution than an NSTimer in this case.
Additionally, you need to remember the time of each "tick" and calculate the rotation or movement that should have been done since the last tick. For example (pseudo-code):
- (void)displayLinkTick:(id)sender
{
NSTimeInterval timespan;
NSDate *now;
now = [NSDate date];
if (myPreviousTick) {
timespan = [now timeintervalSinceDate:myPreviousTick];
} else {
// The very first tick.
timespan = 0;
}
// Calculate the angle according to the timespan. You need a
// value that specifies how many degrees/radians you want to
// revolve per second and simply multiply that with the timespan.
angle += myRadiansPerSecond * timespan;
// You'd do the same with the position. I guess this involves
// minor vector math which I don't remember right now and am
// too lazy to look up. You need to have a distance per second
// which you multiply with the timespan. Together with the
// direction vector you can calculate the new position.
// At the end, remember when this tick ran.
[myPreviousTick release];
myPreviousTick = [now retain];
}

You want to record the time you last rotated, and the difference in time between then and now, and use that to work out a factor, which you can use to adjust the rotation and x/y values.
for example:
NSDate now = [NSDate now];
timeDiff = now - lastRotateTime;
factor = timeDiff / expectedTimeDiff;
x = x + xIncrement * factor;
y = y + yIncrement * factor;
angle = angle + angleIncrement * factor;
There are many better examples on game dev forums, which explain it in more detail.

Related

Velocity Verlet Algorithm: Cannot seem to determine correct velocity for stable Orbit?

Like many that post about this topic, I too am busy trying to write myself an accurate simulator for the movement of objects in a 2D gravitation field.
I decided early on that I would settle on Velocity Verlet Integration, as I want my objects to maintain stable orbits and conserve energy even if the timestep is rather large. So, what might the problem be?
Well, so far, everything seems to behave correctly, except for one component. When I try to calculate the correct velocity for a stable orbit at a certain distance, the resulting velocity sends them into odd elliptical orbits that quickly increase in magnitude each time.
So, to begin, here are the following methods that determine an objects next position, velocity, and acceleration in scene: (Objective C)
Acceleration:
-(CGVector)determineAccelerationFor:(SKObject *)object
{ // Ok, let's find Acceleration!
CGVector forceVector = (CGVector){0,0}; // Blank vector that we will add forces to
for (SKObject *i in self.sceneObjects)
{
if (![i isEqual:object]) // Just make sure we're not counting ourselves here
{
CGPoint distance = [self getDistanceBetween:i.position And:object.position];
float hypotenuse = sqrtf(powf(distance.x, 2)+ powf(distance.y, 2));
float force = ((self.gravity * object.mass * i.mass)/powf(hypotenuse, 3));
float xMagnitude = (force * distance.x);
float yMagnitude = (force * distance.y);
forceVector.dx += xMagnitude;
forceVector.dy += yMagnitude;
}
}
CGVector acceleration = (CGVector){forceVector.dx/object.mass, forceVector.dy/object.mass};
return acceleration;
}
Cool, so basically, I just take an object, add all the other forces that each other object imposes on it together then divide the X & Y factor by the mass of the current object to get the acceleration!
Next up is Velocity. Here I use the following equation:
The method for it is pretty straightforward too:
-(CGVector)determineVelocityWithCurrentVelocity:(CGVector)v OldAcceleration:(CGVector)ao NewAcceleration:(CGVector)a
{
float xVelocity = (v.dx + ((ao.dx + a.dx)/2) * self.timeStep);
float yVelocity = (v.dy + ((ao.dy + a.dy)/2) * self.timeStep);
CGVector velocity = (CGVector){xVelocity,yVelocity};
return velocity;
}
And finally, position! The equation for this is:
And it is determined with the following method!
-(CGPoint)determinePositionWithCurrentPosition:(CGPoint)x CurrentVelocity:(CGVector)v OldAcceleration:(CGVector)ao
{
float xPosition = (x.x + v.dx * self.timeStep + ((ao.dx * powf(self.timeStep, 2))/2));
float yPosition = (x.y + v.dy * self.timeStep + ((ao.dy * powf(self.timeStep, 2))/2));
CGPoint position = (CGPoint){xPosition,yPosition};
return position;
}
This is all called from the below method!!
-(void)refreshPhysics:(SKObject *)object
{
CGPoint position = [self determinePositionWithCurrentPosition:object.position CurrentVelocity:object.velocity OldAcceleration:object.acceleration]; // Determine new Position
SKAction *moveTo = [SKAction moveTo:position duration:0.0];
[object runAction:moveTo]; // Move to new position
CGVector acceleration = [self determineAccelerationFor:object]; // Determine acceleration off new position
CGVector velocity = [self determineVelocityWithCurrentVelocity:object.velocity OldAcceleration:object.acceleration NewAcceleration:acceleration];
NSLog(#"%# Old Velocity: %f, %f",object.name,object.velocity.dx,object.velocity.dy);
NSLog(#"%# New Velocity: %f, %f\n\n",object.name,velocity.dx,velocity.dy);
[object setAcceleration:acceleration];
[object setVelocity:velocity];
}
Okay, so those methods above dictate how objects are moved in scene. Now onto the initial issue, the ever present problem of achieving a stable orbit!
In order to determine what velocity an object should have to maintain an orbit, I use the following equation:
And I implement that as follows:
-(void)setObject:(SKObject *)object ToOrbit:(SKObject *)parent
{
float defaultSeparation = 200;
// Move Object to Position at right of parent
CGPoint defaultOrbitPosition = (CGPoint){parent.position.x + (parent.size.width/2)+ defaultSeparation,parent.position.y};
[object setPosition:defaultOrbitPosition];
// Determine Orbital Velocity
float velocity = sqrtf((self.gravity * parent.mass)/(parent.size.width/2+defaultSeparation));
CGVector vector = (CGVector){0,velocity};
[object setVelocity:vector];
}
And for some reason, despite this, I get abysmal results. Here is some of the output:
Information:
Gravity(constant) = 1000 (For test purposes)
Mass(Parent) = 5000 units
Mass(Satellite) = 1 units
Separation = 224 pixels
It determines that in order for the Satellite to Orbit the Parent, a velocity of:
149.403580 pixels/timeStep
is required. And that checks out on my calculator.
So this has left me a little confused as to what could be going wrong. I log all the output concerning new velocities and positions, and it does use the velocity I set it to, but that just doesn't seem to make a difference. If anyone could possible help spot what's going wrong here I would be immensely grateful.
If anyone believes I have left something out, tell me and I will edit this right away. Thanks!

How many times a second should CADisplayLink's displayLink be called?

I have a CADisplayLink running in line with Chipmunk Physics, and I'm getting very slow performance. I put an NSLog in the method that's called on the CADisplayLink update, and it's being called an average of 22 times per second. I was under the impression that that should be nearer 60. I have the frameInterval set to 1, so should it be 60fps, in a perfect world? The delta times are averaging around 0.0167 seconds (and 1 / 60 IS 0.0167, which is confusing me even further).
I just have four walls around the bounds of my screen and just eight circle-shaped bodies on-screen, updating to UIButton instances on each call, so I don't think I'm doing anything that should tax it to this extent on both my 4S and iPad3. I'm applying a random force to each button once every 2.5 seconds in a separate method. Running in the simulator is butter-smooth, so it's a device-only issue. Can anyone help me spot what's causing the slowdown here, and what I can do about it?
Here's the relevant code, first that which sets up the link:
[NSTimer scheduledTimerWithTimeInterval: 2.5f target: self selector: #selector(updateForces) userInfo: nil repeats: YES];
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: #selector(update)];
_displayLink.frameInterval = 1;
[_displayLink addToRunLoop: [NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes];
Here's the method that should be called (I think!) 60 times per second, but is called only 22 or so:
if (!gameIsPaused) {
cpFloat dt = _displayLink.duration * _displayLink.frameInterval;
cpSpaceStep([[AGChipmunkSpace sharedInstance] space], dt);
for (LCBall *i in balls) {
cpVect pos1 = cpBodyGetPos(i.body);
CGAffineTransform trans1 = CGAffineTransformMakeTranslation(pos1.x, pos1.y);
CGAffineTransform rot1 = CGAffineTransformMakeRotation(cpBodyGetAngle(i.body));
i.button.transform = CGAffineTransformConcat(rot1, trans1);
}
}
And finally, here's the method that's called every 2.5 seconds, to apply the random forces (updateForces):
if (!gameIsPaused) {
for (LCBall *i in balls) {
int randomAngle = arc4random() % 360;
CGPoint point1 = [self getVectorFromAngle: randomAngle AndMagnitude: (arc4random() % 40) + ((arc4random() % 20) + 15)];
i.body -> f = cpv(point1.x, point1.y);
}
}
(Also, here's my method to get a vector from an angle, which I doubt is causing the issue):
angle = (angle / 180.0) * M_PI;
float x = magnitude * cos(angle);
float y = magnitude * sin(angle);
CGPoint point = CGPointMake(x, y);
return point;
Turns out I had a method on a different UIViewController in my storyboard that was firing every 0.1 seconds that hadn't turned off, and combined with the physics processing, was bogging things down.

UITableView get scrolling speed

i have table cell with images, i want them to start loading, as soon as the scrolling speed drops about a threshold
however, how can i determine the current scrolling speed of a UITableView?
i found a
tableView.isDecelerating
however i want to load images, also when scrolling is slow
This is working for me (in the cellForRowAtIndexPath callback). I load images if the velocity falls below 1000 pixels/second:
static double prevCallTime = 0;
static double prevCallOffset = 0;
//Simple velocity calculation
double curCallTime = CACurrentMediaTime();
double timeDelta = curCallTime - prevCallTime;
double curCallOffset = self.tableView.contentOffset.y;
double offsetDelta = curCallOffset - prevCallOffset;
double velocity = fabs(offsetDelta / timeDelta);
NSLog(#"Velocity: %f", velocity);
prevCallTime = curCallTime;
prevCallOffset = curCallOffset;
If you are targeting iOS >=5 you can get the underlying UIPanGestureRecognizer and then ask it's velocity
CGPoint velocity = [tableView.panGestureRecognizer velocityInView:tableView];

Animate UILabel with numbers

I am still learning about UIAnimations, just got into it and I have stumbled upon a problem that I am not sure how to solve. I've seen games where you get a new high score and it adds the new high score to the old high score and they make the numbers animate up or down. It looks very cool and visually appeasing.
Can anyone explain to me how this is done? I apologize if this question is easily solved, like I said I am still trying to learn/perfect animations.
Thanks in advance
I took the code from the post sergio suggested you look at, but took note of Anshu's mention that you wanted a moving up and down animation rather then a fade-in/fade-out animation, so I changed the code to fit what you wanted. Here you go:
// Add transition (must be called after myLabel has been displayed)
CATransition *animation = [CATransition animation];
animation.duration = 1.0; //You can change this to any other duration
animation.type = kCATransitionMoveIn; //I would assume this is what you want because you want to "animate up or down"
animation.subtype = kCATransitionFromTop; //You can change this to kCATransitionFromBottom, kCATransitionFromLeft, or kCATransitionFromRight
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[myLabel.layer addAnimation:animation forKey:#"changeTextTransition"];
// Change the text
myLabel.text = newText;
Hope this helps!
People can correct me if I'm wrong here, but I'm pretty sure you have to code this animation manually. You might be able to find an open source version somewhere online if you look hard enough.
It might be possible to take an image of a UILabel and use sizeWithFont: to determine how wide each character is, then cut the image up into sections based on each digit. Alternatively you could just have multiple UILabels for each digit.
Once you have an array of digit images, you'd have to calculate which digits are going to change during the transition and whether they're going to increase or decrease, then transition to the next digit by pushing it in from the top/bottom (I think there's a built in transition to do this, look around in the Core Animation docs).
You would probably want to determine by how much they increase/decrease and use that to figure out how long the animation will take. That way, if you're going from 5 to 900, the last digit would have to be animating very quickly, the second to last would animate 1/10 as quickly, the third would be 1/100, etc.
This does on ok job, using the reveal function. It would be nice to have some vertical motion, but it's either going to be kCATransitionFromBottom or kCATransitionFromTop - and really we'd need kCATransitionFromBottom | kCATransitionToTop, but that's not a thing. Here's the code:
-(void)countUpLabel:(UILabel *)label fromValue:(int)fromValue toValue:(int)toValue withDelay:(float)delay{
int distance = (int)toValue - (int)fromValue;
int absDistance = abs(distance);
float baseDuration = 1.0f;
float totalDuration = absDistance / 100.0f * baseDuration;
float incrementDuration = totalDuration / (float)absDistance;
int direction = (fromValue < toValue) ? 1 : -1;
//NSString * subtype = (direction == 1) ? kCATransitionFromBottom : kCATransitionFromTop;
for (int n = 0; n < absDistance; n++){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
CATransition * fade = [CATransition animation];
fade.removedOnCompletion = true;
fade.duration = incrementDuration;
fade.type = kCATransitionReveal;
fade.subtype = kCATransitionMoveIn;
[label.layer addAnimation:fade forKey:#"changeTextTransition"];
int value = fromValue + (n+1) * direction;
label.text = [NSString stringWithFormat:#"%i", value];
});
delay += incrementDuration;
}
}

Box2d Physics Simulation Stutter

I have some code that causes the box2d physics simulation to stutter forever after this code is called once. I ran a performance test on it and OSSpinLockLock has a lot of time spent on it. b2Island::Solve and some other box2d functions are causing this to take up so much time. I'm not really sure what is going on here. The effects of this seem to be worse on maps with more overlapping static bodies then maps with less overlapping static bodies. The code that causes the stutter is the following:
for (b2Body*b = currentWorld->GetBodyList(); b!=nil; b = b->GetNext()) {
float distance = 0;
float strength = 0;
float force = 0;
float angle = 0;
CGPoint curPos = [Helper toPixels:b->GetWorldCenter()];
distance = ccpDistance(selfPos, curPos);
if (distance>maxDistance) {
distance = maxDistance - 0.01;
}
//strength = (maxDistance - distance) / maxDistance;
strength = (distance - maxDistance)/maxDistance;
force = strength * maxForce;
angle = atan2f(selfPos.y - curPos.y, selfPos.x - curPos.x);
//b2Vec2 forceVec = [Helper toMeters:force];
//CCLOG(#"strength = %f force = %f angle = %f distance = %f",strength,angle,force, distance);
b->ApplyLinearImpulse(b2Vec2(cosf(angle) * force, sinf(angle) * force), b->GetPosition());
BodyNode * bn = (BodyNode*)b->GetUserData();
if ([bn isKindOfClass:[Soldier class]]) {
((Soldier*)bn).health-=abs(force*(damage * kRocketDamageFactor)); //used to be 50
}
if ([bn isKindOfClass:[BaseAI class]]) {
((BaseAI*)bn).health-=abs(force*(damage * kRocketDamageFactor));
}
if ([bn isKindOfClass:[BaseLevelObject class]]) {
if (((BaseLevelObject*)bn).takesDamageFromBullets == YES) {
((BaseLevelObject*)bn).health-=abs(force*(damage * kRocketDamageFactor));
}
}
I suspect that the ApplyLinearImpulse part is what is causing the stutter because it is the only thing related to physics. I just commented out this whole loop and the stutter never came. But as soon as I exploded some other explosives with the same code, the stutter came. Do overlapping static b2body objects slow the physics simulation down?
The issue was caused by the code giving all the bodies that were farther away than the max distance a small tap towards the explosion. I'm not really sure why this would cause the physics simulation to stutter, but adding a continue statement in place of distance = maxDistance - 0.01; solved the problem.