Saturday, March 21, 2015

Cocos2d-x Animating Objects with OnTouch and RunAction

cocos-2dx-touch-action-code

First of all create a new blank Cocos2d-X C++ project as shown here.
Now follow the previous tutorial(Handle Touch Input in Cocos2d-x Mobile Game) in this series. Don't add the business logic code inside HelloWorld::OnTouchEnded  callback function.

I would love to make this article stand alone by describing all the steps here but I can't do that due to lack of time. I hope it won't hurt much since the other articles are reasonably well written(at least I think so).

Now go inside HelloWorld::OnTouchEnded callback funciton in HelloWorldScene.cpp and add following to the callback

auto mainScene =  getChildByName("mainscene");
auto lblHello = mainScene ->getChildByName("lblHello");

auto vecLocationOfTouch = touch ->getLocation();

CCLOGERROR("onTouchEnded x: %f y: %f", vecLocationOfTouch.x, vecLocationOfTouch.y);


By doing this you will have a handle to the "Hello World!" label you created by following earlier tutorials.

Next we need to animate the label on touch. We will use Cocos2d-x MoveTo action in this case and supply it the vector vecLocationOfTouch.

The exact code to make the label run around the screen chasing your mouse clicks or screen taps is given below.






lblHello ->getActionManager() ->removeAllActions();

auto actMoveTo = MoveTo::create(2.0, vecLocationOfTouch);

lblHello ->runAction(actMoveTo);

You can see the removeAllActions() method call before creating the action, I'm not sure if its necessary but still I kept it there as it's not causing any harm either.

I created a moveTo action and provided it with the speed of the movement as first parameter and the end location of the animation in the second parameter.

I wish I had a decent screen capture software, I would be posting videos of final results too.

Feel free to ask questions and get in touch.

Friday, March 13, 2015

Handle Touch Input in Cocos2D-X Mobile Game

cocos2d-x-ontoucheventhandler-example

In this post I am going to show you how to handle touches on a phone or tablet screen, same method works for PC mouse clicks.
First of all go ahead and create a new project as shown here.
I named my project "TouchMe", since that's what we are going to demonstrate in this article.

Next we will go inside the automatically generated code of HelloWorld class.
Inside the function bool HelloWorld::init() find the following line

addChild(rootNode);

Comment this line and use the code given below to create a root node with a string name so that we will be able to grab it for use later on.

addChild(rootNode, 0, "mainscene");

Next we will instantiate and draw a label object, I've defined constants for starting points.

const float STARTX = 150;
const float STARTY = 500;

The code for creating a label,

auto lblHello = Label::create
 ("Hello World!", "Times", 48.0, cocos2d::Size::ZERO,
cocos2d::TextHAlignment::CENTER,
cocos2d::TextVAlignment::CENTER);

lblHello ->setPosition(STARTX,STARTY);
 
rootNode ->addChild(lblHello, 1, "lblHello");
// CODE PRETTY PRINTING WITH http://tohtml.com/cpp/

Listening For Touch Events

We will use EventListenerTouchOneByOne to listen for screen touch events. Make sure you have appropriate namespaces added, to be exact following macro statement right after include files:

USING_NS_CC;

You don't need to add any special include file.

Next we will define a touchListener object in following manner.

auto touchListener = EventListenerTouchOneByOne::create(); touchListener ->setSwallowTouches(true);

We will supply some methods of HelloWorld class to the touchListener object. First of we will define these methods in header file, open HelloWorldScene.h file.

Add Touch Function Definitions to Class

Add following section right before closing of your HelloWorld class,

private: virtual bool onTouchBegan(Touch *touch, Event *event); virtual void onTouchMoved(Touch *touch, Event *event); virtual void onTouchEnded(Touch *touch, Event *event);

Important: Even if we are not interested in onTouchBegan method, we won't be able to receive onTouchEnded without handling the former.

Now go back to the HelloWorldScene.cpp file, and go inside HelloWorld::init() method. Right after ->setSwallowTouches(true); line add code shown below:

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

CC_CALLBACK_2 macro is used to define a callback.

