Wednesday, January 28, 2015

[Tutorial] Cocos2d-x: The second game - Breakout - Create and destroy brick (Part 2)

Some tasks in this lesson:

+ Create bricks
+ Process physics collision
+ Check the destruction of bricks, if done, it's WINGAME
+ Check GameOver when the ball falls but not colliding with the paddle

- It seems muck work but it's simple because it's rather like the first game

Let's start!

Step 1 - Create bricks

Open file HelloWorldScene.cpp, add the following code block

for (int i = 0; i < 5; i++) {
static int padding = 100;
auto block = Sprite::create("blocks.png");
auto blockBody = PhysicsBody::createBox(block->getContentSize(), PHYSICSBODY_MATERIAL_DEFAULT);
blockBody->getShape(0)->setDensity(10.0f);
blockBody->getShape(0)->setFriction(0.0f);
blockBody->getShape(0)->setRestitution(1.f);
blockBody->setDynamic(false);
// Create the distance even among the bricks
int xOffset = padding + block->getContentSize().width / 2 +
((block->getContentSize().width + padding)*i);
block->setPosition(xOffset, 450);
blockBody->setContactTestBitmask(0x000001);
block->setPhysicsBody(blockBody);
block->setTag(3);
this->addChild(block);
}

Step 2 - Process the collision - Destroy bricks, Game Over

* Add ContactListener

auto dispatcher = Director::getInstance()->getEventDispatcher();
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);  
dispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

* Build the function onContactBegin

bool HelloWorld::onContactBegin(PhysicsContact& contact)
{
// Get two collided object
auto spriteA = (Sprite*)contact.getShapeA()->getBody()->getNode();
auto spriteB = (Sprite*)contact.getShapeB()->getBody()->getNode();

// Check kinds of objects
int tagA = spriteA->getTag();
int tagB = spriteB->getTag();

if (tagA == 3) // is brick
{

this->removeChild(spriteA,true); // delete brick

//spriteA->removeFromParentAndCleanup(true);
}

if (tagB == 3)  // is brick
{
this->removeChild(spriteB,true); // delete brick

//spriteB->removeFromParentAndCleanup(true);
}

// If the ball collides with the floor and the coordinate Y of the ball is smaller than the paddle, Game Over happens
if ((tagA == 0 || tagB  == 0 )& (ball->getPositionY() <= paddle->getPositionY()))
{
auto gameOverScene = GameOverScene::create();
gameOverScene->getLayer()->getLabel()->setString("You Lose!");
Director::getInstance()->replaceScene(gameOverScene);
}

return true;
}

This class GameOverScene is similar in the first project

Step 2 - check Win game

Build the function Tick() like this, remember that declare prototype function

void HelloWorld::tick(float dt)
{
// a bool variable confirm Win game and the initial value is true;
bool isWin = true;
// Vector bodies get all bodies of world ( ball, edge, paddle body)
Vector<PhysicsBody*> bodies = m_world->getAllBodies();

// Navigate the items in the above vector, check the kinds of objects by Tag, you should study again the "for" command, it has many variants for each special kind of class, you can read advanced C++ about list, vector, queue,etc.

for each(PhysicsBody* body in bodies) // This command will generate error when building android, you should edit as follow:  for (auto body : bodies) , this is the new standard for C++ v.11
{
if (body->getNode()->getTag() == 3) // If there is still  body of bricks, it means you haven't destroyed all yet.
{
isWin = false; // Not Win yet
}
}
// If navigate all isWin but not change, process Win game
if (isWin == true)
{
auto gameOverScene = GameOverScene::create();
gameOverScene->getLayer()->getLabel()->setString("You Win!");
Director::getInstance()->replaceScene(gameOverScene);
}
}

To call Tick() function, you should add this following command
this->schedule(schedule_selector(HelloWorld::tick),0); into the end of init() function

If you use scheduleUpdate(), you should build update(float dt) function

Build and Run now























Posted By Dev Bananaer8:47 PM

[Tutorial] Cocos2d-x: The second game - Breakout (Part 1)

In this game, you must do some following tasks:

+ Add objects into game
+ Set physics attributes ( using Chipmunk for ease )
+ Create the movement of the ball
+ Move the paddle

Step 1 - Add objects into game - Set physics attributes

Create a new project with familiar command:

>cocos new breakout -p com.vn.breakout -l cpp -d f:android/project

And remember to add USING_NS_CC; into HelloWorldScene.h 

