Is it possible to deactivate collisions in physics bodies in spriteKit? - ios

I'm looking at doing the best way to collect items with my hero in my spriteKit game for iOs, and after to try a few ways to do it, my conclusion is the best way would be to have an item with a physic body which can detect collisions but don't collide with my hero. Is it possible to do it? to deactivate collisions of a physic body without deactivating its capabilities to detect collisions?? Sounds a bit contradictory I know... Because, the other way would be to create only a SKSpriteNode without physic body, then there wouldn't being collisions, but the way to "detect" collisions would be hand made and much more harder, because i would need to set a coordinate system detection in my hero, that when he will be in those specifics coordinates (over the item) then i'll make the item disappears. Any idea of how to do any of the two ways easier?

Check out collisionBitMask, categoryBitMask, and contactTestBitMask in the SKPhysicsBody class.
Essentially, physics bodies with the same collisionBitMask value will "pass-through" each other.
Correction: If the category and collision bits match, they will interact. If they do not match, those two will not interact. And if the collision bits, and category bits, are both zero, of course that item will interact with nothing whatsoever.
Then, you set the categoryBitMask and contactTestBitMask values to create an SKPhysicsContact Object on contact. Finally, your Class should adopt the SKPhysicsContactDelegate protocol. Use the - didBeginContact: method to detect and handle the SKPhysicsContact object.
static const uint8_t heroCategory = 1;
static const uint8_t foodCategory = 2;
--
food.physicsBody.categoryBitMask = foodCategory;
food.physicsBody.contactTestBitMask = heroCategory;
food.physicsBody.collisionBitMask = 0;
--
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.contactTestBitMask = foodCategory;
hero.physicsBody.collisionBitMask = 0;
--
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody = contact.bodyA;
SKPhysicsBody *secondBody = contact.bodyB;
}

Short answer:
yourHero.physicsBody.collisionBitMask = 0;
The default value of collisionBitMask is 0xFFFFFFFF (all bits set), that's why the node collides with others

you can do this by setting the categoryBitMask and contactBitMasks of the player and the item objects, but making sure that you do not set the collisionBitMask for either to interact with each other (see below)
static const int playerCategory = 1;
static const int worldCategory = 2;
static const int objectCategory = 4;
....
SKSpriteNode *player, *item;
....
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.collisionBitMask = worldCategory;
player.physicsBody.contactTestBitMask = worldCategory;
....
item.physicsBody.categoryBitMask = objectCategory;
item.physicsBody.contactTestBitMask = playerCategory | worldCategory;
item.physicsBody.collisionBitMask = worldCategory;
this way the physics body will pick up collisions between the player and world objects, the item and world objects, but not between the player and items. It will trigger a call to didBeginContact, where you can delete your item node, add health, etc.
Hope this helps!

contactTestBitMask is used to trigger didBeginContact. collisionBitMask is used to activate physics on nodes.
// add a physics body
ship.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ship.size.width/2];
// set the category for ship
ship.physicsBody.categoryBitMask = shipCategory;
// detect collisions with asteroids and edges
ship.physicsBody.contactTestBitMask = asteroidCategory | edgeCategory;
// physically collide with asteroids
ship.physicsBody.collisionBitMask = asteroidCategory;

Related

iOS Collision Detection

I have tried and tried to get collision detection to work the way I need but to no avail. What I want to accomplish is to get notified when the character touches an object but not stop it from moving on to the object. In GameScene, I have the physics of the object set like this:
Category Mask: 8
Collision Mask: 0
Field Mask: 0
Contact Mask: 0
I add the character to the scene in my code. I have a category structure like this:
struct PhysicsCategory {
static let None: UInt32 = 0
static let Player: UInt32 = 0b1 // 1
static let Pillar:UInt32 = 0b10// 2
static let Chest:UInt32 = 0b100// 4
static let Ladder:UInt32 = 0b1000
}
And the character physics is like this:
playerWalk.physicsBody?.categoryBitMask=PhysicsCategory.Player
playerWalk.physicsBody?.collisionBitMask=PhysicsCategory.Pillar
playerWalk.physicsBody?.contactTestBitMask=PhysicsCategory.Chest | PhysicsCategory.Ladder
playerWalk.physicsBody = SKPhysicsBody(rectangleOf:
playerWalk.frame.size)
For some reason the character stops at the ladder instead of moving on top of it. I can't figure out what I'm doing wrong.
Edit - You've set the properties of the playerWalk physics body before you've actually created the playerWalk.physicsBody. So the values for the categoryBitMask, collisionBitMask and contactTestBitMask don't get set because you've used optional chaining and so playerWalk.physicsBody? resolved to nil and nothing gets set.
Then, when you create the playerWalk physic body it just has the defaults properties i.e. no category, no contacts and collides with everything, so it is colliding with your ladder and thus cannot move over it.
Move the line:
playerWalk.physicsBody = SKPhysicsBody(rectangleOf: playerWalk.frame.size)
to before the lines where you set the physics body's properties.