Add touch handling method definitions

Inside the HelloWorldScene.cpp file, we will add method definitions for touch callbacks. The code will be added after HelloWorld::init() method.









void HelloWorld::onTouchEnded(Touch *touch, Event *event)
{
 Vec2 posCurr = event -> getCurrentTarget() ->getPosition();
 auto mainScene =  getChildByName("mainscene");
 auto lblHello = mainScene ->getChildByName("lblHello");

 auto delta =  touch ->getDelta();
 auto a = touch ->getLocation();
 auto b = touch ->getPreviousLocation();
 auto c = touch ->getStartLocation();

 CCLOGERROR("onTouchEnded x: %f y: %f", posCurr.x, posCurr.y);

 lblHello ->setPosition(a);


}

bool HelloWorld::onTouchBegan(Touch *touch, Event *event)
{ 
    return true;
}
void HelloWorld::onTouchMoved(Touch *touch, Event *event)
{

}

You can see that only onTouchEnded method contains some code, including a CCLOGERROR line to show x and y coordinates of the touch. It will show text log in debug view.

Explaining logic for touch event handling

We are getting hold of x,y coordinates of the touch event as Vec2 object using Event * event method getPosition().
After that we got hold of the main scene using getChildByName("mainscene"). 
Next we will use the returned value to reach the label object.
Once the label object is reached, its position will be set using setPosition(a) statement.

Tuesday, March 3, 2015

Use Cocos2D-x Actions & Sequence for animation and callback lambda functions

cocos-2dx-code-running-screenshot

In this post I will show you how to create an animation using Cocos2D-X Actions and Sequences. I
will also show you how to use lambda functions to repeat an action.
Create a new Cocos2D-X project, as shown here.
Now go to HelloWorld::init method inside HelloWorldScene.cpp file.

Right before the function, define constants for starting point where our label "Hello World!" will be shown later.

const float STARTX = 100;
const float STARTY = 500;

Now find the location in the automatically generated code where main scene is being added to root node. Example is shown below

addChild(rootNode, 1, "mainscene");

We will add a text label on screen, code is

auto lblHello = Label::create
 ("Hello World!", "Times", 48.0, cocos2d::Size::ZERO,
 cocos2d::TextHAlignment::CENTER,
 cocos2d::TextVAlignment::CENTER);

lblHello ->setPosition(STARTX,STARTY);
rootNode ->addChild(lblHello, 1, "lblHello");

We created a new label with text "Hello World!", font is Times, size 48, set the location to STARTX STARTY.

Next we add this label to the root node as a child on top of everything else that was there(see the number 1).
Compile, build, and run the code. It should work fine and display a label on screen.

Cocos2D-X Action MoveBy

An action is an instruction for Cocos2D-X engine to do something over a given span of time. For example, move an object from point x, y to x+10, y in 2 seconds.

First of all let's find the size of the window in which the label will be shown, and the size of the text label itself.

auto winSize = Director::getInstance() ->getWinSize(); 
auto lblSize = lblHello ->getContentSize();
auto locToMove = winSize.width - lblSize.width;

We have used the width of the window and the label to calculate how many pixels will our label need to move in order to fly to the right end of the window.
Director is the grand daddy of everything inside a Cocos2D-x game.

To make it fly, we will create an object of Action as illustrated below

auto actMove = MoveBy::create(DURATION, Vec2(locToMove, 0));

I have defined the constant DURATION earlier, it is a float type and represents the speed of animation in seconds.

const float DURATION = 2.0;

This action can be run by calling appropriate method on lblHello, as shown below

lblHello ->runAction(actMove);

Try to build and run your code, you'll see the label will fly from left to right most corner and stop.

Cocos2D-x Sequence

A sequence as the name implies is a set of actions performed on an object one after another. An example of sequence will be

  1. Move an object by 100 pixels on x axis
  2. Scale the object to double its original size
An example of this behavior with the label which we created earlier is given below:


auto winSize = Director::getInstance() ->getWinSize(); 
auto lblSize = lblHello ->getContentSize();

auto locToMove = winSize.width - lblSize.width;