Open file HelloWorldScene.h add some following commands in public area

Sprite* ball; 
Sprite* paddle;
Sprite* edgeSp;

PhysicsWorld* m_world; 

void setPhyWorld(PhysicsWorld* world){ m_world = world; };
// Touch event
void onTouchMoved(Touch *touch, Event *event);
void onTouchEnded(Touch *touch, Event *event);

bool onTouchBegan(Touch *touch, Event *event);
OK
Open file HelloWorldScene.cpp, edit createScene() function a bit like this:

auto scene = Scene::createWithPhysics();
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
Vect gravity(0.0f, 0.0f); // Vector gia tốc =0
scene->getPhysicsWorld()->setGravity(gravity);
auto layer = HelloWorld::create();
layer->setPhyWorld(scene->getPhysicsWorld());

 you delete all init() function but this:

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

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

//---Delete all here

return true;

Add the following long code block into the deleted area

edgeSp = Sprite::create();
auto boundBody = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3);// Create physics body
boundBody->getShape(0)->setRestitution(1.0f); 
boundBody->getShape(0)->setFriction(0.0f);
boundBody->getShape(0)->setDensity(1.0f);
edgeSp->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2)); // Set the position and the centre of Box in the center of the screen
edgeSp->setPhysicsBody(boundBody); // Set physics Body
boundBody->setContactTestBitmask(0x000001); // This is the important command, if not available, there is nothing happening when colliding
this->addChild(edgeSp); // Add into Layer
edgeSp->setTag(0); // Tag==0, to check object when colliding belongs to some kind

ball = Sprite::create("Ball.png", Rect(0, 0, 52, 52));
ball->setPosition(100, 100);
auto ballBody = PhysicsBody::createCircle(ball->getContentSize().width / 2.); // The physics body circle shape
ballBody->getShape(0)->setRestitution(1.0f);
ballBody->getShape(0)->setFriction(0.0f);
ballBody->getShape(0)->setDensity(1.0f);
ballBody->setGravityEnable(false); // Not set acceleration
Vect force = Vect(1010000.0f, 1010000.0f); // Create a force Vector to act with the direction of 45 degree, because x = y 
ballBody->applyImpulse(force); // Push a force into the ball edge
ball->setPhysicsBody(ballBody); // Set Physics body
ballBody->setContactTestBitmask(0x000001); //
ball->setTag(1);
this->addChild(ball);

// Similar with the ball
paddle = Sprite::create("Paddle.png");
auto paddleBody = PhysicsBody::createBox(paddle->getContentSize(), PHYSICSBODY_MATERIAL_DEFAULT);
paddleBody->getShape(0)->setRestitution(1.0f);
paddleBody->getShape(0)->setFriction(0.0f);
paddleBody->getShape(0)->setDensity(10.0f);
paddleBody->setGravityEnable(false);
paddleBody->setDynamic(false); // Set static when reacting, no restitution, no changing position
paddle->setPosition(visibleSize.width / 2, 50);
paddle->setPhysicsBody(paddleBody);
paddleBody->setContactTestBitmask(0x000001); // With reaction 
ball->setTag(2);
this->addChild(paddle);

Add the code block to create a Listener to catch Touch events ( using to control the paddle ) before the command: Return true;

auto touchListener = EventListenerTouchOneByOne::create();
touchListener->setSwallowTouches(true);
touchListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
touchListener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
touchListener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);

_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

Step 2 - Create the movement of the ball

Create the first movement for the ball by the following command ( in the above init() function created)

Vect force = Vect(1010000.0f, 1010000.0f); // Create a force Vector with 45 degree, because x = y

ballBody->applyImpulse(force); // Push a force into the ball edge

Step 3 - Move the paddle

Build 3 Touch functions, because we only use the onTouchMoved function to move so the other Touch functions we leave empty:

bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{
return true; // Not use but must return True
}

void HelloWorld::onTouchEnded(Touch* touch, Event* event)
{
// Not use
}

// Use to move the paddle simplest
void HelloWorld::onTouchMoved(Touch* touch, Event* event){
Point touchLocation = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
// To be simple, use this command: Point touchLocation = touch->getLocation();
paddle->setPositionX(touchLocation.x); // Set the position horizontal of the paddle follow the Touch point
}

OK! Build and run now!



Posted By Dev Bananaer8:20 AM

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:

Posted By Dev Bananaer1:19 AM

Choose an Android item