SpriteKit Node Rotational Issues - ios

I am trying to develop a basic game for iOS involving a rag doll-like entity. Basically (as you can tell below), I have a head, a body, and a right arm (all three are basic sprite nodes) connected via pin joints, simulating joins in the human body (roughly).
The head and the body work perfectly in that, when a force is applied, the body rotates around the head perfectly and eventually comes to a rest under the head, vertically (see picture).
The arm's base is pinned with a pin joint to the body and is supposed to rotate around its base (kind of like a shoulder) and it is set with an initial rotation of 45 degrees so it looks like an arm before the physics engine takes over.
My question is: why doesn't the arm come to rest in a vertical position (like the body) due to gravity? Shouldn't gravity cause the arm to rotate about its base until the tip of the arm rests directly below the top of the arm (shoulder)? Furthermore, when a force is applied to the body (shown in the example code below), the body rotates about the neck joint, exactly as it should, but the arm does not move from its current orientation (and this is not desirable).
If this is not the case, how would I achieve this effect?
Thank you for your time and I'd be happy to provide any additional information if desired
Picture of physics simulation at rest:
Relevant code which demonstrates the problem:
//make the head node
SKSpriteNode *head = [SKSpriteNode spriteNodeWithImageNamed:#"head"];
head.size = CGSizeMake(20 * [CFLConstants universalWidthScaleFactor], 20 * [CFLConstants universalWidthScaleFactor]);
head.position = position;
head.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:head.size.width/2];
head.physicsBody.categoryBitMask = CFLPhysicsCategoriesHead;
head.physicsBody.collisionBitMask = 0;
head.physicsBody.dynamic = NO;
[self.ragdollLayer addChild:head];
//make the body node
SKSpriteNode *body = [SKSpriteNode spriteNodeWithImageNamed:#"body"];
body.size = CGSizeMake(head.size.width, head.size.width * 3);
body.position = CGPointMake(head.position.x, head.position.y - head.size.height/2 - body.size.height/2);
body.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:body.size];
body.physicsBody.categoryBitMask = CFLPhysicsCategoriesBody;
body.physicsBody.collisionBitMask = 0;
[self.ragdollLayer addChild:body];
//attach the head and the body (via a neck joint)
SKPhysicsJointPin *neckJoint = [SKPhysicsJointPin jointWithBodyA:head.physicsBody bodyB:body.physicsBody anchor:CGPointMake(head.position.x, head.position.y - head.size.height/2)];
[self.physicsWorld addJoint:neckJoint];
//make the right arm
SKSpriteNode *rightArm = [SKSpriteNode spriteNodeWithImageNamed:#"arm"];
rightArm.size = CGSizeMake(head.size.width/5, head.size.width/5 * 10);
rightArm.anchorPoint = CGPointZero;
CGPoint rightArmPosition = CGPointMake(body.position.x + body.size.width * 1/5, body.position.y + body.size.height * 1/5);
rightArm.position = rightArmPosition;
rightArm.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rightArm.size];
rightArm.physicsBody.categoryBitMask = CFLPhysicsCategoriesRightArm;
rightArm.physicsBody.collisionBitMask = 0;
rightArm.zRotation = -M_PI_4;
//force which makes the arm problem even more noticeable
[body.physicsBody applyImpulse:CGVectorMake(100, 0)];
[self.ragdollLayer addChild:rightArm];
//make the joint which holds the right arm to the body, but should allow the arm to rotate about this point (and doesn't)
SKPhysicsJointPin *rightShoulderJoint = [SKPhysicsJointPin jointWithBodyA:body.physicsBody bodyB:rightArm.physicsBody anchor:rightArmPosition];
[self.physicsWorld addJoint:rightShoulderJoint];

In my experience, this is because changing the anchor point on the sprite, doesn't change the anchor point for the physics body. Though I swear sometimes you don't have to, so maybe it's an order of operations thing. But anyways, offset the center of the physics body to account for the sprite anchor point. Something like:
spriteRightArm.anchorPoint = CGPointMake(0, 1);
spriteRightArm.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spriteRightArm.size center:CGPointMake(spriteRightArm.size.width/2, -spriteRightArm.size.height/2)];

Related

Can I set anchor point of SKPhysicsJoint outside node?

Let's say I have two circles respectively at (0,0) and (0,1).
I have a SKPhysicsJoint between them and it is working good, now I want to separate them with a distance of 2 on runtime, meaning while physics are working in-game. How can I achieve this?
I've tried setting anchor points to (0,0) and (0,2) but something is bugged, although I see the joint it doesn't have any affect.
I want the circles smoothly push each other, as if the length of the spring has increased.
Everything works if I make a circle 'teleport' to a distance of 2 and then anchor the spring to it, but making a physics object teleport cause other bugs as you can guess.
Before adding the joint I 'teleported' the second object to the desired position, then added the joint and then 'teleported' back to the original position.
Here is the code piece:
SKSpriteNode* node1 = [_bodies objectAtIndex:loop];
SKSpriteNode* node2 = [_bodies objectAtIndex:loop-1];
CGPoint prev1 = node1.position;
CGPoint prev2 = node2.position;
node1.position = [((NSValue*)[positions objectAtIndex:loop]) CGPointValue];
node2.position = [((NSValue*)[positions objectAtIndex:loop-1]) CGPointValue];
[self AttachPoint:node1 secondPoint:node2 pos1:node1.position pos2:node2.posiiton] ;
node1.position = prev1;
node2.position = prev2;
it is working as it is, but I'm not sure how efficient this is. I wish SKPhysicsJointSpring had a 'length' parameter that can be changed over time.