auto actMove = MoveBy::create(DURATION, Vec2(locToMove, 0));
 
auto actGrow = ScaleBy::create(DURATION, 2, 2, 0);

auto seqMoveAndGrow = Sequence::create(actMove, actGrow, nullptr);

lblHello ->runAction(seqMoveAndGrow);



I'm intentionally repeating some of the code since I believe people tend to zoom straight in to the code, without any idea about the context. I personally discourage copy/paste programmers but still they are brothers in code at some level.

Now build and run this code. You will see the text label hello world fly from left to right edge of screen, it will then grow to double its size. The label will go outside the screen, but don't worry about that yet.

Cocos2D-x Spawn

Spawn is yet another construct to group more than one actions, these actions are performed on an object in parallel. For example if you want the label to move right and grow big at same tie. Try following lines instead of seqMoveAndGrow.

auto spawnMoveAndGrow = Spawn::create(actMove, actGrow, nullptr);
lblHello ->runAction(spawnMoveAndGrow);
// see above snippet for variable definitions

Build and run your code now. You will see spawn in action. I highly recommend you to write code alongside reading this article, that will keep you interest level up and fortify the learning as well.

Using Lambda function to repeat animation

Lambda functions were introduced in C++ 11. These are anonymous methods or call back functions in a decent C++ form. We can register a lambda function as part of a sequence. When the sequence ends, our lambda function will be called back.

Define the lambda function

The lambda function will be defined as a member variable in HelloWorld class.
Open the corresponding header file HelloWorld.h
Right before the ending bracket of class HelloWorld, add following code

public:
 cocos2d::CallFunc* callBackLabelMoveComplete;

CallFunc* is a lambda function type defined by Cocos2D-x game engine.

Assign a value to lambda function

Now let's open the file HelloWorld.cpp and inside init() function go to line after where you added the label to root node, I mean this line

rootNode ->addChild(lblHello, 1, "lblHello");

We will assign a value to the callback function in following manner







// callBackLabelMoveComplete is class level variable
callBackLabelMoveComplete = CallFunc::create([this](){

auto mainScene = getChildByName("mainscene");
auto lblHello = mainScene ->getChildByName("lblHello");
  
int nMoveVal = 100;

auto winSize = Director::getInstance() ->getWinSize(); 
  
auto lblSize = lblHello ->getContentSize().width;
  
auto locToMove = winSize.width - lblSize;

nMoveVal = locToMove;
if(lblHello ->getPositionX() >= locToMove){
 nMoveVal = -locToMove;  
 }

cocos2d::MoveBy * actMove;

actMove = MoveBy::create(DURATION, Vec2(nMoveVal, 0));
auto seqMove = Sequence::create(actMove, callBackLabelMoveComplete, nullptr);
lblHello ->runAction(seqMove);
});


Important: We have the lambda function ready to be fired, but it won't do anything until we pull the trigger somewhere.
In order to do so, we will use a sequence. Where the label will move to right, our callback lambda function will be fired and the label will move back to left. Same behavior will keep repeating again and again. The code for this operation is given below:

auto winSize = Director::getInstance() ->getWinSize(); 
auto lblSize = lblHello ->getContentSize();

auto locToMove = winSize.width - lblSize.width;

auto actMove = MoveBy::create(DURATION, Vec2(locToMove, 0));
 
auto seqMoveForever = Sequence::create(actMove, callBackLabelMoveComplete, nullptr);

lblHello ->runAction(seqMoveForever);
// follow me on Twitter @Na3mAkrm
// for variable definitions see earlier snippets
// code highlighting by tohtml.com

Build and run to see the label running around for you.
There could have been a fighter jet here, but that would make things little complicated.
I will post more simple and easy to use articles so that you can understand how games work, what are sprites, and collisions etc.

Assignment:
Try to move the label in a box formation. Left to right, then bottom right, then back left bottom, and up to the starting point again. Code in lambda function will be changed to do so.

Video of the code in action will be posted soon.

Keep coming back for more.
Feel free to talk back, share ideas and discuss issues. Do follow me on twitter: @Na3mAkrm