Correctly removing Box2D fixtures and updating b2Body - iphone

I have a Box2d body which I'm attempting to break into multiple pieces. To do this, I iterate over its fixtures and generate new bodies for each. Using debug draw, I can see that this seems to be working.
As you can see in the above image, the primary body is being broken and a secondary body (labelled: 2) is being generated. Based on the shape rendering from the debug layer, they're being represented correctly. The issue I'm having is that the CCSprite I'm associating with my primary b2body isn't being correctly positioned in reference to the new body. It seems as though the associated CCSprite is being positioned (given an anchor point of 0, 0) as if it were still part of a larger shape.
For reference, here's the code I'm using:
for (b2Fixture *f = body->GetFixtureList(); f; f = f->GetNext())
{
NSString *newSpriteFrameName = (NSString *)f->GetUserData();
// Steal some of our parent bodies properties
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = [self physicsPosition];
bd.angle = [self angle];
b2Body *newBody = _world->CreateBody(&bd);
b2FixtureDef fixtureDef;
fixtureDef.shape = f->GetShape();
fixtureDef.density = f->GetDensity();
fixtureDef.restitution = f->GetRestitution();
fixtureDef.friction = f->GetFriction();
fixtureDef.userData = f->GetUserData();
newBody->CreateFixture(&fixtureDef);
// Try to transfer any angular and linear velocity
b2Vec2 center1 = [self worldCenter];
b2Vec2 center2 = newBody->GetWorldCenter();
CGFloat angularVelocity = parentBody->GetAngularVelocity();
b2Vec2 velocity1 = [self linearVelocity] + b2Cross(angularVelocity, center1 - center1);
b2Vec2 velocity2 = [self linearVelocity] + b2Cross(angularVelocity, center2 - center1);
newBody->SetAngularVelocity(angularVelocity);
newBody->SetLinearVelocity(velocity2);
// Create a new destructable entity
CCSprite *newSprite = [CCSprite spriteWithSpriteFrameName:newSpriteFrameName];
SIDestructableEntity *newEntity = [[SIDestructableEntity alloc] initWithBody:newBody node:newSprite];
[[newEntity ccNode] setAnchorPoint:CGPointMake(0, 0)];
[game.entities addObject:newEntity];
[game.entityLayer addChild:[newEntity ccNode]];
}
Here's how I'm setting my CCSprites location each logic tick:
b2Vec2 position = body->GetPosition();
ccNode.position = CGPointMake(PTM_RATIO*position.x, PTM_RATIO*position.y);
ccNode.rotation = -1 * CC_RADIANS_TO_DEGREES(body->GetAngle());

This line looks suspicious.
[[newEntity ccNode] setAnchorPoint:CGPointMake(0, 0)];
The sprite usually has an anchor point of (0.5,0.5). If your body's anchor point is in the middle (can't tell from code above), then an anchor of (0.5,0.5) for the sprite would put it in the middle as well. Putting it at (0,0) puts the sprite's top left corner at the position of the sprite.
My guess is that your body anchor is at the bottom left of body and the sprite anchor is at the top right, giving the effect you are seeing.

Related

SKNode nodeAtPoint: / containsPoint: not the same behaviour for SKSpriteNode and SKShapeNode

