Collision detection Fail in Box2d - ios

I want to detect collision between bodies, one body has circle shape and 30+ have convex body. Maybe problem is because of detecting collision between circle and convex? Please help, can't find the answer for 2 days...
I have 3 classes: Player, ConctactListener and level1(where I create polygons).
In Player I set the type kGameObjectPlayer:
- (id) init {
if ((self = [super init])) {
type = kGameObjectPlayer;
}
return self;
}
-(void) createBox2dObject:(b2World*)world {
b2BodyDef playerBodyDef;
playerBodyDef.type = b2_dynamicBody;
playerBodyDef.position.Set(self.position.x/PTM_RATIO, self.position.y/PTM_RATIO);
playerBodyDef.userData = self;
playerBodyDef.fixedRotation = true;
body = world->CreateBody(&playerBodyDef);
b2CircleShape circleShape;
circleShape.m_radius = 0.7;
b2FixtureDef fixtureDef;
fixtureDef.shape = &circleShape;
fixtureDef.density = 1.0f;
fixtureDef.friction = 1.0f;
fixtureDef.restitution = 0.0f;
body->CreateFixture(&fixtureDef);
}
In ContactListener:
void ContactListener::BeginContact(b2Contact *contact) {
GameObject *o1 = (GameObject*)contact->GetFixtureA()->GetBody()->GetUserData();
GameObject *o2 = (GameObject*)contact->GetFixtureB()->GetBody()->GetUserData();
if (IS_PLATFORM(o1, o2) && IS_PLAYER(o1, o2)) {
CCLOG(#"-----> Player made contact with platform!");
}
}
void ContactListener::EndContact(b2Contact *contact) {
GameObject *o1 = (GameObject*)contact->GetFixtureA()->GetBody()->GetUserData();
GameObject *o2 = (GameObject*)contact->GetFixtureB()->GetBody()->GetUserData();
if (IS_PLATFORM(o1, o2) && IS_PLAYER(o1, o2)) {
CCLOG(#"-----> Player lost contact with platform!");
}
}
And in level1 I create static polygones that should be a ground with which player should contact.
- (void) drawStaticPolygons
{
GameObject *ground = [[GameObject alloc] init];
[ground setType:kGameObjectGround];
//1st polygon
b2Vec2 vertices1[4];
vertices1[0].Set(0, 1);
vertices1[1].Set(0, 0);
vertices1[2].Set(16, 0);
vertices1[3].Set(16, 1);
b2BodyDef myBodyDef1;
myBodyDef1.type = b2_staticBody;
myBodyDef1.userData = ground;
b2PolygonShape polygonShape1;
polygonShape1.Set(vertices1, 4);
b2FixtureDef myFixtureDef1;
myFixtureDef1.shape = &polygonShape1; //change the shape of the fixture
myBodyDef1.position.Set(0,0);
b2Body *staticBody1 = world->CreateBody(&myBodyDef1);
staticBody1->CreateFixture(&myFixtureDef1); //add a fixture to the body
//2nd polygon
....
//n polygon
}
The question is how to make the ContactListener know that my polygons are kGameObjectGround?

The problem I see, because i did not check other things, is that the polygon shape does have to be CCW and the vertices donĀ“t look like to be following that rule. Make the vertices in counter clockwise order and should works.

Related

How to add physicsBody to tiles generated by JSTileMap and change them into one physicsBody

I use JSTileMap to draw a level. I changed it a little bit to add SKPhysicsBody to every tile but sometimes when I apply some impulse to main character and he hits the wall/ground/ceiling he is acting weird. He reflects from surfaces with the way that is contrary to the principles of physics. I think that it happens because the player hits the point where two physics bodies (for example two physics bodies of the ground) connects.
SKPhysicsBody class provides a method to create one physics body from different physics bodies + (SKPhysicsBody *)bodyWithBodies:(NSArray *)bodies; Can I use this method to create one physics body from ale the tiles physics bodies?
Here's a method from JSTileMap where I add physics bodies:
+(id) layerWithTilesetInfo:(NSArray*)tilesets layerInfo:(TMXLayerInfo*)layerInfo mapInfo:(JSTileMap*)mapInfo
{
TMXLayer* layer = [TMXLayer node];
layer.map = mapInfo;
layer.tilesByColumnRow = [NSMutableDictionary dictionary];
// basic properties from layerInfo
layer.layerInfo = layerInfo;
layer.layerInfo.layer = layer;
layer.mapTileSize = mapInfo.tileSize;
layer.alpha = layerInfo.opacity;
layer.position = layerInfo.offset;
// recalc the offset if we are isometriic
if (mapInfo.orientation == OrientationStyle_Isometric)
{
layer.position = CGPointMake((layer.mapTileSize.width / 2.0) * (layer.position.x - layer.position.y),
(layer.mapTileSize.height / 2.0) * (-layer.position.x - layer.position.y));
}
NSMutableDictionary* layerNodes = [NSMutableDictionary dictionaryWithCapacity:tilesets.count];
//MY CODE
NSMutableArray *arrayOfSprites = [[NSMutableArray alloc] init];
SKNode *theSprite;
//----||----
// loop through the tiles
for (NSInteger col = 0; col < layerInfo.layerGridSize.width; col++)
{
for (NSInteger row = 0; row < layerInfo.layerGridSize.height; row++)
{
// get the gID
NSInteger gID = layerInfo.tiles[col + (NSInteger)(row * layerInfo.layerGridSize.width)];
// mask off the flip bits and remember their result.
bool flipX = (gID & kTileHorizontalFlag) != 0;
bool flipY = (gID & kTileVerticalFlag) != 0;
bool flipDiag = (gID & kTileDiagonalFlag) != 0;
gID = gID & kFlippedMask;
// skip 0 GIDs
if (!gID)
continue;
// get the tileset for the passed gID. This will allow us to support multiple tilesets!
TMXTilesetInfo* tilesetInfo = [mapInfo tilesetInfoForGid:gID];
[layer.tileInfo addObject:tilesetInfo];
if (tilesetInfo) // should never be nil?
{
SKTexture* texture = [tilesetInfo textureForGid:gID];
SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:texture];
sprite.name = [NSString stringWithFormat:#"%ld",(long)(col + row * layerInfo.layerGridSize.width)];
// make sure it's in the right position.
if (mapInfo.orientation == OrientationStyle_Isometric)
{
sprite.position = CGPointMake((layer.mapTileSize.width / 2.0) * (layerInfo.layerGridSize.width + col - row - 1),
(layer.mapTileSize.height / 2.0) * ((layerInfo.layerGridSize.height * 2 - col - row) - 2) );
}
else
{
sprite.position = CGPointMake(col * layer.mapTileSize.width + layer.mapTileSize.width/2.0,
(mapInfo.mapSize.height * (tilesetInfo.tileSize.height)) - ((row + 1) * layer.mapTileSize.height) + layer.mapTileSize.height/2.0);
}
// flip sprites if necessary
if(flipDiag)
{
if(flipX)
sprite.zRotation = -M_PI_2;
else if(flipY)
sprite.zRotation = M_PI_2;
}
else
{
if(flipY)
sprite.yScale *= -1;
if(flipX)
sprite.xScale *= -1;
}
// add sprite to correct node for this tileset
SKNode* layerNode = layerNodes[tilesetInfo.name];
if (!layerNode) {
layerNode = [[SKNode alloc] init];
layerNodes[tilesetInfo.name] = layerNode;
}
//adding physicsbody to every tile
//sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(sprite.frame.size.width, sprite.frame.size.height)];
//sprite.physicsBody.dynamic = NO;
//sprite.physicsBody.categoryBitMask = mapCategory;
//[arrayOfSprites addObject:sprite.physicsBody];
[layerNode addChild:sprite];
NSUInteger indexes[] = {col, row};
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2];
[layer.tilesByColumnRow setObject:sprite forKey:indexPath];
//MY CODE
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size];
sprite.physicsBody.dynamic = NO;
[arrayOfSprites addObject:sprite.physicsBody];
//-----||------
#ifdef DEBUG
// CGRect textRect = [texture textureRect];
// NSLog(#"atlasNum %2d (%2d,%2d), gid (%d,%d), rect (%f, %f, %f, %f) sprite.pos (%3.2f,%3.2f) flipx%2d flipy%2d flipDiag%2d", gID+1, row, col, [tilesetInfo rowFromGid:gID], [tilesetInfo colFromGid:gID], textRect.origin.x, textRect.origin.y, textRect.size.width, textRect.size.height, sprite.position.x, sprite.position.y, flipX, flipY, flipDiag);
#endif
}
}
}
//MY CODE
NSArray *array = [arrayOfSprites copy];
theSprite = [SKNode node];
theSprite.physicsBody = [SKPhysicsBody bodyWithBodies:array];
theSprite.physicsBody.dynamic = NO;
theSprite.position = CGPointMake(layer.position.x+16, layer.position.y+16);
[layer addChild:theSprite];
//-----||------
// add nodes for any tilesets that were used in this layer
for (SKNode* layerNode in layerNodes.allValues) {
if (layerNode.children.count > 0) {
[layer addChild:layerNode];
}
}
[layer calculateAccumulatedFrame];
return layer;
}
After adding physics bodies to every tile and adding those physics bodies to NSMutableArray I assign a copy of this NSMutableArray to the NSArray and try to create one physics body out of it like this:
//MY CODE
NSArray *array = [arrayOfSprites copy];
theSprite = [SKNode node];
theSprite.physicsBody = [SKPhysicsBody bodyWithBodies:array];
theSprite.physicsBody.dynamic = NO;
theSprite.position = CGPointMake(layer.position.x+16, layer.position.y+16);
[layer addChild:theSprite];
//-----||------
In result, one physics body with the height and width of one tile is added.
If you want to use [SKPhysicsBody bodyWithBodies:array], then you need to make sure that all the bodies in the array are relative to the parent node.
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size]; means that your physics body position is relative to the sprite node. You need the body relative to the parent node.
The only way I know how to do this is with center:
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size center:sprite.position];
This should place the SKPhysicsBody at the location of the sprite when it is added to the parent node.

