Cocos2d-x: Space Monkey – Part 3

In the second part of the Space Monkey tutorial we setup the game scene and add a background to it.
In this third part we will add the monkey to the game and we will move him around the scene.

Game Art

For this tutorial we are going to need some game art available at GameArtGuppy.com. Game Art Guppy is a site created by Vicki Wenderlich that provides free and inexpensive art for game development. For this part of the tutorial we will use the art set listed below, which is available for free (or for a small fee if you also want to obtain the vectors, which is not necessary for this tutorial):

Space Monkey Character: Space Monkey

Import the Space Monkey image into the Xcode project

Once you download the Space Monkey art you will find several images of the monkey. For this tutorial we will use spacemonkey_fly01.png which has a resolution of 118×158. For convenience I have renamed that image to space monkey_fly.png since t is the only image I used for the tutorial.

Drag and drop the image into the Resources folder of the Xcode project

In the import options dialog box that appears next, specify that the background image should be made available for both the mobile and desktop versions of the game by selecting the two targets. It is also important to make sure that the option to “Copy items if needed is selected”. Also, make sure the option to create folder references is selected.

Display the Space Monkey

In order to display our character in the game, some changes to the Space Scene class are required.
In Classes/SpaceScene.hpp add an instance variable to store an instance of the monkey image as a Sprite. Let’s call this variable Sprite* _monkey_p. Also, declare a function named addSpaceMonkey() that will be responsible for adding the Space Monkey to the scene. The private section of the SpaceScene class should now look like this:

private:
    cocos2d::Sprite* _background_p;
    cocos2d::Sprite* _monkey_p;

    void startGame (void);
    void addBackground(void);
    void addSpaceMonkey(void);

 

Let’s define the SpaceScene::addSpaceMonkey() function in Classes/SpaceScene.cpp as follows:

 

void SpaceScene::addSpaceMonkey(void){
    // Obtain screen information
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // Add the Space Monkey to the Scene at the center of the screen
    _monkey_p = Sprite::create("spacemonkey_fly.png");
    _monkey_p->setScale(0.5);
    _monkey_p->setPosition(Point(visibleSize.width/2 + origin.x,
                                 visibleSize.height/2 + origin.y));

    this->addChild(_monkey_p, 1);
}

 

The addSpaceMonkey() function starts by getting the screen information related to the visible size of the view and its origin. The Space Monkey sprite is then created, scaled to half its size and then centred on the screen. The sprite is displayed on the screen through the addChild() function with a z-order = 1 which means that the sprite will be displayed in a layer above the background layer (you may remember we set the background with z-order = 0).

Let’s call this function whenever the game starts. Modify function SpaceScene::startGame() so that it calls addSpaceMonkey() after seeing up the background:

void SpaceScene::startGame(void)
{
	log("Starting a new game!");
 	this->addBackground();
	this->addSpaceMonkey();
}

 

Build and Run the game and the scene should now look like this:

 

Moving the Monkey

We will move the monkey around by touching a location on the screen to where we want the monkey to move. In order to do this we need to enable touch handling as described next.

In Classes/SpaceScene.hpp declare the following two private methods:

addEventListener() – This method will be responsible for enabling touch handling in the game scene, including registering the callback function that will be invoked when a touch is detected.

onTouchBegan() – This is a callback function that will be called whenever the player touches the screen. The prototype of this function will be explained later in this tutorial when the implementation is described.

 private :
    cocos2d :: Sprite *  _background_p ;
    cocos2d :: Sprite *  _monkey_p ; 

    void startGame  ( void ) ;
    void addBackground( void ) ;
    void addSpaceMonkey( void ) ;
	void addEventListeners(void);
    bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event);

 

Edit Classes/SpaceScene.cpp and implement the addEventListeners() function as follows:

 void SpaceScene::addEventListeners(void)
{
    // Create a single touch event listener and register a callback function that will be invoked
    // every time the player touches the screen.
    auto touchListener = EventListenerTouchOneByOne::create();
	// Register the callback function that will handle the touch
    touchListener->onTouchBegan = CC_CALLBACK_2(SpaceScene::onTouchBegan, this);

    // Adds an event listener for a specified event with the priority of scene graph.
    this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);
}

 

The addEventListeners() function is responsible for enabling touch handling and to register a callback function that will be invoked when a touch is detected. In order to do this, the function creates an event listener of type EventListenerTouchOneByOne which is able to process one touch at a time.

Once the event listener is created, the callback function is registered in the EventListener by setting a reference to the callback function in the event listener instance variable onTouchBegan. In this particular case the callback being registered is SpaceScene::onTouchBegan that we will implement soon.

Finally, the sprite is added with SceneGrapgPriority. This means that the touch events are sent to objects that exist at the touch location in the order they are drawn. This means that the top most sprite (the one that is drawn on top of all other sprites under the touch location) will receive the touch event first.