The nodeAtPoint: gives not the same result if using SKShapeNode and SKSpriteNode. If i am correct nodeAtPoint: will use containsPoint: to check which nodes are at the given point.
The docu states that containsPoint: will use its bounding box.
I set up a simple scene, where in situation 1 the circle is parent of the purple node and in situation 2 the green node is parent of the purple node.
I clicked in both cases in an area where the bounding box of the parent should be.
The result is differs. If i use a SKSpriteNode the nodeAtPoint: will give me the parent. If i use SKShapeNode it returns the SKScene.
(The cross marks where i pressed with the mouse.)
The code:
First setup:
-(void)didMoveToView:(SKView *)view {
self.name = #"Scene";
SKShapeNode* circle = [SKShapeNode node];
circle.path = CGPathCreateWithEllipseInRect(CGRectMake(0, 0, 50, 50), nil);
circle.position = CGPointMake(20, 20);
circle.fillColor = [SKColor redColor];
circle.name = #"circle";
SKSpriteNode* pnode = [SKSpriteNode node];
pnode.size = CGSizeMake(50, 50);
pnode.position = CGPointMake(50, 50);
pnode.color = [SKColor purpleColor];
pnode.name = #"pnode";
[self addChild: circle];
[circle addChild: pnode];
}
Second setup:
-(void)didMoveToView:(SKView *)view {
self.name = #"Scene";
SKSpriteNode* gnode = [SKSpriteNode node];
gnode.size = CGSizeMake(50, 50);
gnode.position = CGPointMake(30, 30);
gnode.color = [SKColor greenColor];
gnode.name = #"gnode";
SKSpriteNode* pnode = [SKSpriteNode node];
pnode.size = CGSizeMake(50, 50);
pnode.position = CGPointMake(30, 30);
pnode.color = [SKColor purpleColor];
pnode.name = #"pnode";
[self addChild: gnode];
[gnode addChild: pnode];
}
Call on mouse click:
-(void)mouseDown:(NSEvent *)theEvent {
CGPoint location = [theEvent locationInNode:self];
NSLog(#"%#", [self nodeAtPoint: location].name);
}
Did i miss something? Is it a bug in SpriteKit? Is it meant to work that way?
The short answers: yes, no, yes
The long answer...
The documentation for nodeAtPoint says that it
returns the deepest descendant that intersects a point
and in the Discussion section
a point is considered to be in a node if it lies inside the rectangle returned by the calculateAccumulatedFrame method
The first statement applies to SKSpriteNode and SKShapeNode nodes, while the second only applies to SKSpriteNode nodes. For SKShapeNodes, Sprite Kit ignores the node's bounding box and uses the path property to determine if a point intersects the node with CGPathContainsPoint. As shown in the figures below, shapes are selected on a per-pixel basis, where white dots represent the click points.
Figure 1. Bounding Boxes for Shape (blue) and Shape + Square (brown)
Figure 2. Results of nodeAtPoint
calculateAccumulatedFrame returns a bounding box (BB) that is relative to its parent as shown in the figure below (brown box is the square's BB). Consequently, if you don't adjust the CGPoint for containsPoint appropriately, the results will not be what you expected. To convert a point from scene coordinates to the parent's coordinates (or vice versa), use convertPoint:fromNode or convertPoint:toNode. Lastly, containsPoint uses a shape's path instead of its bounding box just like nodeAtPoint.

Move SKSpriteNode with Physics

I am trying to move an SKSpriteNode with physics by setting it's velocity. I am doing this on top of another SKNode (the map) that is part of the SKScene. Positions of "cities" are stored in custom Location objects.
I know the location of the initial SKSpriteNode (the ship) and I know the desired location. From what I've read, I can move the sprite by setting it's velocity. I do so like this:
float dy = _player.desiredLocation.yCoordinate - _mapNode.pin.position.y;
float dx = _player.desiredLocation.xCoordinate - _mapNode.pin.position.x;
_mapNode.pin.physicsBody.velocity = CGVectorMake(dx, dy);
This is all inside the didSimulatePhysics function inside the SKScene. Once the user taps a Location, I find the position and set the velocity. This seems to work well the FIRST time, but the sprite moves all over the place in subsequent times. Any idea what could be going wrong here?
PS: Setting skView.showsPhysics = YES; puts the circle of the physics body way off the position of the sprite.
In the map node:
self.pin = [SKSpriteNode spriteNodeWithImageNamed:[IPGameManager sharedGameData].world.player.ship.type];
self.pin.userInteractionEnabled = NO;
self.pin.position = CGPointMake([IPGameManager sharedGameData].world.player.location.xCoordinate, [IPGameManager sharedGameData].world.player.location.yCoordinate);
self.pin.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10.0];
self.pin.physicsBody.dynamic = YES;
self.pin.physicsBody.allowsRotation = NO;
self.pin.physicsBody.friction = 0.0;
self.pin.physicsBody.linearDamping = 0.0;
[self addChild:self.pin];

SpriteKit 1 Dimensional Movement

I'm using apple's Sprite Kit and I need to move a SKSprite Node in horizontal movement only. I want the rest of the physics to apply but only in the horizontal component.
Context: This is for an object supposedly on a slider that can bounce back and forth. I have everything done but if it is hit the end the wrong way it simply floats off vertically, how can I simply make it ignore all forces in the vertical direction.
By putting the node's position back at the desired Y coordinate every frame after physics has been simulated:
-(void) didSimulatePhysics
{
CGPoint pos = horizontalMoveNode.position;
pos.y = fixedVerticalPosY;
horizontalMoveNode.position = pos;
}
Add this method to your scene class and apply it to whichever node(s) you want to lock in at a given Y coordinate.
You could use constraints for this purpose. I made a short sample with a node that only moves in a fixed X range and never leaves a specified Y position:
SKSpriteNode* node = [SKSpriteNode node];
node.color = [SKColor greenColor];
node.size = CGSizeMake(20, 20);
SKRange* rangeX = [[SKRange alloc] initWithLowerLimit: 100 upperLimit: 400];
SKRange* rangeY = [SKRange rangeWithConstantValue: 100];
SKConstraint* positionConstraint = [SKConstraint positionX: rangeX Y: rangeY];
NSArray* constraintArray = [NSArray arrayWithObject: positionConstraint];
node.constraints = constraintArray;
[self addChild: node];
This method is from SKAction class to move objects only on Horizontal or X-axis:-
[mySpriteNode runAction:[SKAction moveToX:260 duration:0.5]];
I hope this work's for you.

Cocos2d, Box2D Still body till input

Hey guys i have a question here, How do i create a body that will not have physic function until i press it? i have this code in my init
CCSprite *tail = [CCSprite spriteWithFile:#"Ball.jpg"];
[self addChild:tail z:1];
b2BodyDef tailBodyDef;
tailBodyDef.type = b2_dynamicBody;
tailBodyDef.position.Set(100/PTM_RATIO, 100/PTM_RATIO);
tailBodyDef.userData = tail;
tailBody = world->CreateBody(&tailBodyDef);
b2CircleShape circle;
circle.m_radius = 26.0/PTM_RATIO;
b2FixtureDef tailShapeDef;
tailShapeDef.shape = &circle;
tailShapeDef.density = 1.0f;
tailShapeDef.friction = 0.2f;
tailShapeDef.restitution = 0.8f;
tailBody->CreateFixture(&tailShapeDef);
[self schedule: #selector(tick:)];
The ball will drop off the the edge of screen at the start of game, but thats not what i want. i want it to stay at the same position until i press it. is there anyway i could hold the object back until i give some input?
Haven't tried it but toggling the setActive property seems perfect.
tailBody->setActive(NO);
Check out the 'activation' section here: http://www.box2d.org/manual.html#_Toc258082973

How to set the position of a sprite within a box2d body?

Basically I have 2 polygons for my body. When I add a sprite for userData, the position of the texture isn't where I want it to be. What I want to do is adjust the position of the texture within the body. Here's the code sample of where I am setting this:
CCSpriteSheet *sheet = (CCSpriteSheet*) [self getChildByTag:kTagSpriteSheet];
CCSprite *pigeonSprite = [CCSprite spriteWithSpriteSheet:sheet rect:CGRectMake(0,0,40,32)];
[sheet addChild:pigeonSprite z:0 tag:kPigeonSprite];
pigeonSprite.position = ccp( p.x, p.y);
bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
bodyDef.userData = sprite;
b2Body *body = world->CreateBody(&bodyDef);
b2CircleShape dynamicCircle;
dynamicCircle.m_radius = .25f;
dynamicCircle.m_p.Set(0.0f, 1.0f);
// Define the dynamic body fixture.
b2FixtureDef circleDef;
circleDef.shape = &dynamicCircle;
circleDef.density = 1.0f;
circleDef.friction = 0.3f;
body->CreateFixture(&circleDef);
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.0f);
b2PolygonShape triangle;
triangle.Set(vertices, 3);
b2FixtureDef triangleDef1;
triangleDef1.shape = ▵
triangleDef1.density = 1.0f;
triangleDef1.friction = 0.3f;
body->CreateFixture(&triangleDef1);
I'm not that familiar with objective-c but I'll give it a try.
All I can see is that you are storing a pointer to the sprite object in the body's user data and then leaving it there. If you want the body's position to be transferred to the sprite you need to update it every frame.
In C++ this would look something like this.
// To be called each time physics should be updated.
void physicsStep(float32 timeStep, int32 velocityIterations, int32 positionIterations) {
// This is the usual update routine.
world.Step(timeStep, velocityIterations, positionIterations);
world.ClearForces();
// SpriteClass can be replaced with any class you favor.
// Assume there is a known pointer to the b2Body. Otherwise you'll have to get that,
// or iterate over all bodies in the world.
SpriteClass *sprite = (SpriteClass*)body->GetUserData();
// Once you have the pointer you can transfer all the data.
sprite.position = body->GetPosition();
sprite.angle = body->GetAngle();
// ... and so on
}
User data is just an arbitrary storage space in the b2Body and Box2D has no idea about what you decide to store there.
To move sprite with body then You have to set the sprite position accotring to body
in your update method
like this in COCOS2D-X
Here
sprite = static_cast<CCSprite*>body->GetUserData();
sprite->setPosition(vec2(body->GetPosition().x*PTM_RATIO, body->GetPosition().y*PTM_RATIO));
sprite->setRotation(-CC_Radian_to_Degree(body->GetAngle));
PTM_RATIO = 32;