Sprite Kit customFieldWithEvaluationBlock - block not called

I created a new SpriteKit iOS project and added this code to GameScene.m. The block to calculate the force field is never called, so node n1 never moves. Anyone know why? What am I missing?
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
// Create a red circle in the middle of scene
SKShapeNode *n1 = [SKShapeNode shapeNodeWithCircleOfRadius:20];
n1.fillColor = [UIColor redColor];
n1.position = CGPointMake(self.size.width / 2, self.size.height / 2);
n1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:20];
n1.physicsBody.affectedByGravity = NO;
n1.physicsBody.dynamic = YES;
[self addChild:n1];
// Try to add a custom force field to the scene
SKFieldNode *f1 = [SKFieldNode customFieldWithEvaluationBlock:^vector_float3(vector_float3 position, vector_float3 velocity, float mass, float charge, NSTimeInterval deltaTime) {
printf("block called\n");
vector_float3 res = { 1.0, 0.0, 0.0 };
return res;
}];
f1.enabled = YES;
[self addChild:f1];
}

How can i make a node collide with the sides of moving scene?

Hi i wish to know how can my nodes can collide with a moving scene. I have a main node that cans move along the x axis and cans collide with other nodes. I want to make the non main nodes collide with the sides of the moving scene. How can i make that ? ( the camera only follow the main node).
I have an obstacle class.
in my MLObstacle.m
-(id)init
{
if(self = [super init])
{
int aux = arc4random()%3;
switch (aux) {
case 0:
self = [MLObstaculo spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake((aux+1)*10,(aux+1)*13 )];
break;
case 1:
self = [MLObstaculo spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake((aux+1)*10,(aux+1)*15 )];
break;
case 2:
self = [MLObstaculo spriteNodeWithColor:[UIColor redColor] size:CGSizeMake((aux+1)*10,(aux+1)*14 )];
break;
}
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.affectedByGravity = NO;
self.physicsBody.categoryBitMask = obstacleCategory;
self.physicsBody.contactTestBitMask = visibleSidesCategory;
self.physicsBody.collisionBitMask = visibleSidesCategory;
[self setDx:0.0];
[self setDy:2.4];
}
return self;
}
So i add Obstacles to my WorldClass
In my WorlGen.m
-(void)GenerateWorld
{
SKSpriteNode *Ground = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(self.scene.frame.size.width,100)];
Ground.position = CGPointMake(0, -self.scene.frame.size.height/2 + Ground.size.height/2);
Ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Ground.size];
Ground.physicsBody.dynamic = NO;
[self addChild:Ground];
for(int i = 0; i < 3; i++)
{
MLObstaculo * auxobs= [[MLObstaculo alloc]initObstaculo];
auxobs.position = CGPointMake((i+1)*50, Ground.position.y + Ground.size.height/2 +auxobs.size.height/2 );
[self.Obstaculos addObject:auxobs];
[self addChild:auxobs];
auxobs = NULL;
}
}
and then i add the world to my scene
Scene.m
-(id)initWithSize:(CGSize)size
{
if(self = [super initWithSize:size]
{
WorldGen * world = [[WorldGen alloc] init];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.contactDelegate = self;
self.physicsBody.categoryBitMask = VisibleSidesCategory
self.physicsBody.collisionBitMask = ObstacleCategory;
self.anchorPoint = CGPointMake(0.5,0.5);
[self addChild:world];
[world GenerateWorld];
}
}
Update...
-(void)didSimulatePhysics
{
[self centerOnNode:hero];
}
-(void)centerOnNode:(SKNode *)node
{
CGPoint pointinScene = [self convertPoint:node.position fromNode:node.parent];
world.position = CGPointMake(world.position.x -pointinScene.x , world.position.y);
}
I initalized all the category variables with this format ( static const uint32_t ObstacleCategory = 0x1 <<1; )
First edit by defining your collisionBitMask
-(id)init
{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.categoryBitMask = ObstacleCategory;
self.physicsBody.contactTestBitMask = VisibleSidesCategory;
self.physicsBods.collisionBitMask = VisibleSidesCategory;
}
Second make this steps in Xcode -> File -> New -> File -> C and C++ -> Header File
Give a name of Physics.h and add those lines in it:
typedef enum : uint8_t {
ObstacleCategory = 1,
VisibleSidesCategory = 2,
} ColliderType;
and then import Physics.h into all your classes (You don't need to alloc or init for Physics.h, just use ObstacleCategory and VisibleSidesCategory)
From this and another question's problem, I have finally determined there is an error in the API. Specifically, a node's anchorPoint is considered (0,0) during initialization if using a WithRect: or FromRect: based initializer(but not a RectOfSize: or center: based one), so you get unexpected behavior.
Thus, even when the anchorPoint of the Scene and other objects are set to (0.5,0.5), a node places its lower left corner relative to the parent node's midpoint, rather than placing the child's midpoint relative to the parent node's midpoint.
Specifically, this can be solved in any one of these three ways:
Use an anchorPoint of (0,0).
Create your nodes without using Rect based initializers. You can use shapeNodeWithRectOfSize: instead of shapeNodeWithRect: and bodyWithEdgeLoopFromPath: instead of bodyWithEdgeLoopFromRect:
Set the Rect's initial position as lower and to the left, like so:
node.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(-node.size.width/2, -node.size.height/2, node.size.width, node.size.height)];

How to make balls ignore collisions with each other

With sprite kit, I'm trying to make series of balls drop from the air through physics simulation, and I want them to drop without colliding with each other and bounce off. I just want them to go straight down and pass through each other. How should I make this happen with collisionBitMask and categoryBitMask ?
// Common.h
#ifndef Rainy_Poops_common_h
#define Rainy_Poops_common_h
static int poopSize_x = 20;
static int poopSize_y = 20;
static const uint32_t poopCategory = 0x1 << 1;
#endif
// MyScene.h
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
_timer++;
if (_timer % 3 == 0) {
SKSpriteNode * p = [[Poop alloc] init];
NSInteger random_x = arc4random_uniform([[UIScreen mainScreen]applicationFrame].size.width);
p.position = CGPointMake(random_x, [[UIScreen mainScreen]applicationFrame].size.height - 5);
[self addChild: p];
[self enumerateChildNodesWithName:#"poop" usingBlock:
^(SKNode *node, BOOL *stop) {
Poop *poop = (Poop *) node;
if (!poop.isDropping) {
poop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(poopSize_x, poopSize_y)];
poop.isDropping = YES;
}
if (poop.position.y < 0) {
[poop removeFromParent];
}
}];
}
}
#import "Poop.h"
#implementation Poop
- (id)init {
self = [super initWithImageNamed:#"poop2.png"];
self.isDropping = NO;
self.name = #"poop";
self.size = CGSizeMake(poopSize_x, poopSize_y);
SKPhysicsBody *physicsPoop = self.physicsBody;
physicsPoop.collisionBitMask = 0;
physicsPoop.categoryBitMask = poopCategory;
physicsPoop.affectedByGravity = YES;
physicsPoop.mass = 100;
physicsPoop.allowsRotation = NO;
physicsPoop.dynamic = YES;
return self;
}
#end
I think your problem is related to this line -
poop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(poopSize_x,poopSize_y)];
You initialise your categoryBitMask to 2 and collisionBitMask to 0 in the init of your node, but this line will reset the physicsBody for your poop and assign the default collisionBitMask and categoryBitMask values of 0xFFFFFFFF
set collisionButMask equal to 0 for no collision or equal to the categoryBitMask of the sprite you would like the balls to collide with.

