I am working on similar to snake. I want to make snake body.
Game logic is that:
Snake move up and down. The moving should be like real snake motion.
Here i am getting struck.
How to make body of the snake?
Any idea or reference should help me lot.
Thanks in advance.
Ok, this is going to be a LONG answer.
I put together a quick example of this using some code from other projects and the "snake" part. You can find the entire (cocos2d-x) codebase here on github.
The easiest (and first) thing to do is to build the snake body. From the Box2D standpoint, you can build it out of a series of segments, each connected by the a revolute joint.
Here's the picture of what we're going for:
Here is the "rough" code I used to create it:
// Constructor
MovingEntity(b2World& world,const Vec2& position) :
Entity(Entity::ET_MISSILE,10),
_state(ST_IDLE)
{
// Create the body.
b2BodyDef bodyDef;
bodyDef.position = position;
bodyDef.type = b2_dynamicBody;
Body* body = world.CreateBody(&bodyDef);
assert(body != NULL);
// Store it in the base.
Init(body);
// Now attach fixtures to the body.
FixtureDef fixtureDef;
PolygonShape polyShape;
vector<Vec2> vertices;
const float32 VERT_SCALE = .5;
fixtureDef.shape = &polyShape;
fixtureDef.density = 1.0;
fixtureDef.friction = 1.0;
fixtureDef.isSensor = false;
// Nose
vertices.clear();
vertices.push_back(Vec2(4*VERT_SCALE,2*VERT_SCALE));
vertices.push_back(Vec2(4*VERT_SCALE,-2*VERT_SCALE));
vertices.push_back(Vec2(8*VERT_SCALE,-0.5*VERT_SCALE));
vertices.push_back(Vec2(8*VERT_SCALE,0.5*VERT_SCALE));
polyShape.Set(&vertices[0],vertices.size());
body->CreateFixture(&fixtureDef);
body->SetLinearDamping(0.25);
body->SetAngularDamping(0.25);
// Main body
vertices.clear();
vertices.push_back(Vec2(-4*VERT_SCALE,2*VERT_SCALE));
vertices.push_back(Vec2(-4*VERT_SCALE,-2*VERT_SCALE));
vertices.push_back(Vec2(4*VERT_SCALE,-2*VERT_SCALE));
vertices.push_back(Vec2(4*VERT_SCALE,2*VERT_SCALE));
polyShape.Set(&vertices[0],vertices.size());
body->CreateFixture(&fixtureDef);
// NOW, create several duplicates of the "Main Body" fixture
// but offset them from the previous one by a fixed amount and
// overlap them a bit.
const uint32 SNAKE_SEGMENTS = 4;
Vec2 offset(-4*VERT_SCALE,0*VERT_SCALE);
b2Body* pBodyA = body;
b2Body* pBodyB = NULL;
b2RevoluteJointDef revJointDef;
revJointDef.collideConnected = false;
// Add some "regular segments".
for(int idx = 0; idx < SNAKE_SEGMENTS; idx++)
{
// Create a body for the next segment.
bodyDef.position = pBodyA->GetPosition() + offset;
pBodyB = world.CreateBody(&bodyDef);
_segments.push_back(pBodyB);
// Add some damping so body parts don't 'flop' around.
pBodyB->SetLinearDamping(0.25);
pBodyB->SetAngularDamping(0.25);
// Offset the vertices for the fixture.
for(int vidx = 0; vidx < vertices.size(); vidx++)
{
vertices[vidx] += offset;
}
// and create the fixture.
polyShape.Set(&vertices[0],vertices.size());
pBodyB->CreateFixture(&fixtureDef);
// Create a Revolute Joint at a position half way
// between the two bodies.
Vec2 midpoint = (pBodyA->GetPosition() + pBodyB->GetPosition());
revJointDef.Initialize(pBodyA, pBodyB, midpoint);
world.CreateJoint(&revJointDef);
// Update so the next time through the loop, we are
// connecting the next body to the one we just
// created.
pBodyA = pBodyB;
}
// Make the next bunch of segments get "smaller" each time
// to make a tail.
for(int idx = 0; idx < SNAKE_SEGMENTS; idx++)
{
// Create a body for the next segment.
bodyDef.position = pBodyA->GetPosition() + offset;
pBodyB = world.CreateBody(&bodyDef);
_segments.push_back(pBodyB);
// Add some damping so body parts don't 'flop' around.
pBodyB->SetLinearDamping(0.25);
pBodyB->SetAngularDamping(0.25);
// Offset the vertices for the fixture.
for(int vidx = 0; vidx < vertices.size(); vidx++)
{
vertices[vidx] += offset;
vertices[vidx].y *= 0.75;
}
// and create the fixture.
polyShape.Set(&vertices[0],vertices.size());
pBodyB->CreateFixture(&fixtureDef);
// Create a Revolute Joint at a position half way
// between the two bodies.
Vec2 midpoint = (pBodyA->GetPosition() + pBodyB->GetPosition());
revJointDef.Initialize(pBodyA, pBodyB, midpoint);
world.CreateJoint(&revJointDef);
// Update so the next time through the loop, we are
// connecting the next body to the one we just
// created.
pBodyA = pBodyB;
}
// Give the tail some real "drag" so that it pulls the
// body straight when it can.
pBodyB->SetLinearDamping(1.5);
pBodyB->SetAngularDamping(1.5);
// Setup Parameters
SetMaxAngularAcceleration(4*M_PI);
// As long as this is high, they forces will be strong
// enough to get the body close to the target position
// very quickly so the entity does not "circle" the
// point.
SetMaxLinearAcceleration(100);
SetMaxSpeed(10);
SetMinSeekDistance(1.0);
}
This will get you the first part of this, a basic body. Here are some notes about the code:
NOW, getting the body to move the way you want is a bit more tricky. I'm going to show you a picture (and video) first, so you can see where I got it to. All this is in the code base I referenced, so you can tweak it if you like.
First, here is a screenshot of what the snake looked like after I dragged it around a bit.
And I took some video you could see it colliding, slowing, etc. (see it here).
When you see it in motion, it is still not perfect, but I think it looks pretty good for a couple hours of work.
To make the snake move, I took the approach of "dragging" it around by its head. The head rotates towards my finger as I drag it (or you can make it follow a path in code, chase something, etc.) and turns its head towards the target. The rest of the body "drags" along, which gives it an "ok" looking motion.
The controller users two different mechanisms to get the body moving:
Body Direction using Seek Behavior
The body uses a "seek" behavior, which works like this:
Below is the code for the ApplyThrust(...) method of a MovingEntity class.
void ApplyThrust()
{
// Get the distance to the target.
Vec2 toTarget = GetTargetPos() - GetBody()->GetWorldCenter();
toTarget.Normalize();
Vec2 desiredVel = GetMaxSpeed()*toTarget;
Vec2 currentVel = GetBody()->GetLinearVelocity();
Vec2 thrust = desiredVel - currentVel;
GetBody()->ApplyForceToCenter(GetMaxLinearAcceleration()*thrust);
}
This method applies thrust to make the b2Body move towards a target direction. It has a maximum speed it can travel (GetMaxSpeed()) and a maximum linear acceleration (GetMaxLinearAcceleration()) as properties of the class.
If you follow the code and draw the vectors, you will see that what this does is apply thrust to drive your velocity so it is pointing at the position of the target.
Another way to look at it: It acts like a feedback loop in (in vectors) to keep your velocity matched up with the desired velocity. If you just think about it in terms of scalars, it is a little easier to see.
If you are moving to the right (currentVel) at 5 m/s and your maximum speed (desiredVel) is 6 m/s, the thrust will be positive to the right, pushing you faster to the right (desiredVel - currentVel = +1). That is to say, you will speed up to the right.
If you are moving to the right (currentVel) at 7 m/s and your maximum speed (desiredVel) is 6 m/s, the thrust will be negative, point to the left, slowing you down (desiredVel - currentVel = -1).
From update to update of the physics, this makes your body move in the direction of the target at the speed you want.
In your case, you only want to move left and right and let gravity pull your body down. All of this should work well in the context of the physics. You can control how fast your agent speeds up/slows down by controlling the linear acceleration.
Body Rotation using PID Controller
This is a bit complicated (as if the previous part wasn't).
The idea is to apply torque to the body based on the difference between the angle you want to go to and the angle your body is facing. A PID controller does using the current angle difference (times a proportional constant), the difference over the recent history (integral), and the rate of change of the difference in the angle (derivative). The first two get the body turning and the last one slows it down as it reaches the target angle.
You can see more details on the theory and implementation here.
All of this is encapsulated into a single class in the code, called PIDController.
NOTE: The PIDController in the code base is generic. It does not know anything about box2d, physics, or what it is being used for. It's like a sorting algorithm...it only cares about the data it is fed and the parameters you set for how it works. It can easily be used in other contexts...and has for ~100 years.
Hopefully this got you going in the right direction. It may have been a little overkill, but I kind of dig this stuff, so it was fun.