I have sprites(which have bodies) that can fall down into a box, in a b2world.
I wonder in what way should I define that box body, so only when another body hit it from upside, it looks like he went inside that box, and I don't see it anymore ..
should I just put it in another z layer?
how can I define a body to be open in its upside only ?
I have created the body like this:
-(void)basket //TAG5
{
//define sprite
basket=[CCSprite spriteWithFile:#"basket.png"];
basket.tag=5;
basket.position=ccp(200,50);
b2BodyDef spriteBodyDef;
spriteBodyDef.type = b2_staticBody;
spriteBodyDef.position.Set(basket.position.x/PTM_RATIO,basket.position.y/PTM_RATIO);
spriteBodyDef.userData = basket;
basket1Body = world->CreateBody(&spriteBodyDef);
b2PolygonShape spriteShape; //b2polygon-for box shape
spriteShape.SetAsBox(basket.contentSize.width/PTM_RATIO/2,basket.contentSize.height/PTM_RATIO/2); //for b2polygon
b2FixtureDef spriteShapeDef;
spriteShapeDef.shape = &spriteShape;
spriteShapeDef.density = 10.0;
spriteShapeDef.isSensor = false;
stand1Body->CreateFixture(&spriteShapeDef);
[self addChild:basket];
}
I am using cocos2d and box2d.
any explanation of a simple way of doing this will be appreciate .
To make the box you could use three polygons (left and right walls, and bottom), or edge shapes. Then yes, just draw something over the top so that the objects are hidden when they go into that area.
Related
I have a few layers, which holds CCSprites .
I have a main CCScene that is adding these layers at the start.
I have to check collision between sprites from different layers .
I know how to check a simple collision with CGRectContains , my problem is , that i need some kind of a class that holds all the sprites positions from all layers ,and to check each tick for collision.
Question: what is the right way to create such a class,that save/check sprites from all layers on the scene ?
THanks .
If you don't care about empty space, then you can just set self.contentSize for every layer. You can do it like this:
CGRect rect = CGRectNull;
for (CCNode *node in self.children) {
rect = CGRectUnion(rect, node.boundingBox);
}
self.contentSize = rect.size;
But if you care about empty space, then you need to use something like this:
// put it somewhere outside class implementation
static inline BOOL CCLayerIntersectsCCLayer(CCLayer l1, CCLayer l2) {
for (CCNode *n1 in l1.children) {
for (CCNode *n2 in l2.children) {
CGRect r1 = n1.boundingBox;
CGRect r2 = n2.boundingBox;
r1.origin = [l1.parent convertToNodeSpace:r1.origin];
r2.origin = [l2.parent convertToNodeSpace:r2.origin];
if (CGRectIntersectsCGRect(r1, r2)) {
return YES;
}
}
}
return NO;
}
But you must understand, that all sprites (even circles) are actually rectangles. So if you need very precise collision detection, then you should use box2d in your project. If you are interesting in this, here are some links to read:
Box2d
Intro to Box2D with Cocos2D 2.X Tutorial: Bouncing Balls
Box2D Tutorial for iOS: How To Use Box2D For Just Collision Detection with Cocos2D iPhone
Currently, in cocos2d, I have a an app that does the following:
Initiate with a Blank Screen.
When I tap the screen, I get a circle to pop-up. As I hold the circle, the circle will continue to grow at a constant rate. However, despite the fact that the sprite is growing, the box2d physical body isn't, which means that the sprite will not collide with other bodies. I been trying to figure out a way to change the radius that scales with the sprite but no such question exist here for cocos2d. I have noticed other box2d for things other than cocos2d but I am having a hard time translating them over.
//smile.position = ccp(touchLocation.x, touchLocation.y);
smile.scale = .05;
[self addChild:smile];
// b2BodyDef smileBodyDef;
smileBodyDef.type = b2_dynamicBody;
smileBodyDef.position.Set(touchLocation.x/PTM_RATIO, touchLocation.y/PTM_RATIO);
smileBodyDef.userData = smile;
smileBody = world->CreateBody(&smileBodyDef);
//Radius
b2CircleShape smileCircleShape;
int radius = 80;
//Fixture
smileFixtureDef.shape = &smileCircleShape;
smileFixtureDef.density = 0.00f;
smileFixtureDef.friction = .2f;
smileBody->CreateFixture(&smileFixtureDef);
if (CGRectContainsPoint(smileRect, touchLocation)) {
growForever = [CCRepeatForever actionWithAction: [CCScaleBy actionWithDuration: .5 scale: 1.2]];
[growForever setTag:1];
[smile runAction:growForever];
Each time you want to change your radius, grab the shape object associated with the b2Fixture that you created for your body, and then set the new value accordingly:
fixture->GetShape()->m_radius = new_radius/PTM_RATIO;
EDIT 2: Problem solved! I can´t promise it will work with different settings, but by putting my block´s body density to 0, the stack of blocks did not fall when new blocks are added.
I´m sorry about the poor title of the question, I´ll explain my problem closer here:
So, I´ve used Box2D and cocos2D to setup a simple project where two boxes stacks on top of each other (I´m planning to expand to 8-10 boxes). Right now, using a friction of 10.0f on each box, the box at the top still moves around a little. If I would add more boxes, the "tower" would fall and I don´t want that.
I want the boxes to use the gravity to move down, but I never ever want them to change there start x-value.
So, how could I prevent my tower of boxes to fall over or prevent my boxes from moving in x-direction?
EDIT: Posting some code
This code creates on of the boxes, the other one just have a different sprite file.
CCSprite *block = [CCSprite spriteWithFile:#"red.png"];
block.position = ccp(200,380);
[self addChild:block];
//Body definition
b2BodyDef blockDef;
blockDef.type = b2_dynamicBody;
blockDef.position.Set(200/PTM_RATIO, 200/PTM_RATIO);
blockDef.userData = block;
b2Body *blockBody = _world->CreateBody(&blockDef);
//Create the shape
b2PolygonShape blockShape;
blockShape.SetAsBox(block.contentSize.width/PTM_RATIO/2, block.contentSize.height/PTM_RATIO/2);
//Fixture defintion
b2FixtureDef blockFixtureDef;
blockFixtureDef.shape = &blockShape;
blockFixtureDef.restitution = 0.0f;
blockFixtureDef.density = 10.0f;
blockFixtureDef.friction = 10.0f;
_redBlockFixture = blockBody->CreateFixture(&blockFixtureDef);
Nothing fancy.
Regards.
You could setup a 2 (1 pixel wide) walls in box2D to the left and right of the block. Here's some sample code for the left wall. To create the right wall, just copy and past the code and change the variable names and the position of the BodyDef.
// Constant you'll need to define
float wallHeight;
// Create wall body
b2BodyDef wallBodyDef;
wallBodyDef.type = b2_dynamicBody;
wallBodyDef.position.Set(200 - block.contentSize.width/PTM_RATIO/2, 0);
b2Body *wallBody = _world->CreateBody(&wallBodyDef);
// Create wall shape
b2PolygonShape wallShape;
wallShape.SetAsBox(1, wallHeight);
// Create shape definition and add to body
b2FixtureDef wallShapeDef;
wallShapeDef.shape = &wallShape;
wallShapeDef.density = 100.0f;
wallShapeDef.friction = 0.0f;
wallShapeDef.restitution = 0.0f;
b2Fixture *wallFixture = wallBody->CreateFixture(&wallShapeDef);
I solved this problem by adjusting the restitution (bounce) of the static surface upon which the blocks are stacked. For example, if the floor has a restitution of .2, a stack of five blocks will look like they are compressing into each other, and eventually topple:
Set the restitution of the floor to 0, and the blocks stay stacked the way you would expect:
Anybody knows how to add 2 circle fixture to one b2body with desired positioning?
I know how to add two polygon fixture to one body by using m_centroid. But how can I do it for circle fixtures.
Any answer will be appreciated. I want to stick some object together. I tried joints but they all are elastic. I want distance static.
Thanks everyone!
You should create two fixtures for your body and the shapes of those fixtures should be b2CircleShape
//Create a body. You'll need a b2BodyDef, but I've assumed you know how to use these since you say you've created bodies successfully before.
b2Body* body = world->CreateBody(&bodyDef);
//Create the first circle shape. It's offset from the center of the body by -2, 0.
b2CircleShape circleShape1;
circleShape1.m_radius = 0.5f;
circleShape1.m_p.Set(-2.0f, 0.0f);
b2FixtureDef circle1FixtureDef;
circle1FixtureDef.shape = &circleShape1;
circle1FixtureDef.density = 1.0f;
//Create the second circle shape. It's offset from the center of the body by 2, 0.
b2CircleShape circleShape2;
circleShape2.m_radius = 0.5f;
circleShape2.m_p.Set(2.0f, 0.0f);
b2FixtureDef circle2FixtureDef;
circle2FixtureDef.shape = &circleShape2;
circle2FixtureDef.density = 1.0f;
//Attach both of these fixtures to the body.
body->CreateFixture(&circle1FixtureDef);
body->CreateFixture(&circle2FixtureDef);
I'm having a lot of trouble detecting collisions in a zero-G space game. Hopefully this image will help me explain:
http://i.stack.imgur.com/f7AHO.png
The white rectangle is a static body with a b2PolygonShape fixture attached, as such:
// Create the line physics body definition
b2BodyDef wallBodyDef;
wallBodyDef.position.Set(0.0f, 0.0f);
// Create the line physics body in the physics world
wallBodyDef.type = b2_staticBody; // Set as a static body
m_Body = world->CreateBody(&wallBodyDef);
// Create the vertex array which will be used to make the physics shape
b2Vec2 vertices[4];
vertices[0].Set(m_Point1.x, m_Point1.y); // Point 1
vertices[1].Set(m_Point1.x + (sin(angle - 90*(float)DEG_TO_RAD)*m_Thickness), m_Point1.y - (cos(angle - 90*(float)DEG_TO_RAD)*m_Thickness)); // Point 2
vertices[2].Set(m_Point2.x + (sin(angle - 90*(float)DEG_TO_RAD)*m_Thickness), m_Point2.y - (cos(angle - 90*(float)DEG_TO_RAD)*m_Thickness)); // Point 3
vertices[3].Set(m_Point2.x, m_Point2.y); // Point 3
int32 count = 4; // Vertex count
b2PolygonShape wallShape; // Create the line physics shape
wallShape.Set(vertices, count); // Set the physics shape using the vertex array above
// Define the dynamic body fixture
b2FixtureDef fixtureDef;
fixtureDef.shape = &wallShape; // Set the line shape
fixtureDef.density = 0.0f; // Set the density
fixtureDef.friction = 0.0f; // Set the friction
fixtureDef.restitution = 0.5f; // Set the restitution
// Add the shape to the body
m_Fixture = m_Body->CreateFixture(&fixtureDef);
m_Fixture->SetUserData("Wall");[/code]
You'll have to trust me that that makes the shape in the image. The physics simulation works perfectly, the player (small triangle) collides with the body with pixel perfect precision. However, I come to a problem when I try to determine when a collision takes place so I can remove health and what-not. The code I am using for this is as follows:
/*------ Check for collisions ------*/
if (m_Physics->GetWorld()->GetContactCount() > 0)
{
if (m_Physics->GetWorld()->GetContactList()->GetFixtureA()->GetUserData() == "Player" &&
m_Physics->GetWorld()->GetContactList()->GetFixtureB()->GetUserData() == "Wall")
{
m_Player->CollideWall();
}
}
I'm aware there are probably better ways to do collisions, but I'm just a beginner and haven't found anywhere that explains how to do listeners and callbacks well enough for me to understand. The problem I have is that GetContactCount() shows a contact whenever the player body enters the purple box above. Obviously there is a rectangular bounding box being created that encompasses the white rectangle.
I've tried making the fixture an EdgeShape, and the same thing happens. Does anyone have any idea what is going on here? I'd really like to get collision nailed so I can move on to other things. Thank you very much for any help.
The bounding box is an AABB (axis aligned bounding box) which means it will always be aligned with the the Cartesian axes. AABBs are normally used for broadphase collision detection because it's a relatively simple (and inexpensive) computation.
You need to make sure that you're testing against the OBB (oriented bounding box) for the objects if you want more accurate (but not pixel perfect, as Micah pointed out) results.
Also, I agree with Micah's answer that you will most likely need a more general system for handling collisions. Even if you only ever have just walls and the player, there's no guarantee that which object will be A and which will be B. And as you add other object types, this will quickly unravel.
Creating the contact listener isn't terribly difficult, from the docs (added to attempt to handle your situation):
class MyContactListener:public b2ContactListener
{
private:
PlayerClass *m_Player;
public:
MyContactListener(PlayerClass *player) : m_Player(player)
{ }
void BeginContact(b2Contact* contact)
{ /* handle begin event */ }
void EndContact(b2Contact* contact)
{
if (contact->GetFixtureA()->GetUserData() == m_Player
|| contact->GetFixtureB()->GetUserData() == m_Player)
{
m_Player->CollideWall();
}
}
/* we're not interested in these for the time being */
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{ /* handle pre-solve event */ }
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{ /* handle post-solve event */ }
};
This requires you to assign m_Player to the player's fixture's user data field. Then you can use the contact listener like so:
m_Physics->GetWorld()->SetContactListener(new MyContactListener(m_Player));
How do you know GetFixtureA is the player and B is the wall? Could it be reversed? Could there be an FixtureC? I would think you would need a more generic solution.
I've used a similar graphics framework (Qt) and it had something so you could grab any two objects and call something like 'hasCollided' which would return a bool. You could get away with not using a callback and just call it in the drawScene() or check it periodically.
In Box2D the existence of a contact just means that the AABBs of two fixtures overlaps. It does not necessarily mean that the shapes of the fixtures themselves are touching.
You can use the IsTouching() function of a contact to check if the shapes are actually touching, but the preferred way to deal with collisions is to use the callback feature to have the engine tell you whenever two fixtures start/stop touching. Using callbacks is much more efficient and easier to manage in the long run, though it may be a little more effort to set up initially and there are a few things to be careful about - see here for an example: http://www.iforce2d.net/b2dtut/collision-callbacks