Different Collisions - ios

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];
}
}

Related

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

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.
}
}
}

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");
}
}

boundingBox still there after child is removed

I instance a sprite, and then when it collides with a second sprite, the child of that sprite is removed:
if (CGRectIntersectsRect(spriteOne.boundingBox, self.swat.boundingBox))
{
if (spriteOne.tag == 0){
[self removeChild:spriteOne cleanup:YES];
}
if (spriteOne.tag == 1){
[self removeChild:spriteOne cleanup:YES];
}
}
This works how I want it to, and the sprite disappears off the screen. However, it seems that the boundingBox is still there, even if the image is not, and this causes trouble with scoring, etc... So, what I would like to know is how to 'dis-activate' the sprite's boundingBox so that when the first time that the two sprites colide, the collision is detected, but any time after that, it is not.
Thanks in advance.
As far as I understand, you should have some issue with removing the sprite after a collision.
Would you try this?
if (CGRectIntersectsRect(spriteOne.boundingBox, self.swat.boundingBox))
{
if (spriteOne.tag == 0){
[spriteOne removeFromParentAndleanup:YES];
}
if (spriteOne.tag == 1){
[spriteOne removeFromParentAndleanup:YES];
}
}
Have you tried adding some NSLog traces to see whether the sprite is really removed?
You must be retaining spriteOne. If there is a good reason to keep it around, do this:
if ( spriteOne.visible && CGRectIntersectsRect(spriteOne.boundingBox, self.swat.boundingBox))
{
if (spriteOne.tag == 0){
spriteOne.visible=NO;
}
if (spriteOne.visible && spriteOne.tag == 1){
spriteOne.visible=NO;
}
}
Later, when you need spriteOne in play again, just set its visibility to YES;
If not, you have a leak, and do this:
if ( spriteOne && CGRectIntersectsRect(spriteOne.boundingBox, self.swat.boundingBox))
{
if (spriteOne.tag == 0){
[self removeChild:spriteOne cleanup:YES];
self.spriteOne=nil; // assumes you have a property for spriteOne
}
if (spriteOne && spriteOne.tag == 1){
[self removeChild:spriteOne cleanup:YES];
[spriteOne release]; // assumes no property for spriteOne
spriteOne=nil; // dont forget this ! beware of zombies
}
}

Box2D b2ContactListener (Collision Detection)

In my game, I have about 6 different variations of objects. Each object has a b2Body, b2BodyDef, and b2FixtureDef attached to it.
In my case, my b2Bodys are following my CCSprites since Cocos2D is easier with animations.
Anyway I am trying to follow Ray Wenderlich's tutorial: http://www.raywenderlich.com/606/how-to-use-box2d-for-just-collision-detection-with-cocos2d-iphone
The thing is I am quite confused on what he is actually doing!
Questions:
1. Does my Contact Listener code in my CCScene need to be in my game loop?
2. This is his main code for his collision detection in his CCScene:
3. Also I see that in the below code he uses tags for his CCSprites, does that mean my CCSprites do not have to be ivars? Also what about my b2Bodys, b2BodyDefs, and b2FixtureDefs, do they have to be ivars? Does he just do it by tags?
std::vector<b2Body *>toDestroy;
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();
if (spriteA.tag == 1 && spriteB.tag == 2) {
toDestroy.push_back(bodyA);
} else if (spriteA.tag == 2 && spriteB.tag == 1) {
toDestroy.push_back(bodyB);
}
}
}
std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[_spriteSheet removeChild:sprite cleanup:YES];
}
_world->DestroyBody(body);
}
if (toDestroy.size() > 0) {
[[SimpleAudioEngine sharedEngine] playEffect:#"hahaha.caf"];
}
The thing is, like before he only has 2 things that CAN collide. In my case I have 6. So would I have to modify that code in any way to work with my 6 possible b2Bodys?
Thanks!

Resources