destroy box2d body in cocos2d

I have a web and some insects i am throwing web and having collision detection with insects and destroying them. i have used bod2d for collision detection.
World Method
-(void)createWorld
{
// Define the gravity vector.
b2Vec2 b_gravity;
b_gravity.Set(0.0f, -9.8f);
// Do we want to let bodies sleep?
// This will speed up the physics simulation
bool doSleep = true;
// Construct a world object, which will hold and simulate the rigid bodies.
world = new b2World(b_gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(true);
}
Create Web
-(void) createWeb
{
freeBodySprite = [CCSprite spriteWithFile:#"web1.png"];
[self addChild:freeBodySprite z:2 tag:TAG_WEB];
CGPoint startPos = CGPointMake(100, 320/1.25);
bodyDef.type = b2_staticBody;
bodyDef.position = [self toMeters:startPos];
bodyDef.userData = freeBodySprite;
float radiusInMeters = ((freeBodySprite.contentSize.width * freeBodySprite.scale/PTM_RATIO) * 1.0f);
shape.m_radius = radiusInMeters;
fixtureDef.shape = &shape;
fixtureDef.density = 0.01f;
fixtureDef.friction = 0.1f;
fixtureDef.restitution = 0.1f;
circularObstacleBody = world->CreateBody(&bodyDef);
stoneFixture = circularObstacleBody->CreateFixture(&fixtureDef);
freeBody = circularObstacleBody;
}
I have created my insect animation with spritesheet and then created b2body for that Sprite
-(b2Body *) createMovingBoxObstacle
{
//set this to avoid updating this object in the tick schedule
_ants.userData = (void *)YES;
b2BodyDef bodyDef_Ant;
bodyDef_Ant.type = b2_dynamicBody;
CGPoint startPos = ccp(520,winSize.height/7.6);
bodyDef_Ant.position = [self toMeters:startPos];
bodyDef_Ant.userData = _ants;
b2PolygonShape dynamicBox;
float tileWidth = ((_ants.contentSize.width * _ants.scale/PTM_RATIO) * 0.5f);
float tileHeight = ((_ants.contentSize.height * _ants.scale/PTM_RATIO) * 0.5f);
dynamicBox.SetAsBox(tileWidth, tileHeight);
b2FixtureDef fixtureDef_Ant;
fixtureDef_Ant.shape = &dynamicBox;
fixtureDef_Ant.friction = 0.7;
fixtureDef_Ant.density = 0.1f;
fixtureDef_Ant.restitution = 0.7;
staticBody = world->CreateBody(&bodyDef_Ant);
staticBody->CreateFixture(&fixtureDef_Ant);
VAMovingObstacle* moveableObject = [[VAMovingObstacle alloc] init];
moveableObject.startPoint = ccp(520,winSize.height/7.6);
moveableObject.endPoint = ccp(-50,winSize.height/7.6);
moveableObject.transitionTime = 20.0;
moveableObject.breakTime = 1.0;
moveableObject.obstacleSprite = _ants;
moveableObject.physicalBody = staticBody;
[moveableObject startMovement];
if (!movingObstacles) {
movingObstacles = [[NSMutableArray alloc] init];
}
[movingObstacles addObject:moveableObject];
[moveableObject release];
return staticBody;
}
VAMovingObstacle is a class which helps sprite along with b2body to move from left to right (runaction)
Here is my contact listner EndContact method
void ContactListener::EndContact(b2Contact* contact)
{
b2Body* bodyA = contact->GetFixtureA()->GetBody();
b2Body* bodyB = contact->GetFixtureB()->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();
if (spriteA.tag == TAG_WEB && spriteB.tag >= TAG_ANT) {
}
else if (spriteA.tag >= TAG_ANT && spriteB.tag == TAG_WEB) {
[spriteA removeFromParentAndCleanup:YES];
[[HelloWorldLayer sharedLevel] WebCollisionWithInsect:bodyA];
}
}
}
when ever collision happens EndContact method calls WebCollisionWithInsect:bodyA
-(void) WebCollisionWithInsect:(b2Body*) bodyT{
world->DestroyBody(bodyT);
}
On world->DestroyBody(bodyT) line it give an error
Assertion failed: (IsLocked() == false), function DestroyBody, file
/Users/libs/Box2D/Dynamics/b2World.cpp, line 134.
Any Idea what i am doing wrong. ?
Edited
I have added this line in EndContact
destroyCollisionDetectionBody = objBody;
didInsectCollideWithWeb = YES;
Keeping reference of body i want to destroy.
Now i can destroy my body inside tick method on if check
Added these lines in tick method
if(didInsectCollideWithWeb)
{
[self unschedule:#selector(tick:)];
[freeBodySprite removeFromParentAndCleanup:YES]; // this is my web
world->DestroyBody(destroyCollisionDetectionBody);
world->DestroyBody(freeBody);
[self createWeb];
[self schedule:#selector(tick:)];
didInsectCollideWithWeb = NO;
}
But now problem is that when i am destroying my web i.e freebody. i cant recreate it. as i was doing it before by calling [self createWeb];
You are trying to destroy a box2d object while its being used in the simulation phase (as EndContact is called during that phase). The simulation phase is the one executed when you call the world's step method.
So what you should do is keep a reference to the body object you want to remove and after the box2d simulation phase (after the step method returns) execute the WebCollisionWithInsect with the desired body

Resources