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..
Related
I have two SkSpriteNodes called left-ball and right-ball.THey flow from sides of the screen.
I implemented the collision detection for these two balls. However I noticed that before I add collision detection, the balls were moving smoothly without any shaking effect, but after I add the collision detection, the balls were shaking and moving in their path.
I am using the same bit category for both of these balls:
static const int ballHitCategory = 1;
without shaking:
with shaking:
Here is what I tried in code:
_leftBall.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:_leftBall.frame.size];
_leftBall.physicsBody.categoryBitMask=ballHitCategory;
_leftBall.physicsBody.contactTestBitMask=ballHitCategory;
_leftBall.physicsBody.collisionBitMask=ballHitCategory;
_leftBall.physicsBody.dynamic=YES;
_leftBall.physicsBody.usesPreciseCollisionDetection=YES;
_rightBall.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:_rightBall.frame.size];
_rightBall.physicsBody.categoryBitMask=ballHitCategory;
_rightBall.physicsBody.contactTestBitMask=ballHitCategory;
_rightBall.physicsBody.collisionBitMask=ballHitCategory;
_rightBall.physicsBody.dynamic=YES;
_rightBall.physicsBody.usesPreciseCollisionDetection=YES;
-(void)didBeginContact:(SKPhysicsContact *)contact
{
_leftBall = (SKSpriteNode*)contact.bodyA.node;
_rightBall = (SKSpriteNode *)contact.bodyB.node;
if(_leftBall.physicsBody .categoryBitMask == ballHitCategory || _rightBall.physicsBody.categoryBitMask == ballHitCategory)
{
NSLog(#" hit the ");
//setup your methods and other things here
[_leftBall removeFromParent];
[_rightBall removeFromParent];
}
}
Please note that shake happens only after I add the collision detection, If I remove the above code, everything is fine.
What is the proper way to add collision detection without effecting the skspritenode ?
so youre moving the sprites using non physics code.. but theyre still part of the physics simulation. I'm guessing theyre fighting against the force of gravity.
either set affectedByGravity on each sprite to false
or you can turn off gravity in your game completely
[self.physicsWorld setGravity:CGVectorMake(0, 0)];
Don't worry about the Gravity. You should set your spritenode:
_leftBall.physicsBody.dynamic=YES;
_leftBall.physicsBody.affectedByGravity=NO;
Same on _rightBall
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.
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;
So in my game I have Cocos2D + Box2D integrated. I know it is not recommended but I have my b2Bodys follow my CCSprites because I only need the collision part of Box2D. Anyway, using PhysicsEditor, I have created the most efficient body shapes I can and my game normally runs fine at 60FPS. My only issue is that, I want to prepare for the worst here. Lets say just under a really rare occurrence (like 1 in 10,000) the game lags for 5 seconds so that the FPS is around 10FPS. This would cause my main character to not collide with the platform under it. And in turn, it would make the character fall through the platform and the game would end unintentionally. So overall I just want to have a fallback plan when this happens.
So this is how I call the Box2D step:
world->Step(1/60.0f ,8, 8);
Then in my update method I do this to detect the collisions:
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);
}
}
}
So how would I adjust what I am doing now to deal with the possible scenario I explained above? Thanks!
You will not be having this problem because your Box2D step interval is fixed. Your simulation will simply slow down when the framerate decreases.
It might occur if the timestep were multiplied by the frame's delta time. But even then you ought to first verify that you'll be having these problems by forcing the game to run at 10 fps before making any code changes.
Box2D have the option to define objects as bullets. This is to handle that fast moving objects don't pass through solid objects.
The syntax is something like this:
bodyDef.bullet = true;
This makes Box2D do a sweep from the old to the new position, and would catch those obstacle that you did not hit due to low frame rates.
I see everyone saying that you add gravity like so in a Box2D world:
b2Vec2 gravity = b2Vec2(0.0f, -10.0f);
bool doSleep = false;
world = new b2World(gravity, doSleep);
The thing is though, what if I want gravity only on a specific b2Body which contains userData from a CCSprite? AFAIK this will apply gravity to everything in the world which I do not want, so can someone explain to me how I can apply this gravity only to a specific b2Body?
Thanks!
Edit1:
Can I just do this line,
_bottomBody->ApplyForce(gravity, _bottomBody->GetPosition());
Instead of the world = new b2World... etc... Wouldn't that work with gravity only on that b2Body?
Just apply a force/impulse to the specific b2Body every frame. It will emulate gravity.
// a procedure called every frame
void Application::on_update_world(double t)
{
m_body_with_custom_gravity->applyForce(CUSTOM_GRAVITY * m_body_with_custom_gravity->getMass());
m_phys_world->Step(t, VEL_ITERATIONS, POS_ITERATIONS);
}
A thread with a question closely related to your:
How to apply constant force on a Box2D body?