SpriteKit - Detect if node hits another nodes children and perform action - ios

I have a Cat node, and a Bird node. The bird nodes are nested together in a container node called a birdBlock. Everything is contained in a WorldNode. If I add a bird to the WorldNode, the Cat can interact with it appropriately, but when the birds are in the birdBlock, the Cat just shoves them out of the way and they go flying.
I am using the following to find my birds:
[worldNode enumerateChildNodesWithName:kBirdName usingBlock:^(SKNode *node, BOOL *stop)
{
SKSpriteNode *newBird = (SKSpriteNode *)node;
if (CGRectIntersectsRect(newBird.frame,cat.frame))
{
//Do Something
//This is never reached when the birds are in the SKSpriteNode birdBlock.
//They just get shoved all over the screen.
}
}];
The birds in the block have the correct name.
They are now being enumerated, but still do not interact with the cat other than flying around the screen.
Now I am doing this:
[[worldNode children] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
SKSpriteNode *blockNode = (SKSpriteNode *)obj;
if ([blockNode.name isEqualToString:kBirdBlockName])
{
[blockNode enumerateChildNodesWithName:kBirdName usingBlock:^(SKNode *node, BOOL *stop)
{
SKSpriteNode *nbird = (SKSpriteNode *)node;
NSLog(#"FOUND BIRDS HERE");
//THIS IS FOUND! But below still does not work
if (CGRectIntersectsRect(nbird.frame, cat.frame))
{
NSLog(#"Hit BIRD");
[nbird removeFromParent];
}
}
}
}];
So this does not work either. How do you change the coordinate system of a sprite?

You can search the node tree in many ways. The SKNode documentation explains it clearly under the section Searching the Node Tree.
If you have a node named #"node" then you can search through all descendants by putting a // before the name. So you would search for #"//node".
Try changing your constant kBirdName to equal #"//my_bird_name"
An example using a string literal would be -
[worldNode enumerateChildNodesWithName:#"//nodeName" usingBlock:^(SKNode *node, BOOL *stop)
{ // Do stuff here... }

I could not get this to work, so I just gave all the birds a physicsBody, and moved the detection to didBeginContact
Now it does not matter what their parent is and I don't need to worry about changing coordinates.
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKSpriteNode *firstNode = (SKSpriteNode *)contact.bodyA.node;
SKSpriteNode *secondNode = (SKSpriteNode *) contact.bodyB.node;
if ((firstNode.physicsBody.categoryBitMask == catCategory && secondNode.physicsBody.categoryBitMask == birdCategory) || (firstNode.physicsBody.categoryBitMask == birdCategory && secondNode.physicsBody.categoryBitMask == catCategory))
{
NSLog(#"DID HIT BIRD");
if (firstNode.physicsBody.categoryBitMask == catCategory) {
//do something to secondNode.
}
if (firstNode.physicsBody.categoryBitMask == birdCategory) {
//do something to firstNode.
}
}
}

Related

SpriteKit Incorrectly Detecting Multiple Collisions

I have been going through a SpriteKit tutorial that makes a Flappy Bird Style Game. One of the issues I am having, is that it is firing off the code for collision detection incorrectly.
Sometimes, this goes perfect...it hits the ground, it fires the method for when it collides with the ground. However, at seemingly random times, it will hit the ground, and fire off the method for ground collisions anywhere from 2-6 times. It doesn't matter if any other nodes are present on the screen or not. I can sit and let it drop immediately, and sometimes I get the collision code correctly ran once, other times it runs several times. Is there something wrong in this code causing it to do that?
UPDATE: It seems to be where the two objects meet on multiple intersecting points. If object A intersects with object B at 3 points, it will fire 3 times. How do you keep it from doing this?
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ((firstBody.categoryBitMask & pillerCategory) != 0 &&
(secondBody.categoryBitMask & flappyBirdCategory) != 0)
{
[self pillar:(SKSpriteNode *) firstBody.node didCollideWithBird:(SKSpriteNode *) secondBody.node];
}
else if ((firstBody.categoryBitMask & flappyBirdCategory) != 0 &&
(secondBody.categoryBitMask & bottomBackgroundCategory) != 0)
{
[self flappyBird:(SKSpriteNode *)firstBody.node didCollideWithBottomScoller:(SKSpriteNode *)secondBody.node];
}
}
- (void)pillar:(SKSpriteNode *)pillar didCollideWithBird:(SKSpriteNode *)bird
{
NSLog(#"Did collide with bird");
[self showGameOverLayer];
}
- (void)flappyBird:(SKSpriteNode *)bird didCollideWithBottomScoller:(SKSpriteNode *)bottomBackground
{
NSLog(#"Did collide with scroller");
[self showGameOverLayer];
}
The easiest way i would solve this problem is by using this.
1st
Create a BOOL called running.
BOOL running;
2nd
Set running to YES when the game started
running = YES;
3rd
Place an if statement around your collision code like so,
if(running == YES)
{
//do collision detection
}
else
{
//do nothing
}
You can also use this running bool to control various other useful parts such as your update method.

SpriteKit: How can I determine when methods like applyImpulse on a physicsbody are done?

I can see here how to use the update() function to monitor properties like "position" on an SKNode but I don't see how I would know how methods like [node.physicsBody applyImpulse:vector] has finished.
-(void)someMethod {
_monitorOn = YES;
[_node.physicsBody applyImpulse:CGVectorMake(10,10)];
}
-(void)update:(CFTimeInterval)currentTime {
if( _monitorOn == YES ) {
NSLog(#"node position: %f,%f", _node.position.x, _node.position.y);
}
// When will this be turned off?
}
Here are two ways to check if the effect of applyImpulse is completed:
if (_node.physicsBody.resting) {
// Node is at rest, do something
}
You'll often find that the resting property is never set because your sprite is moving very slowly (particularly with circle nodes). Therefore, it's better to check if the speed is nearly zero.
static inline CGFloat speed(const CGVector v)
{
return sqrtf(v.dx*v.dx+v.dy*v.dy);
}
if (speed(_node.physicsBody.velocity) < kSmallValue) {
// Node is moving very slowly, do something
}
applyImpulse simply adds some velocity to your _node; it only does so when you call it and is "finished" after one frame. I think what you're really looking for is when _node stops moving (which will happen the physics engine determines that _node's velocity is zero). To check for this, you can look at SKPhysicsBody's resting property. Simply check for it in your update: loop; when it's true your _node has stopped.
-(void)update:(CFTimeInterval)currentTime {
if( _monitorOn == YES ) {
NSLog(#"node position: %f,%f", _node.position.x, _node.position.y);
}
if( _node.physicsBody.resting ) {
NSLog(#"node is stopped");
}
}
Note: You'll probably want to set an addition flag somewhere that you can use to see if you should be checking if _node is resting, otherwise you're going to get a ton of "node is stopped" messages.
-(void)someMethod {
_monitorOn = YES;
_appliedImpulse = YES;
[_node.physicsBody applyImpulse:CGVectorMake(10,10)];
}
-(void)update:(CFTimeInterval)currentTime {
if( _monitorOn == YES ) {
NSLog(#"node position: %f,%f", _node.position.x, _node.position.y);
}
if( _appliedImpulse && _node.physicsBody.resting ) {
_appliedImpulse = NO;
NSLog(#"node is stopped");
}
}

Different Collisions

I have two collisions in my game that happen when the main characters hits an obstacle, game over appears and one where it touches the ground and nothing happens. This are the codes
-(void)didBeginContact:(SKPhysicsContact *)contact
{
if ([contact.bodyA.node.name isEqualToString:#"ground"] || [contact.bodyB.node.name isEqualToString:#"ground"]) {
[hero land];
} else {
[self gameover];
}
How can I add a different main character/hero collision where it doesn't lead me to game over but a whole different outcome (Like a different reaction for the main character)
Try detecting contact for the other special cases:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
BOOL isContactWithGround = ([contact.bodyA.node.name isEqualToString:#"ground"] || [contact.bodyB.node.name isEqualToString:#"ground"]);
BOOL isContactWithHero = ([contact.bodyA.node.name isEqualToString:#"hero"] || [contact.bodyB.node.name isEqualToString:#"hero"]);
BOOL isContactWithOtherThing = ([contact.bodyA.node.name isEqualToString:#"otherThing"] || [contact.bodyB.node.name isEqualToString:#"otherThing"]);
if (isContactWithHero && isContactWithGround) {
[hero land];
} else if (isContactWithHero && isContactWithOtherThing) {
[self doSomethingElse];
else {
[self gameover];
}
}

More efficient code to test node names

In the game I'm making I have a heart count at the bottom. The idea is to remove one heart when the user touches a "bomb". Right now, I create all hearts at the beginning of the program and name each one "heart1" "heart2" and "heart3". In the doDamage method, add 1 to the hitCount when the user touches a bomb. Then I proceed to remove the heart. I have this code right now. Is there any better and more efficient solution for this? Heere's the code I'm using right now.
- (void) doDamage {
//Sets the isDamaged value to yes and adds a hit point
isDamaged = YES;
hitCount++;
NSLog(#"The hit count is %i ", hitCount);
//calls the damageDone method after a second to avoid being damaged all the frames the bomb touches the basket
[self performSelector:#selector(damageDone) withObject:nil afterDelay:0.5];
//This lengthy code is used to test what heart should be removed each time...
[self enumerateChildNodesWithName:#"heart1" usingBlock:^(SKNode *node, BOOL *stop){
//If the hitCount is the same as the heart we want to remove, then we'll remove it from the parent
if (hitCount == 1) {
[node removeFromParent];
}
}];
[self enumerateChildNodesWithName:#"heart2" usingBlock:^(SKNode *node, BOOL *stop){
if (hitCount == 2) {
[node removeFromParent];
}
}];
[self enumerateChildNodesWithName:#"heart3" usingBlock:^(SKNode *node, BOOL *stop){
if (hitCount == 3) {
[node removeFromParent];
}
}];
}
I would suggest hiding the heart nodes instead of removing them, because then you can bring them back anytime (powerups or something).
SKNode *heartNode;
switch (hitCount)
{
case 1:
heartNode = [self childNodeWithName:#"heart1"];
break;
case 2:
heartNode = [self childNodeWithName:#"heart2"];
break;
case 3:
heartNode = [self childNodeWithName:#"heart3"];
break;
}
if (heartNode)
{
[heartNode setHidden:TRUE];
}
Appreciate this already has an accepted answer, but if code speed is important wouldn't this be quicker...
SKNode *heartNode = [self childNodeWithName:[NSString stringWithFormat:#"heart%d",hitCount]];
[heartNode setHidden:TRUE];
If heartNode is nil then the setHidden does nothing
Just not sure if doing stringWithFormat is quicker than a switch statement

Cocos2d + box2D remove sprite from world

Should be easy but i don't have find...
I have a lot of sprites in my world and on a moment X, i need to destroy multiples sprites.
With the code below, i can remove this sprite by tag number:
CCSprite *sprite = (CCSprite *)[self getChildByTag:TagFromMyArray];
[self removeChild:sprite cleanup:YES];
The problem is the body that remain on screen...and crash the game on the next tick.
With the code below i can find "a body":
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
if (sprite.tag = [[[myGrille.grille objectAtIndex:point.x] objectAtIndex:point.y]blockTag]) {
[self removeChild:sprite cleanup:YES];
world->DestroyBody(b);
}
}
}
The problem is that it remove every body on the world.
Isn't b a reference to a unique body ?
I was around this for hours and it was a small error...
Just have to replace "=" with "==" in the last if condition.

Resources