SpriteKit Physics: Equal and opposite reaction - ios

I'm trying to animate a helicopter-thing in my game using a virtual joystick. The below code works well if your flying it actively, but now I want to animate the helicopter to a hover rotating it to the negative angle and apply a negative/positive vector to it to make it rapidly stop and "hover." And possibly bounce back and forth until its hovering. Any suggestions?
//Just move the hell with no physics
//[heli setPosition:CGPointMake(heli.position.x+self.joystick.x*2, heli.position.y+self.joystick.y*2)];
//Apply physics to the heli
float multiplier = 2.5;
[heli.physicsBody applyForce:CGVectorMake(_joystick.x*multiplier, _joystick.y*multiplier)];
//joystick x position is -99 to 99
SKAction *rotation = [SKAction rotateToAngle:-1*(_joystick.x/2.0) duration:0];
[heli runAction: rotation];
//if i let go of the joystick it returns to zero - bounce to a hover
if (_joystick.x == 0) { //not working
//check the current velocity?
//[heli.physicsBody velocity];
[heli.physicsBody applyForce:CGVectorMake(-1*(_joystick.x*multiplier*2), -1*(_joystick.y*multiplier*2))];
}

Related

SpriteKit SKPhysicsBody applyTorque causes sprite position to change

I have an interesting problem. I create an SKSpriteNode with associated physics body.
SKTexture *shipTexture = [SKTexture textureWithImageNamed:#"Spaceship"];
self.heroSpriteNode = [HeroSpriteNode spriteNodeWithTexture:shipTexture size:CGSizeMake(40, 35.2)];
self.heroSpriteNode.physicsBody = [SKPhysicsBody bodyWithTexture:shipTexture size:CGSizeMake(40, 35.2)];
self.heroSpriteNode.physicsBody.dynamic = YES;
self.heroSpriteNode.physicsBody.mass = 1000.0;
self.heroSpriteNode.physicsBody.affectedByGravity = NO;
self.heroSpriteNode.physicsBody.allowsRotation = YES;
self.heroSpriteNode.physicsBody.angularDamping = 0.5;
self.heroSpriteNode.anchorPoint = CGPointMake(0.5,0.5);
self.heroSpriteNode.physicsBody.categoryBitMask = heroCategory;
self.heroSpriteNode.physicsBody.contactTestBitMask = groundCategory;
self.heroSpriteNode.physicsBody.collisionBitMask = groundCategory | edgeCategory;
The Scene itself has no gravity.
In the update routine I call applyTorque: to the physics body.
-(void) updateWithDeltaTime:(NSTimeInterval)seconds
{
CGFloat rotateTorque = self.rotateRate;
switch (self.rotateForceApplied)
{
case leftForceApplied:
break;
case rightForceApplied:
rotateTorque = -1.0 * rotateTorque;
break;
case zeroForceApplied: // separate from default in case behavior should change
rotateTorque = 0.0;
break;
default:
rotateTorque = 0.0;
break;
}
NSLog(#"before physics position: x %f, y %f", self.heroSpriteNode.position.x, self.heroSpriteNode.position.y);
[self.heroSpriteNode.physicsBody applyTorque:rotateTorque];
NSLog(#"after physics position: x %f, y %f", self.heroSpriteNode.position.x, self.heroSpriteNode.position.y);
}
The sprite rotates and appears to rotate in place. What it is actually doing is rotating around a center point (so no net lateral movement). This is seen by logging the sprite position before and after applying the torque. No other actions are being applied to the physics body. The movement in position is about 9 points in each direction at most.
Because the camera is pinned by constraints to the "hero" sprite, and the world moves with the camera (the centerOnNode: sample code), this causes the whole world to move in a circular pattern as the sprite spins. The world itself does not spin but moves at the same rate in a circular pattern with the spinning.
With the sprite anchorPoint being 0.5, 0.5 I would think it should rotate around a center point which should not change the position of the sprite.
What would I being doing wrong with this?
If it matters this is on iOS9, Xcode7, running on device not in the simulator. (The iOS9 SpriteKit documentation is publicly available on Apple's website, as is a public beta of iOS9 itself so this should not be breaking any NDA, and I don't think anything here is iOS9 specific anyway)
Sprite Kit is a physics engine that attempts to mimic characteristics of objects in the real world, so you should expect an object with a physics body in the simulation to behave as they would in nature. In this case, you are rotating a non-uniform physics body that may rotate non-uniformly and, possibly, move over time (unless the body is rotating about its center of mass). If you change the physics body to a circle, the sprite should rotate uniformly and should remain at a fixed location.

sprite kit - Sprite randomly drifting upwards after rotation

For the sake of simplicity, the scene has a circle sprite and a square sprite. The square sprite is the child of an SKNode that follows around the circle sprite so that rotation of the square always happens around the circle. However, when the node is rotated, the square randomly drifts upwards. This behavior stops once the rotation makes its way all the way around. Here is the code:
_square = [SKSpriteNode spriteNodeWithImageNamed:#"square"];
_circle = [SKSpriteNode spriteNodeWithImageNamed:#"circle"];
_circle.position = CGPointMake(-200, 300);
[self addChild:_circle];
_rotateNode = [SKNode node];
_rotateNode.position = CGPointMake(300, 300);
[self addChild:_rotateNode];
[_rotateNode addChild:_square];
SKAction *moveBall = [SKAction moveTo:CGPointMake(1200, 300) duration:12];
[_circle runAction:moveBall];
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
SKAction *rotate = [SKAction rotateByAngle:-M_PI_2 duration:0.2];
[_rotateNode runAction:rotate];
}
-(void)update:(CFTimeInterval)currentTime {
_rotateNode.position = _circle.position;
double xOffset = (300 -_circle.position.x);
double yOffset = (300 - _circle.position.y);
_square.position = CGPointMake(xOffset, yOffset);
}
Anyone know why this is happening?
If I understand the question, you are asking why the square moves in unexpected ways? The circle is moving, but on every frame you are setting the rotateNode's position to be the same as the circle, and they both have the same parent: self, so we can ignore circle for any debugging because it just executes its action every frame to get a new position.
The oddness, most likely, comes in because you are manually repositioning rotateNode, which would then move all of its children, including square. But in every frame you are also repositioning square manually. So rotateNode rotates, changes the position of square because it rotated, and then you reposition square to have a fixed offset. Further complicated depending on whether your goal is to position the square in world space or in parent space. But it's not clear what your goal is there.

Ios SKSpriteNode prevent negative x velocity

I'm making a game with SpriteKit that involves a hero bouncing on a surface in the positive x and y direction. The hero has this size:
CGSize heroSize = CGSizeMake(hero.size.width-140, hero.size.height-35);
hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:heroSize];
hero.physicsBody.usesPreciseCollisionDetection = YES;
and uses applyImpulse to bounce in the positive x/y direction:
[self.physicsBody applyImpulse:CGVectorMake(xvelocity, yvelocity)];
SKAction *rotation = [SKAction rotateByAngle: -2.0/M_PI duration:.5];
[self runAction: rotation];
The ground has a friction level of 0.4 and restitution of .5. Because the hero rotates when bouncing, he will sometimes bounce backwards and have a negative X velocity. I was wondering if there is any way to prevent an SKSpriteNode from moving in the -X direction. Would a solution be to create a better size for the physics body of my SKSpriteNode? It seems like this could happen with any shape, but would be less likely with more circular objects. Any input is greatly appreciated.
Thanks
You can monitor your hero's velocity and limit negative x movement by using this:
if(hero.physicsBody.velocity.dx < 0) {
hero.physicsBody.velocity = CGVectorMake(0, hero.physicsBody.velocity.dy);
}

SKAction to Change AnchorPoint?

I am creating a relatively complicated animation sequence. In it, a certain SKSpriteNode (shark) does two rotations. At the beginning of the animation, it rotates around a certain anchor point ap1, then later rotates around a different anchor point ap2. How should I change anchor points midway through an animation sequence?
Some initial thoughts:
I could change the anchor point outside of SKActions, in the update: loop perhaps.
I could use multiple SKSpriteNodes for the same shark sprite (with their respective anchor points), switching (hiding/showing) the sprite nodes when I need to change the anchor point.
Since changing a sprite's anchor point affects where it's rendered, you will likely need to make some sort of adjustment to prevent the sprite from appearing to suddenly move to a new location. Here's one way to do that:
Create action that changes the anchor point
SKAction *changeAnchorPoint = [SKAction runBlock:^{
[self updatePosition:sprite withAnchorPoint:CGPointMake(0, 0)];
}];
SKAction *rotate = [SKAction rotateByAngle:2*M_PI duration: 2];
Run action to rotate, change the anchor point, and rotate
[sprite runAction:[SKAction sequence:#[rotate,changeAnchorPoint,rotate]] completion:^{
// Restore the anchor point
[self updatePosition:sprite withAnchorPoint:CGPointMake(0.5, 0.5)];
}];
This method adjusts a sprite's position to compensate for the anchor point change
- (void) updatePosition:(SKSpriteNode *)node withAnchorPoint:(CGPoint)anchorPoint
{
CGFloat dx = (anchorPoint.x - node.anchorPoint.x) * node.size.width;
CGFloat dy = (anchorPoint.y - node.anchorPoint.y) * node.size.height;
node.position = CGPointMake(node.position.x+dx, node.position.y+dy);
node.anchorPoint = anchorPoint;
}
As Julio Montoya said, the easiest way to do this is to just "translate" code into an SKAction with the method [SKAction runBlock:myBlock].

Scale CCSprite from bottom right hand corner

So here's the gist of what I'm trying to do here.
I have an array of foreground sprites that I scroll forever as the player moves along. What I would like to do, is when the player starts passing a certain point on the Y axis, scale down those foreground sprites while still moving them.
I'd like to be able to scale the sprites from their bottom left hand corners when the player is going up, and I've got this working without any problems.
The real problem is that I'd also like to scale the sprites from their bottom right hand corners when the player is coming down. Now I thought that I could do this by setting each sprite's anchor point to 1,0 before scaling it, but that doesn't seem to work. The sprites still scale from their bottom left hand corners.
What am I missing here?
// do logic to identify the scale factor we want
for (CCSprite *sprite in foreground_sprites)
{
CGPoint old_anchor = sprite.anchorPoint;
[sprite setAnchorPoint:ccp(1,0)];
[sprite setScale:scale_factor];
[sprite setAnchorPoint:old_anchor];
}
Have you tried messing with this property?
ignoreAnchorPointForPosition(false);
I'm using cocos2d-x, there should be something similar to that
If I understand correctly, you want to scale from the bottom left while the player's Y position increases but scale using the bottom right while they are descending?
Well you can't just change the anchor point alone. The anchor point and position go hand in hand to position the sprite on the screen. So if you positioned the sprite on to the screen using an anchor point of (0,0) then if you want to switch it's anchor point to (1,0) while keeping it in the same location on the screen, you'll need to update the position.
CCSprite* sprite = ...;
sprite.anchorPoint = CGPointZero;
sprite.position = CGPointZero;
...
sprite.anchorPoint = CGPointMake(1.0f, 0.0f);
sprite.position = CGPointMake(sprite.position.x + (sprite.contentSize.width * sprite.scaleX * sprite.anchorPoint.x),
sprite.position.y + (sprite.contentSize.height * sprite.scaleY * sprite.anchorPoint.y));
Hopefully I understood your problem correctly and was able to help.

Resources