Box2D b2ContactListener (Collision Detection) - ios

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!

Related

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

How do I link all the UISlider events to one method?

I have the following method in my view-controller:
- (IBAction)itemSlider:(UISlider *)itemSlider withEvent:(UIEvent*)e;
{
UITouch * touch = [e.allTouches anyObject];
if( touch.phase != UITouchPhaseMoved && touch.phase != UITouchPhaseBegan)
{
}
else if( touch.phase != UITouchPhaseMoved && touch.phase != UITouchPhaseBegan)
{
}
...
}
Do I really need to link all the events one by one to my controller?
if you are seeking to capture the value changed on slider , then you may follow up the below tutorials instead of using touch events.
http://www.techrepublic.com/blog/software-engineer/better-code-uislider-basics-for-apple-ios/

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.

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

Resources