SUVAT Maths don't add up in SpriteKit's physics engine (iOS - Objective C)

I am using SpriteKit's built in Physics Engine to build a game for iOS. Basically it involves a bouncing ball which moves via me manually setting it's initial velocity and bounces via resetting the velocity within the contact event with the floor.
The issue is, the actual maths for this environment do not add up. Using 'SUVAT' equations it's easy to determine how far the ball's x-displacement should be when it reaches the floor after being thrown with a certain velocity, however (with gravity set to -9.81), it barely moves a couple of pixels.
I simplified the problem to just trying to shoot a ball a certain distance upwards (in the y-direction) and the same thing happened, it moves a couple of points up and then just falls to the floor, at least a 20th of how far it should move.
This is how I have set the physics environment up:
self.physicsWorld.contactDelegate = self;
self.physicsWorld.gravity = CGVectorMake(0.0, -9.81);
And then this is my function for generating this ball (shooting upwards) example. Mathematically it should reach the height of the tower:
-(void)generateTestBall {
self.ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
SKSpriteNode * tower = [SKSpriteNode spriteNodeWithImageNamed:#"player"];
self.ball.position = CGPointMake(self.scene.size.width/2,self.scene.size.height/2);
self.ball.size = CGSizeMake(20,20);
self.ball.color = [SKColor redColor];
self.ball.colorBlendFactor = 1;
tower.position = CGPointMake(self.scene.size.width/2 + 20,self.scene.size.height/2+100);
tower.size = CGSizeMake(20,200);
tower.color = [SKColor blueColor];
tower.colorBlendFactor = 1;
[self addChild:tower];
[self addChild:self.ball];
self.ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
self.ball.physicsBody.affectedByGravity = YES;
self.ball.physicsBody.linearDamping = NO;
self.ball.physicsBody.dynamic = YES;
CGFloat ballVel = sqrt(2*9.81*tower.size.height);
NSLog(#"%f",ballVel);
self.ball.physicsBody.velocity = CGVectorMake(0.0f, ballVel);
}
Please can someone explain what I am doing wrong? I've double checked my maths (I'm a maths student so fingers crossed that's not the issue)!
Thanks!
Steve
So I FINALLY managed to figure it out. Just incase anyone else is experiencing the same issue I'll post the answer here:
The issue was that, although gravity is (apparently) in ms^-2 and velocity m2^-1 (to replicate earth), any distances in Objective C are measured in POINTS rather than the required form of METRES. Therefore any calculation done with x,y position / size values taken from SKSpriteNodes etc will be a certain factor out.
After running a few tests I found the factor to roughly be 157. This means that you must multiply any sizes / distances in POINTS by 157 to get the relative 'METRE' value which will work with SUVAT.
The actual numbers themselves seem a bit ridiculous as they're all very big (velocity, distance etc) but that doesn't actually pose a problem anyway as they all now work relative to each other!
Hope this helps anyone!
Steve

Spritekit physics and camera smoothing

I've been struggling with this problem for a while, which appears to be buried deep inside the spritekit physics engine.
My first question would be: Does Spritekit process its physics updates in a different thread than the main thread?
I have a "world" node in the scene which I move around to simulate a "camera" view. As such, I can center the "camera" on my player character node. Since the player jumps up and down a lot, I want to smooth the camera. Without camera smoothing, there's no problems. But when I add camera smoothing, as such: (this code is called from didFinishUpdate)
CGPoint ptPosition = self.position;
float fSmoothY = m_fLastCameraY + (ptPosition.y - m_fLastCameraY) * 0.1;
CGPoint pointCamera = [self.scene convertPoint:CGPointMake(ptPosition.x, fSmoothY) fromNode:self.parent];
[gameScene centerOnPoint:pointCamera];
m_fLastCameraY = fSmoothY;
If I call the above code from didSimulatePhysics, it does the exact same thing. It stutters whenever it falls down (due to the Y camera smoothing).
When plotting it out, you can see that the player Y (the red line) is stuttering over the course of the frames.
How can I fix this, or work around it if it can't be truly "fixed"?
I suggest you apply an infinite impulse response (IIR) filter to smooth the camera. You can do that by...
First, declare the instance variables
CGFloat alpha;
CGFloat fSmoothY;
and then Initialize alpha and fSmoothY
// alpha should be in [0, 1], where a larger value = less smoothing
alpha = 0.5;
fSmoothY = ptPosition.y;
Lastly, apply the filter with
fSmoothY = fSmoothY * (1-alpha) + ptPosition.y * alpha;

Circular body positioning not working in box2d(cocos2dx)

I'm developing a cocos2dX game. I use box2d for physics simulation. I'm trying to add a circular body and a rectangular body. Here is my code
// Create circular sprite and body
CCSprite* ball_sprite = CCSprite::create("ball.png");
this->addChild(ball_sprite);
b2BodyDef ballBodyDef;
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set(screenSize.width/PTM_RATIO, screenSize.height/2/PTM_RATIO);//im running it in an iphone retina and screensize is 640x960
ballBodyDef.userData = ball_sprite;
ball_body = _world->CreateBody(&ballBodyDef);
b2CircleShape ballshape;
ballshape.m_radius = BALL_SIZE/2;
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &ballshape;
ballShapeDef.density = 100.0f;
ballShapeDef.friction = 0.5f;
ballShapeDef.restitution = 0.7f;
ball_body->CreateFixture(&ballShapeDef);
// Create rectangular sprite and body
CCSprite* block_sprite = CCSprite::create("HelloWorld.png");
this->addChild(block_sprite);
b2BodyDef blockBodyDef;
blockBodyDef.type = b2_staticBody;
blockBodyDef.position.Set(0, screenSize.height/2/PTM_RATIO);
blockBodyDef.userData = block_sprite;
block_bodie = _world->CreateBody(&blockBodyDef);
b2PolygonShape blockshape;
blockshape.SetAsBox(B_WIDTH/PTM_RATIO,B_HEIGHT/PTM_RATIO);
b2FixtureDef blockShapeDef;
blockShapeDef.shape = &blockshape;
blockShapeDef.density = 100.0f;
blockShapeDef.friction = 0.5f;
blockShapeDef.restitution = 0.7f;
block_bodie->CreateFixture(&blockShapeDef);
The rectangular body is shown in the screen as expected.
But the circular body is not shown in the screen.
When I printed the position of circular body in the update function, the positions are large numbers around 2000. And this position is different each time I run the programm.
If the rectangular body is not added(commenting the line block_bodie->CreateFixture(&blockShapeDef);) then the circular body is shown in the screen as I expected.
What I'm doing wrong here?
Thanks in advance.
Are these two bodies overlapping when they are created? Most likely the circle is simply being pushed away by the rectangle, because the rectangle is static and the circle is dynamic. If you try creating both of them, but don't call the world Step function to run the physics simulation, you will probably see them both on screen.
You could create them so that they are not overlapping, or at least not overlapping as much, or make one of them a sensor, or set their collision category and mask bits so they don't interact.
Of course, I am assuming you are looking at the debug draw display, which is really the only way to know for sure what the physics engine is doing.

Box2D with UIKit - Body Physics

I followed this tutorial on how to add Box2D to my existing ViewBased Application.
http://www.cocoanetics.com/2010/05/physics-101-uikit-app-with-box2d-for-gravity/
Now when my body/UIButton is above half way my screen, it flies upward. and when it's half way downward, it flies downward. Also the physics are not seeming to be very realistic. I built my world the same way it is (with a couple of modifications to make it Xcode 4.0 compatible) built in the tutorial. Same goes for the timer, and the body creation. Anyone know what's wrong? (let me know if you need more detail)
Here is my code for body creation:
// Define the dynamic body.
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
CGPoint p = physicalView.center;
CGPoint boxDimensions = CGPointMake(physicalView.bounds.size.width/PTM_RATIO/2.0,physicalView.bounds.size.height/PTM_RATIO/2.0);
bodyDef.position.Set(p.x/PTM_RATIO, (self.stage.frame.size.height - p.y)/PTM_RATIO);
bodyDef.userData = (__bridge void *)physicalView;
// Tell the physics world to create the body
b2Body *body = world->CreateBody(&bodyDef);
// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(boxDimensions.x, boxDimensions.y);
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 3.0f; // 0 is a ball, and 1 is a rock
fixtureDef.friction = 0.3f; // 0 is a lubercated ball, 1 is rough as sand paper
fixtureDef.restitution = 0.5f; // 0 is a lead ball, 1 is a super bouncy ball
body->CreateFixture(&fixtureDef);
Here is my code for setting up the world:
CGSize screenSize = self.stage.bounds.size;
screenSize.width = 368;
// Define the gravity vector.
b2Vec2 gravity;
gravity.Set(0.0f, -9.81f);
// Do we want to let bodies sleep?
// This will speed up the physics simulation
bool doSleep = false;
// Construct a world object, which will hold and simulate the rigid bodies.
world = new b2World(gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(true);
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2EdgeShape groundBox;
// bottom
groundBox.Set(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox, 0);
// top
groundBox.Set(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox, 0);
// left
groundBox.Set(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox, 0);
// right
groundBox.Set(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox, 0);
// a dynamic body reacts to forces right away
body->SetType(b2_dynamicBody);
// we abuse the tag property as pointer to the physical body
physicalView.tag = (int)body;
The problem was I had an invisible UIView that programically pops into the view and started colliding with the other objects.

Resources