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 Live Blog8: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 Live Blog8: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 Live Blog1:19 AM

Saturday, January 24, 2015

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

Filled under: ,

Now, you study how to use Box2D in Cocos2d-x. By default, Cocos2d-x is installed for users to use the library of Cocos2d-x + Chipmunk, if you want to use Box2D, you must do some task to import it into the compiler.

In this lesson, you study:
+ How to setup to use Box2D
+ An example of physic, applying Box2D physic

Bắt đầu chém nào!

Step 1 - Setup to use Box2D

Because by default, Cocos2d-x forces you to use Chipmunk, so you must do some following tasks:
1/ Open file CMakeLists.txt in Project  ( using NotePad++ to display line number ). Find to line number 162, and add "Box2D" as below:

(Before)

Posted By Live Blog9:06 PM

[Tutorial] Cocos2d-x: Create physics body of the complex objects by using Physical Editor software

We know how to create physics in Cocos2d-x V3.0, but in the case that is a circle, so creating physics body is rather easy. if our object has complex boundary, how do we create?

This lesson has two parts:
+ Create PhysicsBody Polygons from images by Box2d-Editor software
+ Import into game and make Physics of Engine process through analysis functions.








Part 1: Create PhysicsBody Polygons

Posted By Live Blog7:07 AM

Wednesday, January 21, 2015

[Tutorial] Cocos2d-x: Create the first simple game - Gameover, calculate scores, add sounds for game effervescent (Part 4)

In this last part, we do all remaining tasks:

+ Check game-Over when monsters collide character
+ Add sound when shooting bullets and add some background music for more attractive.
+ Here skip calculating scores. This lesson is only in simple level.

Now let's start!!!!!


Step 1 - Add sound into game

You Download resource, copy into folder Resource of Project
Then open file HelloWordScene.cpp, in function init(), add the following lines

CocosDenshion::SimpleAudioEngine::getInstance()->preloadBackgroundMusic("background-music-aac.wav");
CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("background-music-aac.wav",true); // True = unlimited loop


Find to function onTouchEnded(), add the following lines


CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("pew-pew-lei.wav");

CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("pew-pew-lei.wav");

Add a command #include"SimpleAudioEngine.h" after first "#include" .

Step 2 - Create GameOver scene

you create a new Scene named GameOverScene.h and GameOverScene.cpp like this, Download Class


Now view a bit the two files:

* Open file GameOverScene.h, declare 2 Classes GameOverLayer, and GameOverScene, with constructor, destructor, init() function.

But, you should notice the two commands:

CC_SYNTHESIZE_READONLY(cocos2d::LabelTTF*, _label, Label); (1)

CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer); (2)

It's rather difficult to see, but we can see as follow:

(1) create a variable _label with type LabelTTF* through a method of Label class

(2) create a variable _layer with type GameOverLayer* tthrough a method of Layer class

It's simple like so, you can study more about function Inline here

* Open file GameOverScene.cpp, destructor (with the mark ~ ), simply release the pointers of class to release memory.

Consider function Init()
GameOverScene::init(), create a new Scene with new Layer
GameOverLayer::init(), create a layer with white color - Color4B(255,255,255,255)

Color3B create color of RGB from 3 input params. Color4B create color of RGBA from 4 input params, there the last param will adjust opacity.

This block: 

this->runAction( Sequence::create(
                                DelayTime::create(3),
                                CallFunc::create(this, 
                                callfunc_selector(GameOverLayer::gameOverDone)),
                                NULL));

is to create 2 Actions continuously
+ delay 3 seconds: DelayTime::create(3)
+ Then CallFunc::create(this, callfunc_selector(GameOverLayer::gameOverDone)), // call function gameOverDone

The function gameOverDone() do only task to replace GameOverScene by new Game Scene to continue playing

void GameOverLayer::gameOverDone()
{
    Director::getInstance()->replaceScene(HelloWorld::createScene()); // Create a new Game Scene
}


You open file HelloWordScene.cpp, find to function onContactBegin(), in the code block:

// Character that is collided by monsters
if((tag==1&tag1==2)||(tag==2&tag1==1))
 {
// process GameOver
// calculate scores
 }

Add the following code block

auto gameOverScene = GameOverScene::create(); // Create a Scene Over of GameOverScene class
gameOverScene->getLayer()->getLabel()->setString("You Lose :["); // Set a message on the screen
Director::getInstance()->replaceScene(gameOverScene); // Replace game Scene by  game Over Scene 

Remember to add "#include"GameOverScene.h" in #include lines.

Before compiling and running, you must do the following task:

+ Build Android, open file Android.mk in the path firstgame\proj.android\jni\Android.mk add this line ../../Classes/GameOverScene.cpp below the line ../../Classes/HelloWorldScene.cpp
+ Build Win32: open file firstgame.vcxproj in the path firstgame\proj.win32\firstgame.vcxproj add the two following lines:

<ClCompile Include="..\Classes\GameOverScene.h" /> below the line <ClCompile Include="..\Classes\HelloWorldScene.h" />

<ClCompile Include="..\Classes\GameOverScene.cpp"/> below the line <ClCompile Include="..\Classes\HelloWorldScene.cpp" />

Now build and run 

And ... error 

You should add USING_NS_CC; below the lines "#include" in GameOverScene.h ( each .h file should be added it ).

Done, build and run now is OK.

























Posted By Live Blog7:52 AM

Choose an Android item