Pages

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

Thursday, February 26, 2015

Cocos2d-x Hello World Practical Game Programming

In this post I am going to show you how to create a "Hello World!" program in Cocos2d-x game development engine.

  1. We will use C++ programming language
  2. Windows PC 
  3. Visual Studio 2012 for code editing
  4. Cocos2D-x version 3.4 game engine

It will be a hello world in true sense of the word. To learn about installing Cocos2D-x and setting things up for game development please check following link:

Step 1. Create a new Cocos2D-X project with Cocos-Console

  • Open command prompt (Widows + X then C in Windows 8, Windows + R then type CMD press enter on older versions). Run following command to create a new game project.

cocos new -p com.honesitconsultancy.helloworld -d "D:\Cocos\Projects" -l cpp "HelloWorld"

  • Now to to the folder location where you created the new project, in my case it is D:\Cocos\Projects\HelloWorld
Contents of the folder are shown in Fig 1. 
cocos2d-x-hello-world-blank-project
Fig 1: Blank Cocos2D-X project

  • Open the sub-folder "proj.win32".
  • Open the file "HelloWorld.sln"
The name can be different if you use anything other than HelloWorld.

  • Expand the solution in "Solution Explorer" window, often found on right side of screen.
  • Expand the solution sub folder "src" and click "HelloWorldScene.cpp"

cocos2d-x-visual-studio-hello-world
Fig 2: HelloWorldScene.cpp and Init function

HelloWorld::Init function

This function is the heart of a Cocos2D-X game/program. From a game programmer's perspective, this function is the new void main(void).
We will write our custom logic here.

Create a label object with cocos2d::Label

Anywhere inside the Init function, create a new label object.

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

// code highlighting with http://tohtml.com/cpp/

The keyword "auto" is a new construct introduced in C++ 11 standard. Cocos 3.x follows the latest C++ standard. you may write cocos2d::Label instead, but let's live with auto for now.

We have created a new cocos2d::Label object with text "Hello World!", font "Times", and font size 48.

But it will not show on screen right now.

Scene Node

Cocos2D-X game engine keeps all game objects organized in a set of interlinked nodes. These nodes form a graph together.
Our game starts running from the top most node which is also called "root". Cocos2D-x has generated a default root node for us. Here's the default code:

auto rootNode = CSLoader::createNode("MainScene.csb");

addChild(rootNode);








We will add our newly created label as a child to the root node represented by "rootNode" object.
The label will appear on very bottom left of our screen. We can move it around by calling "setPosition" function on it to set position of our label on x axis and y axis. The source code is given below

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

lblHello ->setPosition(300,500);

Now build the project by pressing F6 key, Ctrl+B keyboard shortcut, or going to menu "Build -> Build Solution". The build process might take a minute.

Once the build is finished, run the project by pressing F5 key or clicking the tiny play icon on top.
The result is shown below.

cocos2dx-helloworld-running
Fig 3: HelloWorld program running

The init method will look like the screen shot shown below after we finish editing.

Fig 4: Final code snippet

Bonus

Once you're done with the main subject of this post, try adding this code right before return true;

//bonus

auto actMove = MoveBy::create(1.5, Vec2(100, 100));

lblHello ->runAction(actMove);

Build & run and see what happens :)

For other articles in this series, please visit

Wednesday, February 25, 2015

Cocos2D-X Game Development in Android Studio

When we create a new Cocos2D-X project using Cocos2D-Console, an Eclipse project is generated by default. The problem is that Google has dropped support for Eclipse ADT, they're pushing IntelliJ Idea based Android Studio with full force.
In this post I will show you how to convert your Cocos2D-X Android Eclipse ADT game project into Android Studio project, how to configure NDK and JNI in Android Studio to run your game from Android Studio directly.


Create a new Cocos2D-X Project

First of all, let's create a brand new Cocos2D-X game development project, you may skip this part if yo already have a project. Run the Cosos-Console command.
cocos new -p com.honesitconsultancy.indiegame -d "D:\Cocos\Projects" -l cpp "IndieGame"

Fig 1: Cocos-Console New Project


Once the command line operation is done, you will have a new project folder named "IndieGame" on path "D:\Cocos\Projects\". The folder will look like the screenshot below:

Fig 2: Cocos2D-X New Project Structure

1. Import Android project into Android Studio

Run Android studio, if you already have a project opened close it. Otherwise, it will show the project selection dialog exhibited below:
Fig 3: Android Studio New Project Dialog