The registered callback function SpaceScene::onTouchBegan() is where touch events are handled. This function is defined below:

 bool SpaceScene::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
    // Get the coordinate of the touch
    Vec2 touchLocation = touch->getLocation();
    log("Touch location: (%f,%f)", touchLocation.x, touchLocation.y);

    // This callback function needs to return true when finish handling the current touch
    // in order to receive the subsequent touch event.
    return true;
}

 

At this point we are only printing to the console the coordinates of the touch location.

From the startGame() function call the addEventListeners() function.

void SpaceScene::startGame(void)
{
	log("Starting a new game!");
	this->addBackground();
	this->addSpaceMonkey();
    this->addEventListeners();
}

If you run the game now and touch or click on any part of the screen you will see an output similar to this one

Now, to actually move the monkey we need to add to the SpaceScene class a function that will move the monkey to the selected location. We also need a class constant that will define the velocity of the monkey. Open Classes/SpaceScene.hpp and edit it so it now includes the new function and constant as shown below:

private:
    // Monkey velocity in points per second.
    constexpr static const float _monkeyVelocity = 500;

    cocos2d::Sprite* _background_p;
    cocos2d::Sprite* _monkey_p;

    void startGame (void);
    void addBackground(void);
    void addSpaceMonkey(void);
    void addEventListeners(void);
    bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event);
    void moveMonkey(cocos2d::Vec2 location, float velocity);

 

The velocity at which the monkey moves is defined in points per second. One point equals one pixel on a non-retina display, but in devices with Retina display a point is an area of 2×2 pixels.

Let’s implement SpaceScene::moveMonkey() in Classes/SpaceScene.cpp:

void SpaceScene::moveMonkey(Vec2 location, float velocity)
{
    // Based on the velocity, determine how much time it will take the object
    // to arrive to the touch location
    Vec2 objectPosition = _monkey_p->getPosition();
    Vec2 direction = Vec2(objectPosition,location);

    float timeToMove = direction.length()/velocity;

    // Calculate the rotation of the monkey so that it faces the direction of the touch location
    float angle = CC_RADIANS_TO_DEGREES(direction.getAngle()) - 90.0f;

    // move the monkey to the touch location.
    // we will do this using an action.
    MoveTo *actionMove_p = MoveTo::create(timeToMove, location);
    RotateTo *rotateAction = RotateTo::create(0.1, -angle);

    Action *easingAction_p = EaseCubicActionInOut::create(actionMove_p);
    _monkey_p->runAction(Sequence::create(rotateAction,easingAction_p, nullptr));
}

 

The function first obtains the distance between the touch location and the monkey position. This is done using one of the Cocos2d-x built in functions that perform the required vector arithmetic direction.length(). Dividing the distance to travel by the velocity of the monkey the function calculates the amount of time that should take the money to arrive to its destination.

Before the monkey moves, its sprite needs to be rotated so that it faces the direction of the target location. I am using the function direction.getAngle() to obtain the angle between the current position of the monkey and the new target. Please note that I am subtracting 90 degrees to the sprite angle because the space monkey image was made pointing North (90 degrees) but the sprite when created points internally to the East (0 degrees) but it displays the image as drawn.

In order to actually move the monkey, we will use a sequence of cocos2d-x actions. The first action in the sequence is a rotation so that the monkey first turns towards the target location. The second action in the sequence is an EasCubicActionInOut to move the sprite to its target location. I used an easing effect because I wanted to simulate inertia. My goal was to make the player feel that the monkey accelerates slowly and decelerates when is close to the target location. You will probably understand why I mean once you run the game 🙂

Let’s modify SpaceScene:onTouchBegan() function to call SpaceScene::moveMonkey().

bool SpaceScene::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
    // Get the coordinate of the touch
    Vec2 touchLocation = touch->getLocation();

    // Stop all previous actions before starting new ones. This resets
    // the internal state of the actions so that the monkey will end up
    // when we want even if the player toches a new destination while
    // the monkey is in the middle of the previous action.
    _monkey_p->stopAllActions();

    // Move the monkey to the new location
    this->moveMonkey(touchLocation, _monkeyVelocity);

    // This callback function needs to return true when finish handling the current touch
    // in order to receive the subsequent touch event.
    return true;
}

 

The onTouchBegan() function obtains the coordinates of the touch location and then stops all ongoing actions that the space monkey is performing, if any. This means that if the monkey is moving and the player touches the screen then the monkey will stop moving.

Finally, the function calls the moveMonkey() function again to move the monkey to the new touch location.

Build and run the game and see the monkey move!

 

Conclusion:

In this third part of the tutorial we have added the space monkey and we are able to move it around the screen.
In the next part of the tutorial we will make him capture bananas and for that we will dive into collision detection in Cocos2d-x.

One thought on “Cocos2d-x: Space Monkey – Part 3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s