Developers Manual > Cocos2d-x > How To > User Contributed Tutorials > User Tutorial-Make A Game-Catch Me

Make A Game - Catch Me

Contributed By: Torelli95

In this tutorial we are going to build a simple game called CatchMe.

The game consist in use clicks to catch a simple animated label moving arround de window.

The code will be written on linux, only for clarification.

The tutorial include:

  • Custom typography
  • Actions
  • Events
  • Animations
  • Use UserDefault (save player scores).

Lets start.

Chapter 1


Setting the proyect

Create a new cocos project:

Write in terminal

cocos new -l cpp -d /path/to/project CatchMe

Replace /path/to/project for the path where you want to save your project.

After that open the project in your IDE, there must be 4 clases:

  • HelloWorld.cpp
  • HelloWorld.h
  • AppDelegate.cpp
  • AppDelegate.h

Add a new empty class called CatchMe.cpp and his respective CatchMe.h. This class take care of the most important element on our game, the label and all his procedures (Listeners,Animations,etc.)

Until now our HelloWorld looks like default cocos HelloWorld:

HelloWorld


Building our CatchMe label

First go to: Dafont to download a new font for our label, I downloaded the: NextGames.tff; We must save it in /path/to/CatchMe/Resources/fonts

You can build your own font.ttf or dowloaded in other places too.

In second place we have to declare the next variables and procedures in our CatchMe.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
///include cocos to the new class

#include "cocos2d.h"


///Extends LabelTTF because CatchMe will have _eventDispatcher for this must be a Node(Father of LabelTTF)

class CatchMe : cocos2d::LabelTTF
{
public:

   ///Constructor and Destructor of CatchMe class

    CatchMe();
    ~CatchMe();

    ///Gives LabelCatchMe to others

    cocos2d::LabelTTF* getLabel();

protected:
    cocos2d::LabelTTF* LabelCatchMe;

};

After that, declare constructor,destructor and getLabel method in Catch Me.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CatchMe::CatchMe()
{

    LabelCatchMe = LabelTTF::create("CatchMe", "fonts/NextGames.ttf", 72);
}

CatchMe::~CatchMe()
{
}

LabelTTF *CatchMe::getLabel()
{
    return LabelCatchMe;
}

Now we replace Hello World label for our new label on HelloWorld.cpp; for that we first have to create a new instance of our class CatchMe on HelloWorld.cpp.

Then declare the CatchMe instance like attribute of HelloWorld class in HelloWorld.h:

1
2
protected:
    CatchMe* Game; cocos2d::LabelTTF* LabelCatchMe;

As you might have noticed also declare another variable, LabelCatchMe, this will be used to maintain backward copy our label in HelloWorld.

And replace:

1
2
3
4
5
6
7
8
9
10
11
12
 // add a label shows "Hello World"
    // create and initialize a label

    auto label = LabelTTF::create("Hello World", "Arial", 24);

    // position the label on the center of the screen
    label->setPosition(Point(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label, 1);

to:

1
2
3
4
5
6
7
8
9
10
11
12
13
 // add a label shows "CatchMe"

    Game = new CatchMe();

    LabelCatchMe = Game->getLabel();


    // position the label on the center of the screen
    LabelCatchMe->setPosition(Point(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - LabelCatchMe->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(LabelCatchMe, 1);

Don't forget add#include "CatchMe.h" on HelloWorld.h

If you run the proyect, should look like this:

Second HelloWorld

Chapter 2


Add some color

The label looks good but would look better if the colors change.For this purpose we use GLubyte type variable.

In CatchMe.h declare the next variables:

1
GLubyte r,g,b;

r,g,b corresponds to RGB color model, The value of this variables can vary between 0 and 255.

Now we go to CatchMe Constructor in CatchMe.cpp and add the next lines

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CatchMe::CatchMe()
{
    ///Initializes the Label CatchMe
    LabelCatchMe = LabelTTF::create("CatchMe", "fonts/NextGames.ttf", 72);

    srand(time(nullptr));
    r = rand()%255;
    g = rand()%255;
    b = rand()%255;
    LabelCatchMe->setColor( Color3B(r,g,b) );
}```

Because of this the label will appear with a random color; but we want to change the color in current time, then we do the next steps.

Go to CatchMe.h and declare the methods:

```cpp
protected:
    ///return a small value to change the colors gradually
    GLubyte newColor();

public:
    ///Change color label
    void changeColor();

In CatchMe.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GLubyte CatchMe::newColor()
{
    GLubyte nuevoColor = rand() % 5;
    if(rand() % 3 == 0)
    nuevoColor = -nuevoColor;
    return nuevoColor;
}

void CatchMe::changeColor()
{
    r += newColor();
    g += newColor();
    b += newColor();
    LabelCatchMe->setColor( Color3B(r,g,b) ); 
}

The next step is to update the game scene and call the methods we declared above.

In HelloWorld.h:

1
protected: void update(float df);

In HelloWorld.cpp:

1
2
3
4
5
void HelloWorld::update(float df)
{
 Game->changeColor();
 LabelCatchMe = Game->getLabel();
}

And called in the HelloWordl::init:

1
schedule (schedule_selector(HelloWorld::update));

note that the update need a schedule.

if you run the project you have something like this:

With the label color changing Constantly.

Chapter 3


Events

In this section we want that our label do something if the player touched.

For that we dacleare the next method in CatchMe.h:

1
2
3
4
protected:
    ///Manage Events and Actions
    void setEventHandlers();

And in the CatchMe.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void CatchMe::setEventHandlers()
{
        //Create a "one by one" touch event listener (processes one touch at a time)
        auto listener = EventListenerTouchOneByOne::create();
        // When "swallow touches" is true, then returning 'true' from the onTouchBegan method will "swallow" the touch event, preventing other listeners from using it.
        listener->setSwallowTouches(true);
        // Example of using a lambda expression to implement onTouchBegan event callback function
        listener->onTouchBegan = [&](Touch* touch, Event* event){
            // event->getCurrentTarget() returns the *listener's* sceneGraphPriority node.
            auto target = static_cast<Sprite*>(event->getCurrentTarget());

            //Get the position of the current point relative to the button
            Point locationInNode = target->convertToNodeSpace(touch->getLocation());
            Size s = target->getContentSize();
            Rect rect = Rect(0, 0, s.width, s.height);
            //Check the click area
            if (rect.containsPoint(locationInNode))
            {
                ///The action that we want to run if the Label get touched
                auto hide = Hide::create();
                target->runAction(hide);
                return true;
            }
            return false;
        };
        //Add listener
            _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, LabelCatchMe);
}

Also need add the call of this method in CatchMe constructor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CatchMe::CatchMe()
{
    ///Initializes the Label CatchMe
    LabelCatchMe = LabelTTF::create("CatchMe", "fonts/NextGames.ttf", 72);

    srand(time(nullptr));
    r = rand()%255;
    g = rand()%255;
    b = rand()%255;
    LabelCatchMe->setColor( Color3B(r,g,b) );

    setEventHandlers();

}

Now if you run the proyect and click the label you will see the magic!

Movement and Animations

Now we want to do your game hard to play isn't it?

To move the label we need to generate a random point on the screen,taking into account the size of the label, so we will need the following method:

In CatchMe.h:

1
2
3
protected:
///Generate a random point on the scree,taking into account the size of the label
    cocos2d::Point generatedRandomPoint(cocos2d::Node* node);

In CatchMe.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Point CatchMe::generatedRandomPoint(Node* node)
{
   srand(time(nullptr));
   int randx;
   int randy;
   int contentSizeHeight = node->getContentSize().height;
   int contentSizeWidht = node->getContentSize().width;
   int visibleSizeHeight = Director::getInstance()->getVisibleSize().height - (contentSizeHeight * 0.5);
   int visibleSizeWidth = Director::getInstance()->getVisibleSize().width - (contentSizeWidht * 0.5);
   randx = rand() % visibleSizeWidth;
   randy = rand() % visibleSizeHeight;
      while((randx > (contentSizeWidht * 0.5) &&  randx < visibleSizeWidth && randy > (contentSizeHeight *0.5) && randy < visibleSizeHeight) == false ){
          randx = rand() % visibleSizeWidth;
          randy = rand() % visibleSizeHeight;}
   return Point(randx,randy);

}

Now we can add movement and animations, with the next method:
In CatchMe.h:

1
2
  protected:
    void animateGameTitle();

In CatchMe.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void CatchMe::animateGameTitle()
 {
    ///get the label size
     int nodeWidth = LabelCatchMe->getContentSize().width;
     int  nodeHeight = LabelCatchMe->getContentSize().height;
     ///Animatios to use
     auto moveTo = MoveTo::create(0.2f + rand() % 40 / 150.0f, generatedRandomPoint(LabelCatchMe));
     auto rotateTo = RotateTo::create(0.5f + rand() % 40 / 150.0f, rand() % 360);
     auto scaleTo = ScaleTo::create(0.2f + rand() % 40 / 150.0f,rand() % nodeWidth * 0.5/60,rand()%nodeHeight*0.5 /60);
    ///The animations will repeated many times,calls himself
     auto callFunc = CallFunc::create( this, callfunc_selector(CatchMe::animateGameTitle) );
     auto sequence = Sequence::create(moveTo,callFunc, nullptr);
     ///this Actions will run in parallel to MoveTo sequence.
     LabelCatchMe->runAction(sequence);
     LabelCatchMe->runAction(rotateTo);
     LabelCatchMe->runAction(scaleTo);
 }

Cocos2d-x has many other animations to use, can be found in the documentation of this.

Finally call animateGameTitle in the constructor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CatchMe::CatchMe()
{
    ///Initializes the Label CatchMe
    LabelCatchMe = LabelTTF::create("CatchMe", "fonts/NextGames.ttf", 72);

    srand(time(nullptr));
    r = rand()%255;
    g = rand()%255;
    b = rand()%255;
    LabelCatchMe->setColor( Color3B(r,g,b) );
    setEventHandlers();
    animateGameTitle();

}

If you run your proyect you will have something like this:

With the label moving, rotating and resizing, all over the screen.

But we have a problem, if we click the label, this not appears again.
For fix it we go to the setEventHandlers where we had this:

1
2
3
4
///The action that we want to run if the Label get touched
                auto hide = Hide::create();
                target->runAction(hide);
                return true;

we replace for:

1
2
3
4
5
6
7
8
9
    ///The action that we want to run if the Label get touched
                auto hide = Hide::create();
                auto show = Show::create();
                auto delay = DelayTime::create(0.75f);
   ///make a sequence that hide and show a little delay to the label
   ///keep moving to another part of the screen without the player sees
                 auto sequenceTouch = Sequence::create(hide,delay,show, nullptr);
                target->runAction(sequenceTouch);
                return true;

Now we have a funnier game to play!

Chapter 4


Scores!

A game without scores? What sense does it play if you can not exceed the maximum score or even know your score!?

We need to declare 3 new variables, due to time I'll add in the class HelloWorld is that it is not very elegant but worked for now.

HelloWorld.h:

1
2
3
4
5
6
7
8
public
 cocos2d::LabelTTF* currentScore;
 cocos2d::LabelTTF* bestScore;
    ```
And in the CatchMe.h:
```cpp
public:
int currentPoints;

HelloWorld.cpp:
In HelloWorld::init;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
currentScore = LabelTTF::create("0000","fonts/NextGames.ttf",25);
    currentScore->setPosition(Point(origin.x + visibleSize.width-currentScore->getContentSize().width,
                                    origin.y + visibleSize.height-currentScore->getContentSize().height));
    addChild(currentScore,-1);
    ```
And in CatchMe.cpp:
In:
```cpp
///The action that we want to run if the Label get touched
                auto hide = Hide::create();
                auto show = Show::create();
                auto delay = DelayTime::create(0.75f);
                ///make a sequence that hide and show a little delay to the label
                ///keep moving to another part of the screen without the player sees
                 auto sequenceTouch = Sequence::create(hide,delay,show, nullptr);
                target->runAction(sequenceTouch);

add:

1
currentPoints +=5;

This will increment currentPoints value if the label get touched.

get back to HelloWorld.cpp
In HelloWorld::update
add:

1
2
3
 char buffer[10];
 sprintf(buffer, "%04lli", Game->currentPoints);
 currentScore->setString(std::string(buffer));

This will take the current value og 'currentPoints' and transform it to string.

Now if you run the proyect you will play with points!!

But what about best score?

For that we declare the method bestScoreUpdated in HelloWorld.h:

1
void bestScoreUpdated();

and in the HelloWorld.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void HelloWorld::bestScoreUpdated()
{
    int best = UserDefault::getInstance()->getIntegerForKey("Best_Score");

    if(best < Game->currentPoints){
        best = Game->currentPoints;
        UserDefault::getInstance()->setIntegerForKey("Best_Score",best);
        char buffer[10];
        sprintf(buffer, "%04lli", best);
        bestScore->setString("Best: " + std::string(buffer));
        UserDefault::getInstance()->flush();
    }
    else{
        char buffer[10];
        sprintf(buffer, "%04lli", best);
        bestScore->setString("Best: " + std::string(buffer));
}
}

Now we call the method in HelloWorld::update:

1
2
3
4
5
6
7
8
9
void HelloWorld::update(float df)
{
 Game->changeColor();
 LabelCatchMe = Game->getLabel();
 char buffer[10];
 sprintf(buffer, "%04lli", Game->currentPoints);
 currentScore->setString(std::string(buffer));
 bestScoreUpdated();
}

And finally we add the label to the scene un HelloWorld::init:

1
2
3
4
 bestScore = LabelTTF::create("Best: ","fonts/NextGames.ttf",25);
    bestScore->setPosition(Point(origin.x + currentScore->getContentSize().width * 2.0f,
                                    origin.y + visibleSize.height-currentScore->getContentSize().height));
    addChild(bestScore,-1);

Now you have a useful game!


I hope you enjoy the tutorial!

Some extras to improve the game:
Add a timer for play,add funny music. Limit is your imagination with cocos2d-x!

Sign up for our newsletter to keep up with the latest developments, releases and updates for Cocos2d-x.