UIKit Dynamics: Calculating pushDirection Needed for UIPushBehavior - ios7

I'd like to use UIPushBehavior to give a physically realistic animation of a view across the screen. But to do this, I need to be be able to calculated the magnitude of the push needed in the CGVector in the pushDirection property of UIPushBehavior.
Basically, I know the size of the view, the amount of time, and the distance I'd like it to travel. Is there a way to use the UIKit Newton definition and a UIDynamicItemBehavior with resistance to calculate exactly the magnitude need to move a view a specific distance in a specific amount of time?

If you're using a pan gesture, you can do this:
if ( gestureRecognizer.state == UIGestureRecognizerStateEnded )
{
CGPoint velocity = [gestureRecognizer velocityInView:gestureRecognizer.view.superview];
CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
myPushBehavior = [[UIPushBehavior alloc] initWithItems:#[gestureRecognizer.view]
mode:UIPushBehaviorModeInstantaneous];
myPushBehavior.pushDirection = CGVectorMake((velocity.x / 10) , (velocity.y / 10));
myPushBehavior.magnitude = magnitude;
}
Courtesy of Tony Dahbura's tutorial.

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!

Using CGAffineTransformMakeRotation to rotate a point

If I want to rotate one CGPoint about another, I'm currently doing this (which works fine):
CGFloat rx = cos(DEGREES_TO_RADIANS(-angle)) * (positionToRotate.x-rotationPoint.x) - sin(DEGREES_TO_RADIANS(-angle)) * (positionToRotate.y-rotationPoint.y) + rotationPoint.x;
CGFloat ry = sin(DEGREES_TO_RADIANS(-angle)) * (positionToRotate.x-rotationPoint.x) + cos(DEGREES_TO_RADIANS(-angle)) * (positionToRotate.y-rotationPoint.y) + rotationPoint.y;
It strikes me that I should be able to do this with a CGAffineTransform, but I'm a bit stuck as to how it would work:
CGAffineTransform affine CGAffineTransformMakeRotation(M_PI/4);
CGPointApplyAffineTransform(positionToRotate, affine);
That does nothing as I'm (hopefully) missing something obvious :)
So how do you rotate a CGPoint about another without doing the matrix math myself?
Cheers,
Ian
CGPointApplyAffineTransform returns the transformed point. It doesn't mutate the CGPoint you pass in.
CGPoint transformedPoint = CGPointApplyAffineTransform(positionToRotate, affine);

iOS 7 Sprite Kit frame rate

I have written the -update method for an SKScene in two different forms and one has a much better and constant frame rate than the other.
Can someone explain the difference between the two implementations and reason(s) why they create two different frame rates?
Method 1 (frame rate drops to about 20-30 fps when moving sprite)
- (void)update:(NSTimeInterval)currentTime
{
[self updateBackground];
player.position = CGPointMake(slider.sliderValue * self.view.frame.size.width/200 + self.view.frame.size.width/2, player.position.y);
}
Method 2 (frame rate remains constant 60 fps when moving sprite)
- (void)update:(NSTimeInterval)currentTime
{
[self updateBackground];
delta = currentTime - previousTime;
previousTime = currentTime;
//NSLog(#"%f",delta);
player.position = CGPointMake(delta * adjustmentFactor * slider.sliderValue * self.view.frame.size.width/200 + self.view.frame.size.width/2, player.position.y);
}

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.

UIView Delta Time

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.