Cocos2d Sprite collision detection during action - ios

I am unable to detect collision between two sprites, one moving in action.
I have a sprite of class "Enemy", moving across the screen via CCMoveTo, and another sprite of class "Hero", controlled by touch, both added onto a scene on class "MainGame"
The following indicates how the two sprites are added onto the scene and Enemy actioned:
MainGame.m
-(id) init{
if( (self=[super init]) ) {
_enemies = [[NSMutableArray alloc]init];
_enemyLink = [Enemy nodeWithTheGame:self];
_enemyLink.position = ccp(10, 10);
[self.enemies addObject:_enemyLink];
CCMoveTo *test = [CCMoveTo actionWithDuration:40 position:ccp(500, 250)];
[_enemyLink runAction:test];
_heroArray = [[NSMutableArray alloc]init];
_heroLink = [Hero nodeWithTheGame:self location:ccp(100,100)];
[_heroArray addObject:_heroLink];
[self scheduleUpdate];
}
}
-(void)update:(ccTime)delta{
if (CGRectIntersectsRect([self.enemyLink.enemySprite boundingBox], [self.heroLink.heroSprite boundingBox])) {
NSLog(#"rect intersects rect");
}
for (CCSprite *enemies in self.enemies) {
NSLog(#"enemy position: %f, %f",enemies.position.x,enemies.position.y);
}
}
I am unable to detect collision between these two sprites during and after the action. However if I move the Hero to the position on the scene (0,0) the log in my code will trigger, and the two will engage in attacks.
The for loop in the update indicates that the Enemy sprite position is constantly moving during the action. Hence why I am stumped as to why the collision is not being detected.

Have you checked that both enemy and hero positions are tested within the same coordinates space?
This is because boundingBox() is local, relative to its parent and if the nodes you compare do not have the same parent, the check will be invalid.
You can use convertToNodeSpace()/convertToWorldSpace() prior to checking bounding boxes.
Another answer that may be relevant to your case here: A sprite bounding box shows wrong position after moving the layer.

Related

SpriteNode from class not rotating correctly in Scene

I have a Class with a SpriteNode that rotates very wide, when rotated within the main Scene(as if the anchor point is in the middle of the screen and the Sprite is rotating around it). I want it to rotate around the anchor point of itself in the main Scene(anchor point on the Sprite).
So in the Class i have something like the following
- (void)createChainWithPos:(CGPoint)pos {
SKTexture *myTex...
SKTexture *myTex2...
SKSpriteNode *chainFront = [SKSpriteNode spriteWithTexture:myTex];
chainFront.position = pos;
chainFront.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mytex.size];
[self addChild:chainFront];
[_chainParts addObject:chainFront];
SKSpriteNode *chainSide = [SKSpriteNode spriteWithTexture:myTex2];
chainSide.position = CGPointMake(chainFront.position.x, chainFront.position.y - chainSide.size.height + 6);
chainSide.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:myTex2.size;
[self addChild:chainSide];
[_chainParts addObject:chainSide];
}
I have an loop creating the chain parts in the main file but couldn't get it rotate so stripped it down in an new project. There is actually 4 chain parts but i only did two. The other two are just mirrors of the ones above with their positions mirroring the chainSide.(to position them in a chain like fashion)
and in the Scene
self.chain1 = [chain node];
[self.chain1 createChainWithPos:CGPointMake(self.size.width/2, self.size.height/2);
self.chain1.zRotation = 3.14/4;
[self addChild:self.chain1];
I have a NSMutableArray in the chain class header that i use to hold the chains.
the physics joints
for (int i = 1; i < self.chain1.chainParts.count; i++ {
SKSpriteNode *nodeA = [[self.chain1 chainParts]objectAtindex:i-1];
SKSpriteNode *nodeB = [[self.chain1 chainParts]objectAtindex:i];
SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:nodeA.physicsBody
bodyB:nodeB.physicsBody
anchor:CGPointMake(CGRectGetMidX(nodeA.frame), CGRectGetMinY(nodeA.Frame))];
}
I found that if i set the position of the chain in the middle in the Class, it rotates correctly in the Scene. However, the physics joints just start moving randomly across the screen and isn't correct to the anchor points set. (the physics joints are set in the Scene)
I don't know if i have to convert the coordinates or play with random anchor point positions , but if someone could shed some light if would be greatly appreciated.
You do not want to use the nodes frame, you want to use the nodes size, the frame mid is the screen coordinate + the fitted node size / 2, you want the middle of your sprite only, also for pi / 4 use M_PI_4

Move all objects in an array - IOS and Sprite Kit

So here is what I am trying to achieve;
I am trying to create a tile map system. So when the player presses the up button for example, the player stays central and the map moves down to give the look that the player is walking up the world (its a top-down game).
I have created an array of SpriteNode's and a function that creates 64x64 tiles and adds each new object to the array. I am trying to figure out a way that I can apply a function to all of the tiles.
So for example, when the up button is pressed all the tiles start to move down.
I could do this manually:
SpriteNode *tile00;
SpriteNode *tile01;
SpriteNode *tile02;
...
and then change the position of each one manually when the player moves but this is going to be very tedious (especially considering that I plan to create rather large maps)
My Question: Can I apply functions to several SpriteNodes?
You should definitely create functions:
- (void) moveUp {
// Loop through array of nodes and move them down
}
- (void) moveDown {
// Loop through array of nodes and move them up
}
- (void) moveRight {
// Loop through array of nodes and move them left
}
- (void) moveLeft {
// Loop through array of nodes and move them right
}
You can loop through the nodes if you create an NSArray of nodes and do this:
for (SKSpriteNode *n in NodeArray){ // NodeArray is you NSArray
// Do something to n
}
If you have an NSArray of tile sprites, applying an action to all of them could be done like this
SKAction *moveAction = //move all tiles down, up, etc.;
for (SKSpriteNode *tile in self.tiles) /* an NSArray containing the tile sprites */
{
[tile runAction:moveAction];
}

intersectsNode not working for right half only

I have two spriteNodes that intersect but at different positions. both sprites are rectangular. I am getting some strange behavior where it only detects an intersection between the nodes if they intersect on the left half of one of the nodes (this node is always the same one and is always a stationary node). So what could cause such a behavior?
I do have some code I can show but it seems pretty straight forward so if anyone thinks it might help just let me know?
Update: it works with a different sprite here is the code for each of them:
if (!weldRUHit && [weldRU intersectsNode:[barriers objectAtIndex:i]]) {
SKSpriteNode *nodeA = (SKSpriteNode*)[barriers objectAtIndex:i];
SKPhysicsBody *firstBody = nodeA.physicsBody;
SKSpriteNode *nodeB = weldRU;
SKPhysicsBody *secondBody = nodeB.physicsBody;
weldRUHit = YES;
[self beginIntersectionWithBodyA:firstBody bodyB:secondBody];
}
if (!weldLDHit && [weldLD intersectsNode:[barriers objectAtIndex:i]]) {
SKSpriteNode *nodeA = (SKSpriteNode*)[barriers objectAtIndex:i];
SKPhysicsBody *firstBody = nodeA.physicsBody;
SKSpriteNode *nodeB = weldLD;
SKPhysicsBody *secondBody = nodeB.physicsBody;
weldLDHit = YES;
[self beginIntersectionWithBodyA:firstBody bodyB:secondBody];
}
it works for weldLD but not for weldRU.
I am experiencing the same problem with SKNode's intersectsNode: method. I think it has to do with a combination of anchor points and of two nodes not being children of the same parent node. The frame property is used to detect node intersection, and Apple defines the frame property of SKNode as "A rectangle in the parent’s coordinate system..." Thus with different parents you have different coordinate systems. Either way, my solution is to use this function instead.
bool CGRectIntersectsRect (
CGRect rect1,
CGRect rect2
);
And just make sure you have the correct coordinate system. Hint: use SKNode's convertPoint:FromNode: method.

SpriteKit Fixed Joints

I have a SKSpriteNode called "SpikyRedBall" which is a red ball. I wanted to add spikes to it so I used the following code. I can see the Spike attached to the ball but when the ball collides with another ball it does not take the fixed joints into consideration and moves them separately. I am using the following implementation:
#implementation SpikyRedBall
-(instancetype) init
{
self = [super init];
[self attachSpikes];
return self;
}
-(void) attachSpikes
{
Spike *spike = [[Spike alloc] init];
spike.position = CGPointMake(0, 0);
// attach the joint
SKPhysicsJointFixed *ballAndSpikeJointFixed = [SKPhysicsJointFixed jointWithBodyA:self.physicsBody bodyB:spike.physicsBody anchor:CGPointZero];
[self.scene.physicsWorld addJoint:ballAndSpikeJointFixed];
[self addChild:spike];
}
#end
It sounds like you don't have collision or contact categories setup for the spikes themselves. I would try setting all physicsBody properties on the spikes to be identical to those of the balls, but obviously ensuring that they don't have collision or contact categories setup in a way that they would collide with their own parent ball.
If you can require iOS 7.1, you could use +bodyWithBodies: instead of attaching any joints.
Why don't you just add the spikes to the sprite image? If they need to disappear or fall off you can just create multiple versions of the image without spikes.

How to do a camera movement in spriteKit

I have created a SKSpriteNode for a camera with a physic body size of 0.0 , to avoid unwanted collisions and a world node:
-(void)createSceneContents {
SKNode *world = [SKNode node];
world.name = #"world";
self.anchorPoint = CGPointMake(0.1, 0);
SKSpriteNode *camera = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(300, 300)];
camera.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(0, 0)];
camera.physicsBody.affectedByGravity = NO;
camera.physicsBody.usesPreciseCollisionDetection = NO;
camera.physicsBody.categoryBitMask = noColisions;
camera.alpha = 0.5;
camera.zPosition = 1;
camera.name = #"cam";
[self addChild:world];
[world addChild:camera];
I've tried a little tutorial to add a camera in a spriteKit platform game, but i can't even move the view, i don't know hoy to access to the property that move the view. Anybody knows what am i doing wrong?
Here's my code:
-(void)didSimulatePhysics
{
//I've tried with #"cam" and #"hero"
[self centerOnNode: [self childNodeWithName:#"world"]];
}
-(void)centerOnNode:(SKNode *) camera {
CGPoint cameraPositionInScene = [camera.scene convertPoint:camera.position fromNode:camera.parent];
[self.parent setPosition:CGPointMake(
camera.parent.position.x - cameraPositionInScene.x,
camera.parent.position.y - cameraPositionInScene.y
)];
}
In the example from Apple's Documentation, which you are following the camera node isn't an SKSprite, it's an SKNode. I think that will fix your problem.
To answer the question from the title, what you're essentially doing is attaching a world node to the scene. Inside this node, all the sprites are placed. As a child to the world node you add another node for the camera.
This gives you three distinct coordinate systems. Imagine, three sheets of paper, the bottom most one is your world, ie the layer with all the sprites. On top of that is a small piece of paper that represents the camera. Above all of this you have a transparent box that represents your viewing area.
The way it's set up it's impossible to move the top most transparent viewing layer. Instead, what you're doing is moving the point that's sits on top of the world layer and then sliding the world layer to that point.
Now imagine, in the paper scenario, this is a 2D scrolling world where you can only go left and right. Now take the camera point and put it all the way to the right most side of the viewing area. Now, take the world layer and drag it to the left until the camera is in the center of the non-moveable viewing area. That is more or less, what's happening.
In Apple's Adventure sample game they don't move the camera but the "World" SKNode which is the top one.
Excerpt from Apple docs on how they do it:
In Adventure all world-related nodes, including background tiles,
characters, and foliage, are children of a world node, which in turn
is a child of the scene. We change the position of this top-of-tree
world node within the scene to give the effect of moving a camera
across the level. By contrast, the nodes that make up the HUD are
children of a separate node that is a direct child of the scene rather
than of the world node, so that the elements in the HUD don’t move
when we “move the camera.”
Read about it more here
to add the previous answers , you should center on your camera , not the world..
so instead of
[self centerOnNode: [self childNodeWithName:#"world"]];
you should use
[self centerOnNode: [self childNodeWithName:#"cam"]];
and dont forget to change your camera to SKNode instead of SKSprite.
.. and for testing, add a moveTo action on your camera node , move it around back and forth to check if your camera centering works. I recommend putting the call in the touchesbegan
example (put this on your scene where your camera is) :
Put these before the #implementation
#interface yourClassNameHere() // edit this to your own class name
#property SKNode *theWorld;
#property SKNode *theCamera;
#property BOOL cameraRunning;
#end
As you see above, i put the nodes (world and camera) on property of this class, so i dont refer them with node name like you did on your post..
Put this on the Implementation section
// Process Camera centering
-(void) didSimulatePhysics {
[self centerOnNode:self.theCamera];
}
-(void) centerOnNode: (SKNode *) node {
CGPoint pos = [node.scene convertPoint:node.position fromNode:node.parent];
CGPoint p = node.parent.position;
node.parent.position = CGPointMake(p.x - pos.x, p.y-pos.y);
}
// .. Move the camera around when you touch , to see if it works..
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (!self.cameraRunning) {
self.cameraRunning = YES;
SKAction *moveUp = [SKAction moveByX:0 y:500 duration:3];
SKAction *moveDown = [SKAction moveByX:0 y:-500 duration:3];
SKAction *moveLeft = [SKAction moveByX:-500 y:0 duration:3];
SKAction *moveRight = [SKAction moveByX:500 y:0 duration:3];
SKAction *sequence = [SKAction sequence:#[moveUp, moveRight,moveDown,moveLeft]];
[self.theCamera runAction:sequence];
} else {
self.cameraRunning = NO;
[self.theCamera removeAllActions];
self.theCamera.position = CGPointZero;
}
}
regards
PS: do you want anchor point 0,0 or 1,1 ? check your anchor point setting there
If you want to move the view, just move the camera:
// Center the view at 100, 0
camera.position = CGPointMake(100, 0);
Here's a slightly longer example here on how to set up a 2D camera system in SpriteKit (in Swift, not ObjC, but easily translated).

Resources