How to translate SKPhysicsContact contactPoint into my SKSpriteNode position - sprite-kit

I've spent too long on this. Someone has solved this already, please help.
Enemy fires on my ship. SKPhysicsContact takes over and I get contactPoint. I explode the fired missile at the contactPoint, in world coordinates. All good. Now I would like to use the point, not in the scene coordinates returned by contactPoint, but in my ship coordinates to start emitting smoke from the ship, where I've been hit. I've used convertPoint function before, with success, but I can't seem to get it right here...Docs say the contactPoint is expressed in Scene coordinates, while myShip has its coordinates, living as a child of a world node, which is a child of the scene node. They are in the same Node hierarchy. I think...I have a Scene->World->Camera, where myShip is a child of World. My code, I think, is saying convert contactPoint from Scene coordinates to coordinates in myShip. But this does not work. Nor does any other combination. What am I missing? I suspect the Camera hierarchy, but unsure. The numbers being returned into smoke.position are way out of whack...
- (SKEmitterNode *) newSmokeEmitter: (SKPhysicsContact *) contact
{
NSString *smokePath = [[NSBundle mainBundle] pathForResource:#"ShipSmoke" ofType:#"sks"];
SKEmitterNode *smoke = [NSKeyedUnarchiver unarchiveObjectWithFile:smokePath];
smoke.targetNode = myShip;
smoke.name = #"shipSmoke";
smoke.position = [self convertPoint:contact.contactPoint toNode:myShip];
//my temporary kludge that places the smoke on the ship, randomly
//smoke.position = CGPointMake(skRand(-25,25), skRand(-25,+25));
NSLog(#"Ship at world pos: %f,%f", myShip.position.x, myShip.position.y);
NSLog(#"Contact at scene pos: %f,%f", contact.contactPoint.x, contact.contactPoint.y);
NSLog(#"Smoke position at ship pos: %f,%f", smoke.position.x, smoke.position.y);
[myShip addChild:smoke];
return smoke;
}

If you haven't solved it already, the answer is to convert from the node's scene:
smoke.position = [self convertPoint:contact.contactPoint fromNode:self.scene];
Or, in Swift:
smoke.position = convertPoint(contact.contactPoint, fromNode: scene)

If I understand your node hierarchy correctly this could fix the issue.
CGPoint contact = [self convertPoint:contact.contactPoint toNode:self.world];
smoke.position = [self.world convertPoint:contact toNode:myShip];
I don't think you can convert the point directly to ship. You have to convert it down the hierarchy until you get to the node you want.
Hopefully that helps.
Edit
To verify that the actual contact point is on the scene where I would expect you could try this.
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(50, 50)];
sprite.position = contact.contactPoint;
[self addChild:sprite];
This should add a sprite exactly where the contact point is if the point returned is in the scene coordinate system. If it it does show up at the right spot than it truly does come down to converting points correctly.

Related

bodyWithEdgeLoopFromRect doesn't match SKSpriteNode

I have in my scene playing board (for Tetris) - smaller then scene (750x510px). I need to frame it with bodyWithEdgeLoopFromRect. Here is a code :
SKSpriteNode *herniPlocha = [[SKSpriteNode alloc]initWithImageNamed:#"Plocha"];
herniPlocha.anchorPoint = CGPointMake(0, 0);
herniPlocha.position = CGPointMake(10, self.size.height-450);
herniPlocha.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:herniPlocha.frame];
[self addChild:herniPlocha];
but when I try to put block inside this board, the physicsBody doesn't match my board, it is about 40px moved to the right and up. Can you help me solve this problem, please?
Don't change the anchorPoint. The anchorPoint shifts the texture relative to the node's position and does nothing else. It certainly does not change where the physics shape is relative to the node.

Move SKSpriteNode with CoreMotion

I have a SKSpriteNode that represents the user and I'd like this node gets move with the user movement, if the user walks then the node moves in the same direction.
The start position is the center of the view and I think it's possible to make this works with rotationRate, userAcceleration and gravity from CMMotionManager (iOS 7) but I don't know if I'm wrong or what operations I should do with these values.
I appretiate if someone could help me.
Thanks.
Now, I'm do
if ([self.motionManager isDeviceMotionAvailable] == YES) {
[self.motionManager startDeviceMotionUpdatesToQueue:self.queueMotion withHandler:^(CMDeviceMotion *motion, NSError *error) {
self.xpos = motion.userAcceleration.x * motion.gravity.x * motion.rotationRate.x;
self.ypos = motion.userAcceleration.y * motion.gravity.y * motion.rotationRate.y;
[self performSelectorOnMainThread:#selector(updatePosition:) withObject:nil waitUntilDone:YES];
}];
}
Then I add these x and y coordinates to the SKSpriteNode position and I move the SKSpriteNode to this final position. What I am getting is that the SKSpriteNode doesn't get move while I'm walking but if I make a hard acceleration it moves but just a moment, if I'm walking with a constant velocity it doesn't get move.

How to move the PhysicsSprite in box2d

I am having a physicsSprite of kinematics body type and I want to move the body and sprite bit down and back to its position. I tried it like this Inside update method:
for (NSValue *bodyValue in [self getGoalPostBodiesList])
{
b2Body *gPBody = (b2Body *)[bodyValue pointerValue];
PhysicsSprite *pSprite =(PhysicsSprite *) gPBody->GetUserData();
NSLog(#"\n tag value of Sprite = %d",pSprite.tag);
if(pSprite == goal1)
{
pSprite.position = CGPointMake((gPBody->GetPosition().x)*32.0,(gPBody->GetPosition().y)*32.0);
float angle = gPBody->GetAngle();
pSprite.rotation = -(CC_RADIANS_TO_DEGREES(angle));
id moveDownAction = [CCMoveTo actionWithDuration:0.5 position:CGPointMake(pSprite.position.x,(pSprite.position.y )- 40)];
id moveUpAction = [CCMoveTo actionWithDuration:0.5 position:CGPointMake(pSprite.position.x,(pSprite.position.y )+ 40)];
CCSequence *seqAction = [CCSequence actions:moveDownAction,moveUpAction, nil];
[pSprite runAction:seqAction];
b2Vec2 pos = b2Vec2(pSprite.position.x/32.0, pSprite.position.y/32.0);
gPBody->SetTransform(pos, CC_DEGREES_TO_RADIANS(pSprite.rotation));
gPBody->SetLinearVelocity(b2Vec2(0.0f, 0.0f));
gPBody->SetAngularVelocity(0.0f);
}
}
Still the sprite is not changing its position.
Anyone's help will be deeply appreciated.
Thanks all,
MONISH
To summarize your code, you update the position of your sprite to reflect that of the body, start an animation, and then update the position of the body to correspond to the position of the sprite. So naturally, nothing should move here, since your CCMoveTo actions have not exerted any effect on your sprite yet.
Second, your update method may be called very often, like dozens of times per second, so the animation gets reset continously and will not make any visible progress.
To follow a consistent pattern, how about you set the velocity of your kinematic bodies. Also, update the position of your sprites to correspond to these bodies as you would do for dynamic bodies, but don't set the transformation of your bodies to correspond to their sprites.

can't offset ccnode to let me center my main character

I have a standard cocos2d startup layer( HelloWorldLayer). I created another class of type CCNode named "Terrain" for my terrain. Then i add it to my layer in the layer's init:
terrain = [[Terrain alloc] initWithWorld:world AndLevel:0];
[self addChild:terrain z:1];
i add a 'CarObject' class (a CCSprite class), and add a car object to my terrain
car = [[CarObject alloc] initWithWorld:world];
[terrain addChild:car];
-i.e. in both the initWithWorld for terrain and car, i initialize some Box2d code
I then try to center my car object to my screen when i move it, i do this in my update method:
float offsetX = car.position.x;
float offsetY = car.position.y;
[terrain setOffsetX:(int)offsetX andOffsetY:(int)offsetY];
where the setOffsetX.. method is:
- (void) setOffsetX:(int)newOffsetX andOffsetY:(int)newOffsetY {
_offsetX = newOffsetX;
_offsetY = newOffsetY;
CGSize winSize = [CCDirector sharedDirector].winSize;
self.position = CGPointMake(-(_offsetX - winSize.width/2), -(_offsetY - winSize.height/2));
}
When i use a NSLog to see if the terrain position changes, i can see that the position actually chages, but the view does not. What am i doing wrong? am sure it's a dumb mistake!
btw, if i try this in my HelloWorldLayer's update method (instead of [terrain setOffsetX..])
self.position = CGPointMake(self.position.x-1, self.position.y);
the terrain is moving.
Car is a child of Terrain. Car's position is therefore relative to Terrain's position. Since you base Terrain's position on Car's position, which is actually relative to Terrain's position, you may be simply running into the effect that your position updates simply cancel each other out.
If you want to move the Terrain while keeping the Car centered, you shouldn't add the Car as a child of Terrain. Instead add it to the same node as the Terrain (HelloWorldLayer). Then you can move the Car and Terrain independently of each other.

Cocos2D CCNode position in absolute screen coordinates

I've been looking around for a while, and I have not been able to find an answer to this for some reason. It seems simple enough, but maybe I just can't find the right function in the library.
I have a scene with a layer that contains a bunch of CCNodes with each one CCSprite in them.
During the application, I move around the position of the main layer, so that I "pan" around a camera in a way. (i.e. I translate the entire layer so that the viewport changes).
Now I want to determine the absolute position of a CCNode in screen coordinates. The position property return the position relative to the parent node, but I really would like this transformed to its actual position on screen.
Also, as an added bonus, it would be awesome if I could express this position as coordinate system where 0,0 maps to the upper left of the screen, and 1,1 maps to the lower right of the screen. (So I stay compatible with all devices)
Edit: Note that the solution should work for any hierarchy of CCNodes preferably.
Every CCNode, and descendants thereof, has a method named convertToWorldSpace:(CGPoint)p
This returns the coordinates relative to your scene.
When you have this coordinate, flip your Y-axis, as you want 0,0 to be in the top left.
CCNode * myNode = [[CCNode alloc] init];
myNode.position = CGPointMake(150.0f, 100.0f);
CCSprite * mySprite = [[CCSprite alloc] init]; // CCSprite is a descendant of CCNode
[myNode addChild: mySprite];
mySprite.position = CGPointMake(-50.0f, 50.0f);
// we now have a node with a sprite in in. On this sprite (well, maybe
// it you should attack the node to a scene) you can call convertToWorldSpace:
CGPoint worldCoord = [mySprite convertToWorldSpace: mySprite.position];
// to convert it to Y0 = top we should take the Y of the screen, and subtract the worldCoord Y
worldCoord = CGPointMake(worldCoord.x, ((CGSize)[[CCDirector sharedDirector] displaySizeInPixels]).height - worldCoord.y);
// This is dry coded so may contain an error here or there.
// Let me know if this doesn't work.
[mySprite release];
[myNode release];