I've blurred the project names on left to hide names of a few client project I'm providing consultancy through oDesk.


  1. Click on the item "Import project(Eclipse ADT, Gradle, etc.)
  2. Project selection dialog will open. Browse to your game path, in my case it was "D:\Cocos\Projects\IndieGame\". The sub folder "IndieGame" was not being shown by the user interface, I had to click the refresh button.
  3. Select the Android project generated by Cocos-Console command line. The name currently will be "proj.android". The refresh button is highlighted by a circle, the Android project is shown by an arrow.

Fig 4: Select Project Dialog Android Studio

The next dialog will ask for a path to save the new Android Studio project. You must keep the new project alongside the other projects.
Fig 5: Android Studio Select Destination Path

  • Change the name of project folder to proj.androidstudio and click Next

You'll see a few options on next screen. Android Studio is trying to help you, we don't need it though. check all 3 check-boxes reading "Replace jars with dependencies, when possible"; "Replace library sources with dependencies, when possible"; and "Create Gradle-style(camelCase) module names" on the next screen, as shown below. Click Finish button.

Fig 6: Android Studio Import project from ADT Options


The Android Studio will ask that the directory does not exist and do you want to create it? Click "OK" and a new directory/folder will be created for you.

2. Add NDK to your project properties

Android Native Development Kit(NDK) path will be added to your the "local.properties" Android Studio project file, it is an auto generated file.
Click on the proj.androidstudio name on top left of your screen, and select local.properties as shown in the screen grab below.
Android Studio Open local.properties


By default this file will contain the path to Android SDK, don't mess with it. We need to add a line of text below the existing SDK line.
The format of the NDK entry will be ndk.dir=%Path of your NDK directory%
In my case, the precise statement is shown below:

ndk.dir=D\:\\cocos\\android-ndk-r10d-windows\\android-ndk-r10d

Important: Windows users must modify the path string and add backslash in following places:
  • Right after the drive letter
  • Right every backslash in the path
Save the local.properties file and close it.

3. Modify Build.Gradle to handle JNI

  • On top left, click proj.androidstudio
  • Click IndieGame(or whatever project name you've chosen)
  • Click build.gradle




Add a new line right after closing bracket } pertaining to defaultConfig section.
Add following magic lines down there:

sourceSets.main
            {
                jni.srcDirs = []
            }
The sourceSets.main line will enable use of Java Native Interface or JNI in your project. JNI does same thing as Interop on Microsoft .Net.









Running your Cocos2D-x Indie Game on Android Devices

We need to perform just one more step before hitting the build icon.

  • Open command prompt.
  • Change directory to the location of your project.
  • Once there, type following cocos-console command

cocos compile -s ".\proj.androidstudio" -p android --ndk-mode debug

Be Patient: I got a system with Core-i7 CPU and 8 GB RAM, my system takes about a minute to compile the code. If someone's got a slower/older system, getting a cup of coffee while the code is compiling is a good idea.

4. API Level 9 Error

By default the Cocos-Console generates a project with Android SDK level 9. Its an old platform, if you don't have installed on your system Android Studio will show an error. To fix the problem, you'll need to modify the Android SDK level values in your game Manifest.xml and Gradle configuration file.

4a. Changes in Gradle Build File


  • Luckily you would already have the Gradle file opened to make half of the changes(otherwise follow Modify Build.Gradle to enable JNI section above). Find the defaultConfig section shown below and change values accordingly. I got platform 14 installed so I changed it to 14.


defaultConfig {
        applicationId "com.honesitconsultancy.indiegame"
        minSdkVersion 14
        targetSdkVersion 14

        ndk {
            moduleName "cocos2dcpp_shared"
        }
    }


  • Change the compileSDKVersion value as well. By default it was 10, I'll change it to 14.
  •     compileSdkVersion 14
Save the file.

4b. Changes in Android Studio Manifest File

Now open the Manifest file, you can do so by clicking project name hierarchy as shown by the thunderbolt in the screen grab below(proj.androidstudio -> IndieGame -> src -> main). I replaced the arrow, it looked boring :)


We need to change the minSDKVersion property here. We'll change it to 14(or any suitable SDK platform level you got installed).

<uses-sdk android:minSdkVersion="14"/>

After changing the value, save the manifest file. And hit the Synchronize button as well.

Make The Project

Here comes the climax!

Click the "Build" menu and select "Make Project" menu item. You may use shortcut key Ctrl + F9
Again, just stay calm and wait for a while as the project is being make.
Click "Run" button, or use shortcut key Shift + F10.

Note: I assume you've already configured a phone or emulator to work with your Android Studio, how to do so is beyond the scope of this article.

Once your blank Cocos2D-X game project starts running following screen will be displayed by default:
Please feel free to post comments on this blog, do check other articles in this series. You may contact me via e-mail using the form on left top of this post.
I will be posting more stuff, you better subscribe to the mailing list.