Making physics bodies responsive to fields but not to other physics bodies

In my game I have a body that flies across the screen, and is supposed to be affected by a radial gravity field that I have created. To do this, I needed to turn the
body.dynamic = true
But this results in the body colliding with other objects (with physics bodies) that are on the screen, when I just want it to be affected by the field and not by these objects. If I turn the
body.dynamic = false
then it doesn't get affected by the field.
What should I do to make the body stay on its path?
EDIT This is my code for the main body that flies across the screen, after a node has already been created
body.physicsBody = SKPhysicsBody(rectangleOfSize: rectangle)
body.physicsBody?.dynamic = true
body.physicsBody?.categoryBitMask = PhysicsCategory.body
body.physicsBody?.contactTestBitMask = PhysicsCategory.otherBody
body.physicsBody?.affectedByGravity = false
body.physicsBody?.fieldBitMask = PhysicsCategory.shield
body.physicsBody?.allowsRotation = false
The image shows the 2 possible ways I want the body to avoid colliding with the object. Either it can pass through it, or it can pass around it and return to the earlier path. How do I do that?
You should set
body.physicsBody?.collisionBitMask = 0
collisionBitMask - A mask that defines which categories of physics bodies can collide with this physics body. IF the value is not set your PhysicsBody collides with every other PhysicsBodies, If value is set to 0 your PhysicsBody do not collide with other PhysicsBodies
I you set collisionBitMask = 0 your body node will go through all other nodes, if you whant it to collide you need to set node physicsBody?.collisionBitMask (for example if you whant your node to collide only with ball and wall physicsBody?.collisionBitMask = kCategoryWall | kCategoryBall)
I hope it was helpfull

cocos2dx - Bypass body contact

