Rigid Body and Soft Body Physics
This is the first of a 4 part series which will show you how to simulate a simple soft body physics object in Box2d. We will be attempting to draw a fully texture mapped, squishy object. In part 1, I will be constructing the physics object.
Box2d is a rigid body physics engine. This means that the physics bodies are non-deformable. But imagine a scenario where you do want a deformable physics body. A good example is a water balloon. The water balloon should be squishy and should deform when it hits the ground. A soft body physics engine can handle something like this. Unfortunately, Box2d is not a soft body physics engine. But there is a way to simulate this in Box2d using distance joints.
Distance Joint
A distance joint keeps the distance between two points a constant. The key feature of distance joint that will help us achieve the soft body physics simulation is that a distance joint can be made soft to simulate a springy connection. We will be creating a wheel like structure in Box2d and then connecting all of the bodies with a springy distance joint in order to simulate the squishiness.
You can read more about distance joints here.
Wheel Construction
The shape to the left is what we will be constructing in Box2d. The greater the number of circles in the outer ring, the better the circle will look. In a later part of the tutorial, I will go through constructing a polygon in OpenGL to map to this Box2d wheel construction.
The wheel is constructed of circles surrounding an inner circle. All of the circles are connected to each other by distance joints. The distance joints are configured to be springy. You may find it helpful to read through this post, which explains drawing a circle in Cocos2d using OpenGL. The math part will be relevant to our discussion here.
Let’s Start
Create a Box2d Cocos2d project. You will want to turn on the b2DebugDraw::e_jointBit so that you can see the joints in the debug drawing. I will not go through how to set up the physics world here. You can find articles on this site that can explain it to you if you do not know how to do this. Also, sample source code will be posted so you can refer to that as well. In HelloWorldLayer.mm, you will see the code below. MyNode is the our custom node which will hold our squishy physics object. We add this to the layer and move it to the center of the screen.
// MyNode is the squishy physics MyNode *node = [MyNode node]; node.position = ccp(240, 160); [node createPhysicsObject:world]; [self addChild:node]; |
Here is the source code for MyNode. We create the outer circles and lay them out in a circle pattern around the center circle. Then we connect them all together with distance joints. The code is well commented so you should be able to figure out what’s going on from reading it.
#import "CCNode.h" #import "Box2D.h" typedef struct { GLfloat x; GLfloat y; } Vertex2D; static inline Vertex2D Vertex2DMake(GLfloat inX, GLfloat inY) { Vertex2D ret; ret.x = inX; ret.y = inY; return ret; } @interface MyNode : CCNode { } - (void) createPhysicsObject:(b2World*)world; @end |
#import "cocos2d.h" #import "MyNode.h" #define PTM_RATIO 32.f @implementation MyNode - (id) init { self = [super init]; return self; } // The number of outer circles (more, the smoother the circle) #define NUM_SEGMENTS 12 - (void) createPhysicsObject:(b2World *)world { // Center is the position of circle that is in the center b2Vec2 center = b2Vec2(240/PTM_RATIO, 260/PTM_RATIO); b2CircleShape circleShape; circleShape.m_radius = 0.25f; b2FixtureDef fixtureDef; fixtureDef.shape = &circleShape; fixtureDef.density = 0.1; fixtureDef.restitution = 0.05; fixtureDef.friction = 1.0; // The greater the number, the more springy float springiness = 4.0; // Delta angle to step by float deltaAngle = (2.f * M_PI) / NUM_SEGMENTS; // Radius of the wheel float radius = 50; // Need to store the bodies so that we can refer back // to it when we connect the joints NSMutableArray *bodies = [NSMutableArray array]; // For each segment... for (int i = 0; i < NUM_SEGMENTS; i++) { // Current angle float theta = deltaAngle*i; // Calcualte the x and y based on theta float x = radius*cosf(theta); float y = radius*sinf(theta); // Remember to divide by PTM_RATIO to convert to Box2d coordinates b2Vec2 circlePosition = b2Vec2(x/PTM_RATIO, y/PTM_RATIO); b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; // Position should be relative to the center bodyDef.position = (center + circlePosition); // Create the body and fixture b2Body *body; body = world->CreateBody(&bodyDef); body->CreateFixture(&fixtureDef); // Add the body to the array to connect joints to it // later. b2Body is a C++ object, so must wrap it // in NSValue when inserting into it NSMutableArray [bodies addObject:[NSValue valueWithPointer:body]]; } // Circle at the center (inner circle) b2BodyDef innerCircleBodyDef; innerCircleBodyDef.type = b2_dynamicBody; // Position is at the center innerCircleBodyDef.position = center; b2Body *innerCircleBody = world->CreateBody(&innerCircleBodyDef); innerCircleBody->CreateFixture(&fixtureDef); // Connect the joints b2DistanceJointDef jointDef; for (int i = 0; i < NUM_SEGMENTS; i++) { // The neighbor. int neighborIndex = (i + 1) % NUM_SEGMENTS; // Get the current body and the neighbor b2Body *currentBody = (b2Body*)[[bodies objectAtIndex:i] pointerValue]; b2Body *neighborBody = (b2Body*)[[bodies objectAtIndex:neighborIndex] pointerValue]; // Connect the outer circles to each other jointDef.Initialize(currentBody, neighborBody, currentBody->GetWorldCenter(), neighborBody->GetWorldCenter() ); jointDef.collideConnected = true; jointDef.frequencyHz = springiness; jointDef.dampingRatio = 0.5f; world->CreateJoint(&jointDef); // Connect the center circle with other circles jointDef.Initialize(currentBody, innerCircleBody, currentBody->GetWorldCenter(), center); jointDef.collideConnected = true; jointDef.frequencyHz = springiness; jointDef.dampingRatio = 0.5; world->CreateJoint(&jointDef); } } @end |
Video and Code
You can download the sample source for this article here.
Soft Body Physics with Box2d and Cocos2d Part 2/4
Soft Body Physics with Box2d and Cocos2d Part 3/4
Soft Body Physics with Box2d and Cocos2d Part 4/4
[...] 1. Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan) 2. Soft Body Physics with Box2d and Cocos2d Part 1/4 [...]
[...] Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan) 2. Soft Body Physics with Box2d and Cocos2d Part 1/4 3. Soft Body Physics with Box2d and Cocos2d Part [...]
[...] Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan) 2. Soft Body Physics with Box2d and Cocos2d Part 1/4 3. Soft Body Physics with Box2d and Cocos2d Part 2/4 4. Soft Body Physics with Box2d and Cocos2d [...]