Tuesday, January 27, 2015

[Tutorial] Cocos2d-x: Box2D - another physics library of cocos2d-x (Part 2)

Filled under: ,

In previous part, we studied about Box2D, and build a small app using physics Box2D. In this lesson, we come to advanced a bit, control the ball, make it collide with the screen với màn hình as follow:

+ Make the ball move by vector + force
+ Predict the direction the ball moves

Step 1 - Make the ball move by vector + force

* Open file HelloWorldScene.h, in Public area, add the following code block:

// You can see the meanings of variables in the below block
bool existBall;
float ballX;
float ballY;   
int dragOffsetStartX;
int dragOffsetEndX;
int dragOffsetStartY;
int dragOffsetEndY;   
float powerMultiplier; // Force
Sprite *points[32];
void defineBall(); // Create the ball follow Box2D

// Simulate the path
void simulateTrajectory(b2Vec2 coord);

// Catch the touch events 
bool onTouchBegan(Touch* touch, Event* event);
void onTouchMoved(Touch* touch, Event* event);
void onTouchEnded(Touch* touch, Event* event);

* Open file HelloWorldScene.cpp, Delete all old code but these:

if ( !Layer::init() )
{
return false;
}

Size visibleSize = Director::getInstance()->getVisibleSize();
Point origin = Director::getInstance()->getVisibleOrigin();

// Delete all here
return true;


+ Add the following code block into the deleted place:

b2Vec2 gravity = b2Vec2(0.0f, -10.0f); // Vector of acceleration
world = new b2World(gravity);  // Create world

// Create the ball, save the coordinate of the beginning point and end point 
 dragOffsetStartX = 0;   
dragOffsetEndX = 0;   
dragOffsetStartY = 0;   
dragOffsetEndY = 0;   
existBall= false; 

ballX = 500;
ballY = 200; 
powerMultiplier = 10; // The value of force is 10
ball =Sprite::create("ball.png");
ball->setPosition(Point(ballX,ballY));
this->addChild(ball);

// Build the frame around three sides of the screen 
addWall(visibleSize.width ,10,(visibleSize.width / 2) ,0);// Floor
addWall(10 ,visibleSize.height ,0,(visibleSize.height / 2) ); // Left
addWall(10 ,visibleSize.height ,visibleSize.width,(visibleSize.height / 2) ); // Right


// Catch the touch event
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);

listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

scheduleUpdate(); // Update Scene by Time 

+ Build the function addWall as follow, like the previous part, is also building physics body by Box2D 

void HelloWorld::addWall(float w,float h,float px,float py) {

b2PolygonShape floorShape; // the floor shape

floorShape.SetAsBox(w/ SCALE_RATIO,h/ SCALE_RATIO); // square, or rectangle
b2FixtureDef floorFixture;

floorFixture.density=0;
floorFixture.friction=10;
floorFixture.restitution=0.5;
floorFixture.shape=&floorShape;

b2BodyDef floorBodyDef;
floorBodyDef.position.Set(px/ SCALE_RATIO,py/ SCALE_RATIO);

b2Body *floorBody = world->CreateBody(&floorBodyDef);
floorBody->CreateFixture(&floorFixture);

}

+ The function defineBall(), declare again to create physics body for the ball of previous part and convert it as private function

void HelloWorld::defineBall(){
bodyShape.m_radius = 45 / SCALE_RATIO;

fixtureDef.density=10;
fixtureDef.friction=0.8;
fixtureDef.restitution=0.6;
fixtureDef.shape=&bodyShape;

bodyDef.type= b2_dynamicBody;
bodyDef.userData=ball;

bodyDef.position.Set(ball->getPosition().x/SCALE_RATIO,ball->getPosition().y/SCALE_RATIO);

ballBody = world->CreateBody(&bodyDef);
ballBody->CreateFixture(&fixtureDef);
ballBody->SetGravityScale(10);
}

+ Create the function onTouchBegan

bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{
// Save the coordinate of first touch point
dragOffsetStartX = touch->getLocation().x;
dragOffsetStartY = touch->getLocation().y;

Point touchLocation = touch->getLocation(); // Get the coordinate of touch point

// Save
ballX = touchLocation.x;
ballY = touchLocation.y;

// Check if the touch point doesn't have the ball, delete body of the ball created in defineBall() function
if (existBall){       
world->DestroyBody(ballBody);
}

ball->setPosition(Point(ballX ,ballY)); // Set new position ballX ,ballY
return true;
}

+ The function onTouchEnded

void HelloWorld::onTouchEnded(Touch* touch, Event* event)
{

existBall = true;

HelloWorld::defineBall(); // Create body of the ball at the last touch point

Point touchLocation = touch->getLocation(); // Get the touch point

// Save the last touch point
dragOffsetEndX = touchLocation.x;
dragOffsetEndY = touchLocation.y;

// The distance of moving the ball 
float dragDistanceX = dragOffsetStartX - dragOffsetEndX;
float dragDistanceY = dragOffsetStartY - dragOffsetEndY;

// Create the movement of body with an unchanging velocity with direction and the size = vector with the last point, the first point, multiply with a force = 10 = powerMultiplier
ballBody->SetLinearVelocity(b2Vec2((dragDistanceX*powerMultiplier)/SCALE_RATIO,(dragDistanceY*powerMultiplier)/SCALE_RATIO));
}

+ The function onTouchMoved

void HelloWorld::onTouchMoved(Touch* touch, Event* event)
{

}

Build and run. Pull the ball and release, you will see that the ball shoot out and collide with the wall of the screen.

Step 2 - Predict the direction of the movement of the ball

You need to build two functions as follow:

onTouchMoved

void HelloWorld::onTouchMoved(Touch* touch, Event* event)
{
Point touchLocation = touch->getLocation();

// Save the last point
dragOffsetEndX = touchLocation.x;
dragOffsetEndY = touchLocation.y;

// The distance of the movement of the ball
float dragDistanceX = dragOffsetStartX - dragOffsetEndX;
float dragDistanceY = dragOffsetStartY - dragOffsetEndY;

// Call the function to simulate the path of the ball, vector multiply with a force = 10 = powerMultiplier HelloWorld::simulateTrajectory(b2Vec2((dragDistanceX * powerMultiplier)/SCALE_RATIO,(dragDistanceY * powerMultiplier)/SCALE_RATIO));
}

simulateTrajectory

void HelloWorld::simulateTrajectory(b2Vec2 coord){
//Định nghĩa physics body bóng HelloWorld::defineBall();

// Tạo chuyển động cho body với 1 vận tốc không đổi có phương và độ lớn = vector truyền vào
ballBody->SetLinearVelocity(b2Vec2(coord.x,coord.y));


// Duyệt mảng point for (int i = 1; i <= 31; i++){

//Trong hàm Step, Giá trị đối số phải bằng gia tốc 10 và powerMultiplier (=10) world->Step(deltaTime,10,10);
points[i]->setPosition(Point(ballBody->GetPosition().x*SCALE_RATIO,ballBody->GetPosition().y*SCALE_RATIO));
world->ClearForces();

world->DestroyBody(ballBody);
}

+ In init() function, add a code block

// Initialize a Sprite array including 31 points, to demon the path of the movement of the ball
for (int i = 1 ; i <= 31; i++){
points[i] =Sprite::create("dot.png");
this->addChild(points[i]);
}

Build and run. When you drag the ball, you will see the dot path to show the direction of its movement. When you release, the ball fly right the direction, it means you're successful














Done for this lesson. Conclusion, in this lesson, you know how to:


+ Create the force for an object moving
+ Show the path of its movement in world

Expand this lesson, you can create the billiard game.

P/S: You can see that in Box2D, sprite is depended to Body, setting and moving body first, then display sprite follow the position of body. In Chipmunk is contrary, setting position Sprite first, then place body after. You can reference the previous lessons of Physics.




[ttaiit.blogspot.com translated]

0 comments:

Post a Comment

Choose an Android item