I am creating an app in which i am trying to Join two bodies in such away that when i move one body, the second body should be moved within the first body.
Here's my code to create a body:
- (b2Body *)addBoxBodyForDynamicSprite:(CCSprite *)sprite {
b2BodyDef spriteBodyDef;
spriteBodyDef.type = b2_dynamicBody;
//spriteBodyDef.position.Set(sprite.position.x/PTM_RATIO, sprite.position.y/PTM_RATIO);
CGPoint asolutePoint = [sprite.parent convertToWorldSpace:sprite.position];
spriteBodyDef.position.Set(asolutePoint.x/PTM_RATIO, asolutePoint.y/PTM_RATIO);
spriteBodyDef.userData = (__bridge void*)sprite;
b2Body *spriteBody = world->CreateBody(&spriteBodyDef);
b2PolygonShape spriteShape;
spriteShape.SetAsBox(sprite.contentSize.width/PTM_RATIO/2,
sprite.contentSize.height/PTM_RATIO/2);
b2FixtureDef spriteShapeDef;
spriteShapeDef.shape = &spriteShape;
spriteShapeDef.density = 0.3;
spriteShapeDef.isSensor = true;
spriteBody->CreateFixture(&spriteShapeDef);
return spriteBody;
}
One body is kinematic and other body is Dynamic. I am moving these bodies by using:
theBody->SetTransform(locationWorld, theBody->GetAngle());
If i apply linear force here, the bodies are not moving & the Joint which i used to fix them is b2WeldJoint.
b2JointDef jointDef;
jointDef.bodyA = another;
jointDef.bodyB = leftHandFixBody;
aJoint = (b2Joint *)world->CreateJoint(&jointDef);
It moves the dynamic body but the kinematic body remains on its position. I want to move both the bodies together. Any help will be really appreciated. Thanks!
Also, according to the manual, kinematic bodies are moved by setting their velocity, not by applying forces.
A kinematic body moves under simulation according to its velocity.
Kinematic bodies do not respond to forces. They can be moved manually
by the user, but normally a kinematic body is moved by setting its
velocity. A kinematic body behaves as if it has infinite mass,
however, Box2D stores zero for the mass and the inverse mass.
Kinematic bodies do not collide with other kinematic or static bodies.
Also, I have found that using SetTransform(...) to move bodies is less than effective. I created a portal with it to jump a body from one place to another, and that worked. But if I updated it every simulation cycle, the body stopped colliding with other bodies. This is just a word of caution.
Was this helpful?
Related
I have set the gravity of my world as follows
world=new b2World(b2Vec2(0.0,9.8));
I have a dynamic body called b2Body* sta1 which is falling from the top of the screen. And I have another dynamic body sta2 which should not fall i.e it just move on some keypress. The problem is how can I give this body of my world zero gravity i.e is there any way to give different gravity values to different bodies in the world?
yes you are right. You should set the gravity scale of the body defition.
sta2def.gravityScale = 0.0f;
i'm losing myself in this...
Situation:
Working on a game in cocos2d with box2d and I have a ropejoint between one fixed body and one dynamic body.
When I drop the dynamic body is swings from left to right and then from right to left due to the gravity in the world.
The problem:
The swings are getting shorter and shorter till finally the dynamic body hangs still beneath the fixed body. This is normal behavior but I need it to keep swinging.
My thoughts:
I think I need to apply a tangential force to the ropejoint in the direction of the swinging but how to do this is a mystery for now :)
Try setting the damping factor of the rope joint to zero
ropeJointDef.dampingRatio = 0.0f;
Hope it helps!
Here is a little code that should help you with your little problem
bool YourClass::init(){
CCCallFunc *swingL = CCCallFunc::create(this,callfunc_selector(YourClass::swingLeft));
CCDelayTime *delay = CCDelayTime::create(5);
CCCallFunc *swingR = CCCallFunc::create(this, callfunc_selector(YourClass::swingRight));
this->runAction(CCRepeatForever::create(CCSequence::create(swingL,delay,swingR,NULL)));
}
void YourClass::swingLeft(){
b2Body *dynamicBody = get your body from b2world;
dynamicBody->SetLinearVelocity(b2Vec2(-10, 0));//set velocity of the swing
}
void YourClass::swingRight(){
b2Body *dynamicBody = get your body from b2world;
dynamicBody->SetLinearVelocity(b2Vec2(10, 0));//set velocity of the swing
}
I've looked around quite a bit and haven't been able to find a concise answer to this question. For my Cocos2D game I have integrated the Chipmunk physics engine. On initialization, I setup the boundaries of the 'playing field' by establishing a series of bodies and static shapes as follows:
- (void)initPlayingField {
// Physics parameters
CGSize fieldSize = _model.fieldSize;
CGFloat radius = 2.0;
CGFloat elasticity = 0.3;
CGFloat friction = 1.0;
// Bottom
CGPoint lowerLeft = ccp(0, 0);
CGPoint lowerRight = ccp(fieldSize.width, 0);
[self addStaticBodyToSpace:lowerLeft finish:lowerRight radius:radius elasticity:elasticity friction:friction];
// Left
CGPoint topLeft = ccp(0, fieldSize.height);
[self addStaticBodyToSpace:lowerLeft finish:topLeft radius:radius elasticity:elasticity friction:friction];
// Right
CGPoint topRight = ccp(fieldSize.width, fieldSize.height);
[self addStaticBodyToSpace:lowerRight finish:topRight radius:radius elasticity:elasticity friction:friction];
// Top
[self addStaticBodyToSpace:topLeft finish:topRight radius:radius elasticity:elasticity friction:friction];
}
This setup works, except for one thing-- the positions of the boundaries appear to be off by a significant amount of pixels to the right. After initializing a player, and sending him towards the boundary walls, it does not bounce off the edges of the screen (as I would expect by setting up the bounding box at 0,0), but rather some number of pixels (maybe 30-50) inwards from the edges of the screen. What gives?
I initially thought the issue came from where I was rendering the player sprite, but after running it through the debugger, everything looks good. Are there special protocols to follow when using chipmunk's positions to render sprites? Do I need to multiply everything by some sort of PIXELS_PER_METER? Would it matter if they are in a sprite sheet? A sprite batch node? Here is how I set the sprite position in my view:
Player *player = [[RootModel sharedModel] player];
self.playerSprite.position = player.position;
self.playerSprite.rotation = player.rotation;
And here is how I set the position in my Model:
- (void)update:(CGFloat)dt {
self.position = self.body->p;
self.rotation = self.body->a;
}
(Yes, I know it's redundant to store the position because chipmunk does that inherently.)
Also, bonus question for for those who finished reading the post. How might you move a body at a constant velocity in whatever direction it is facing, only allowing it to turn left or right at any given time, but never slow down?
What's the shape of the physics body? Perhaps you simply forgot to consider making the shape the same size of the sprite. If you only consider the position, then the sprite will be able to move halfway outside the screen at any border because its position is at the center of the sprite.
To move a body at constant velocity, set its velocity while disabling friction (set to 0?) and allowing it to rebound off of collisions with its impact speed (in Box2D that would be restitution = 1, don't know about Chipmunk).
Have you tried debug drawing the collision shapes? Cocos2D 2.1 has my CCPhysicsDebugNode class in it that makes it pretty easy to add. That will let you know if your geometry is lined up with your graphics. How thick are your boundary shapes? Are you possibly adding the sprites to a parent node that might be offsetting them?
Chipmunk doesn't require you to tune it for any real units. You can just use pixels or whatever.
Lastly, #LearnCocos2D has the right idea about the constant velocity. Set friction to 0 and elasticity to 1.0. You will also probably want to check it's velocity every frame and accelerate it back towards it's normal velocity each frame. Colliding with non-static geometry will cause it to lose speed.
For the past days, I've been trying to make a ping pong like game. I have 2 paddles and a ball. All dynamic sprites. Everything's been working well except for one issue I'm having. The ball tends to bounce on the same angle at some point. So there would be times when the player can simply move the paddle on a specific part and the game can go on for a while or might be forever, since the ball doesn't change its angular velocity regardless of which part of the paddle it hits. I'm using a combination of linear and angular velocity to keep the ball moving like so:
if(_isPaused == FALSE)
{
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
if(sprite.tag == 2)
{
b2Vec2 dir = b->GetLinearVelocity();
dir.Normalize();
float currentSpeed = dir.Length();
int maxSpeed = 60;
float accelerate = vel;
if(currentSpeed <= maxSpeed)
{
b->SetLinearVelocity(accelerate * dir);
}
sprite.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
sprite.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
//Keep sprite from bouncing in a straight angle
b->SetAngularVelocity(_body->GetAngle());
}}}
So my question is, how can I manipulate the angular velocity to keep the ball bouncing on different angles everytime it collides with my paddle? I'm thinking something like getting the current angular velocity then multiplying it with some value but I'm not sure if that's the right way to approach the issue I'm having. Any suggestions or ideas would be greatly appreciated. Thanks in advanced.
The way I see it, you have two options:
Check the location of a collision. If it's close to the top/bottom edge of the paddle, deflect the outgoing velocity by an angular amount proportional to the surface "curvature" at that point. Of course, this is cheating, but if the artwork and code are in agreement, it looks correct. And graphics is "the art of cheating without getting caught".
You could take into account the current velocity of the paddle as well as that of the ball. Eg: if the ball is moving downwards and to the right, and the paddle is moving down, then you can compute the outgoing direction using conservation of linear momentum. Just make sure you restrict the paddle's change in momentum along the horizontal axis to be zero.
Finally, you could combine the above techniques, but now you'd have to use accurate collision detection (not the hack I described in (1) above).
Hope that helps!
A few pointers, you should use SetLinearVelocity() and SetAngularVelocity() rarely. Each alters a property of the body definition, which could make you run into problems later on when things get more complex. It would be better to use ApplyForceToCenter() or ApplyLinearImpulse() in the place of SetLinearVelocity() as these two are much more versatile functions and are a bit more coder-friendly. In my opinion, I don't think you should use b->SetAngularVelocity(_body->GetAngle()); If you wanted to change the angular velocity each time it collided, you could, in your beginContact method, write that every time the body collides with the paddle body, a random angular impulse is applied to the ball, using ApplyAngularImpulse().Hope that helps.
I'm trying to make a system of "chunks" in Box2D - i.e. shapes that are attached together in a big lump, so that you can break chunks off into their own body.
Currently, my system is very basic (just starting really) and modelled after the "Breakable" test in the Box2D testbed. It's a body with quite a few basic box fixtures (which will later become polygons). When you click a body, it deletes the fixture and creates a new body with the same fixture, rotation, etc in the same place as the old "chunk".
However, I can only get it to create the new body at the big body's origin
(->GetWorldCenter). What I want to do is find the global location of the fixture to be deleted and replaced, so I can make the new chunk there.
I've read some stuff about b2PolygonShape having a "centroid", but I'm not sure how to access and use that...
To put it simply, I want to find the global location (as x+y or a b2Vec2) of a FIXTURE, NOT a body (that's easy). The fixture will move with the body it's attached to, but I only currently need the position for one frame (having it update would be useful too though).
For Box2D position of the body not so important. Body position is only shift for fixture coordinates. It do not impact to simulation process. So, I don't understand, why you so care about this.
I made something like this, and can say, that using big body's origin works fine. You can see into Box2D testbed to Breakable test. There also used big body's origin. Only reason to calculate new coordinates that I see is strange behavior of SetAngle method.
If you still want centroid, look at ComputeCentroid, located at b2PolygonShape.cpp. Pay attention, method don't declared at b2PolygonShape.h. You can copy code from cpp and use it like this:
b2Fixture* chunk = ...;
b2Vec2 chunkCentroidLocal = ComputeCentorid(chunk->m_vertices,
chunk->m_vertexCount);
b2Vec2 chunkCentroidWorld = bigBody->GetWorldPoint(chunkCentroidLocal);
p.s. Don't forget to transform polygon points, when you will create new body. Simply find difference between big body and new body, and subtract it from every point of polygon.
b2Vec2 shift = chunkCentroidWorld - bigBody->GetWorldCenter();
b2Vec2 newVertices = new b2Vec2[chunk->m_vertexCount];
for(int i = 0; i< chunk->m_vertexCount; i++)
{
newVertices[i] = chunk->m_vertices[i] - shift;
}