I am using cocos2dx and setup two Sprites with same Physics Body definitions see below
PhysicsBody *physicsBody=PhysicsBody::createBox(size,PhysicsMaterial(1.0f,0.0,0.5f),Vec2(0, size.height*0.5));
physicsBody->setGravityEnable(true);
physicsBody->setCategoryBitmask(1);
physicsBody->setCollisionBitmask(1);
physicsBody->setContactTestBitmask(true);
physicsBody->setDynamic(true);
physicsBody->setContactTestBitmask(0xFFFFFFFF);
physicsBody->setMass(5.0f);
node->setPhysicsBody(physicsBody);
I am defining a collision detection between both the physics bodies
auto contactListener=EventListenerPhysicsContactWithBodies::create(player1->getPhysicsBody(), player2->getPhysicsBody());
contactListener->onContactBegin = CC_CALLBACK_1(Game::onPlayerContact, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
I don't want these two bodies to collide with each other or, any effect of physics if they collide. I am doing
bool Game::onPlayerContact(cocos2d::PhysicsContact &contact){
auto nodeA = contact.getShapeA()->getBody()->getNode();
auto nodeB = contact.getShapeB()->getBody()->getNode();
if(nodeA != NULL && nodeB != NULL){
printf("\nPlayers collided");
}
return false;
}
I am not able to bypass the contact between two bodies, what else should be done to avoid collision?
Please Help.
Thanks.
Collision filtering allows you to prevent collision between shapes. Cocos2d-x supports collision filtering using category and groups bitmasks.
Cocos2d-x supports 32 collision categories. For each shape you can specify which category it belongs to. You also specify what other categories this shape can collide with. This is done with masking bits. For example:
auto sprite1 = addSpriteAtPosition(Vec2(s_centre.x - 150,s_centre.y));
sprite1->getPhysicsBody()->setCategoryBitmask(0x02); // 0010
sprite1->getPhysicsBody()->setCollisionBitmask(0x01); // 0001
auto sprite3 = addSpriteAtPosition(Vec2(s_centre.x + 150,s_centre.y + 100),2);
sprite3->getPhysicsBody()->setCategoryBitmask(0x03); // 0011
sprite3->getPhysicsBody()->setCollisionBitmask(0x03); // 0011
then in your collision callback. Do this.
if ((shapeA->getCategoryBitmask() & shapeB->getCollisionBitmask()) == 0
|| (shapeB->getCategoryBitmask() & shapeA->getCollisionBitmask()) == 0)
{
// shapes can't collide
ret = false;
}
source : http://www.cocos2d-x.org/wiki/Physics
You may want to check the Queries Under the Physics
http://www.cocos2d-x.org/wiki/Physics
Perhaps the Point Query..
Basically the Box2d has sensors which are used to detect contact and not any collision...
i recommend using Box2d over Cocos2d Physics...It is much Better..

If Statement to stop character falling through ground

I'm making a platform and want to stop the character (a ball) from going through the ground.
Can I write an if statement similar to if (Ball.center.y == 463) Ball.center.y = 463;?
463 is the y position that the ground is in the game.
While that approach is certainly possible, typically you want to abstract away the details (such as the 463 y-position of the ground) so that your code is more robust. For example, if you changed the y-position of the ground, you would have to change the 463 value everywhere you use it!
But fundamentally, yes you would use an if statement somewhat like what you provided. One thing to note is that if the ground is at 463, your ball will be half-way through the ground (since you are looking at the y-position of the centre of the ball.
Moreso, you want a check that is not so absolute... what if the position of the ball somehow becomes lower than the ground? Say 462? What should the behavior be now?
Without getting into the physics and design of your program, you would at the very least want to change your statement to something like:
int ball_lower_bound = Ball.center.y - Ball.height/2;
int ground_bound = 463;
if (ball_lower_bound < ground_bound) {
ball_lower_bound = ground_bound;
}
Since you are using sprite kit you should use physics body to handle that.
This is in swift:
variables of your class:
let balCategory:UInt32 = 1 << 0
let groundCategory:UInt32 = 1 << 1
Then when you define your ball sprite:
ball.physicsBody.categoryBitMask = ballCategory
ball.physicsBody.collisionBitMask = groundCategory
ball.physicsBody.contactTestBitMask = groundCategory
Then when you define your ground sprite (hopefully you have a image for your ground, if not make a very thin transparent or have something just below the screens edge):
ground.physicsBody.categoryBitMask = groundCategory
ground.physicsBody.collisionBitMask = ballCategory
ground.physicsBody.contactTestBitMask = ballCategory
Hopefully you can translate it to objective C if you are using that.

sprite kit collisions: ignore transparency?

I am building a game with Xcode's spritekit. Its a platform game and right now it functions well with flat ground pieces. I was wondering if it was possible to ignore transparency in the png for collisions. That is to say, If i have a ground piece with a curved floor and transparency filling the troughs, can i make the player walk the curves instead of a square bounding box covering the whole thing? The only example i can find is in the Gamemaker GML language, you can do "precise" collisions such that blank space in the images do not count as part of the sprite. I can supply code if necessary but this seems like more of a conceptual question. Thanks in advance
Hey there's a easy solution for this provided in the Apple Documentation.
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
sprite.physicsBody = [SKPhysicsBody bodyWithTexture:sprite.texture size:sprite.texture.size];
This creates a physics body around the physical paths of the texture.
Simulating Physics - SpriteKit Programming Guide
It's all in how you instantiate the physicsBody of the node in question.
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node.size];
is probably the easiest and most common way of making a physicsBody, but will also create the issue you've identified above, because, for all collision purposes, the node is a rectangle.
take a look at the documentation for SKPhysicsBody to see the other options available to you. PolygonFromPath and BodyWithBodies are probably the best suited for what you're doing.
SKPhysicsBody is the right move, just wanted to note that it does make sense to have a separate (simplified) mask-image for SKPhysicsBody to improve your performance, simplified from color and geometry standpoint, and here's the code:
let birdMask: UInt32 = 0x1 << 0
let pipeMask: UInt32 = 0x1 << 1
//...
pipeImage = SKSpriteNode(imageNamed: "realImage")
//... size and position
let maskTexture = SKSpriteNode(imageNamed: mask)
maskTexture.size = pipeImage!.size // size of texture w/ real imageNamed
pipeImage!.physicsBody?.usesPreciseCollisionDetection = true
pipeImage!.physicsBody = SKPhysicsBody(texture: maskTexture.texture!, size: size)
pipeImage!.physicsBody?.affectedByGravity = false // disable falling down...
pipeImage!.physicsBody?.allowsRotation = false
pipeImage!.physicsBody?.isDynamic = true
pipeImage!.physicsBody?.friction = 0
pipeImage!.physicsBody?.categoryBitMask = pipeMask
pipeImage!.physicsBody?.collisionBitMask = birdMask | pipeMask
pipeImage!.physicsBody?.contactTestBitMask = birdMask | pipeMask
and more detailed example/guide.

Resources