Overview

This document describes how to use the Cocos2d-x runtimes. The language of the runtimes is in C++. We will go through an example of how to load an exported JSON file and play it back in the scene.

Grabbing the runtimes

You can either download the runtimes directly from the Creature Game Runtimes window or grab them from the repository here.

Libraries Needed to compile

Along with the core runtime files, the following libraries are also included:

  • gason (Include the Header and also compile the single gason.cpp source)

  • glm (Header only so make sure your include paths are setup for it)

Make sure to include these 2 libraries in source form in your project.

Header Includes

The following headers need to be included:

#include "MeshBone.h"
#include "CreatureRenderer.h"

Loading and Initialization

Let us assume we have an exported dragon animation file called dragonTest.json. We also have its corresponding texture atlas called character-dragon.png. We start off by first loading the file assets:

auto filename = CCFileUtils::getInstance()->fullPathForFilename("dragonTest.json");
auto texture_filename = CCFileUtils::getInstance()->fullPathForFilename("character-dragon.png");

CreatureModule::CreatureLoadDataPacket json_data;
CreatureModule::LoadCreatureJSONData(filename, json_data);

The above will load the JSON data from disk and into memory. Next, let us create the actual objects that can make use of these loaded assets:

auto cur_creature = std::shared_ptr<CreatureModule::Creature>(new CreatureModule::Creature(json_data));

creature_manager = new CreatureModule::CreatureManager(cur_creature);
creature_manager->CreateAnimation(json_data, "default");
creature_manager->CreateAnimation(json_data, "second");    

In the example above, the JSON file has 2 animation clips: default and second. Hence, we will need to create 2 animations from the creature_manager object to make them available for playback.

Now that we are done loading, we can set the active animation for playback:

creature_manager->SetActiveAnimationName("default");

This sets default as the currently active animation. We will now go ahead and create the object to render the character animation:

auto creature_renderer = CreatureRenderer::Renderer::create(
	creature_manager,CCTextureCache::getInstance()->addImage("character-dragon.png"));

This creates a creature renderer with our texture atlas image file. Once that is done, we can go ahead and set some playback and rendering properties:

creature_manager->SetIsPlaying(true);
creature_manager->SetShouldLoop(true);
creature_manager->SetMirrorY(true);
creature_renderer->setColor(cocos2d::Color3B(255, 255, 255));

Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
creature_renderer->setScale(30.0);
creature_renderer->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

Finally, we add the object to the scene:

this->addChild(creature_renderer, 1);

That concludes the basic instructions on how to integrate your character into Cocos2dX!

Complete Code Sample

Here is the complete code layout. Most the the code is generated from the default Cocos2dX project starter template. Pay attention to the method loadModel() which does most of the work:

#include "HelloWorldScene.h"
#include "MeshBone.h"
#include "CreatureRenderer.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
	 // 'scene' is an autorelease object
    auto scene = Scene::create();

    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

	 // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
	 return scene;
}

void HelloWorld::loadModel()
{
	// The actual code that loads the character animation data
    auto filename = CCFileUtils::getInstance()->fullPathForFilename("dragonTest.json");
    auto texture_filename = CCFileUtils::getInstance()->fullPathForFilename("character-dragon.png");

    CreatureModule::CreatureLoadDataPacket json_data;
    CreatureModule::LoadCreatureJSONData(filename, json_data);

    auto cur_creature = std::shared_ptr<CreatureModule::Creature>(new CreatureModule::Creature(json_data));

    creature_manager = new CreatureModule::CreatureManager(cur_creature);
    creature_manager->CreateAnimation(json_data, "default");
	creature_manager->CreateAnimation(json_data, "second");

    creature_manager->SetActiveAnimationName("default");

    auto creature_renderer = CreatureRenderer::Renderer::create(creature_manager,                                                             			 CCTextureCache::getInstance()->addImage("character-dragon.png"));
    creature_manager->SetIsPlaying(true);
    creature_manager->SetShouldLoop(true);
    creature_manager->SetMirrorY(true);
    creature_renderer->setColor(cocos2d::Color3B(255, 255, 255));
    creature_renderer->SetDebugDraw(false);


    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    creature_renderer->setScale(30.0);
    creature_renderer->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    this->addChild(creature_renderer, 1);
	scheduleUpdate();
}

	// on "init" you need to initialize your instance
	bool HelloWorld::init()
	{
	    //////////////////////////////
	    // 1. super init first
	    if ( !Layer::init() )
   		 {
   	   	  return false;
	    }

	    Size visibleSize = Director::getInstance()->getVisibleSize();
   		 Vec2 origin = Director::getInstance()->getVisibleOrigin();

	    /////////////////////////////
		 // 2. add a menu item with "X" image, which is clicked to quit the program
   		 //    you may modify it.

	    // add a "close" icon to exit the progress. it's an autorelease object
	    auto closeItem = MenuItemImage::create(
                                       "CloseNormal.png",
                                       "CloseSelected.png",
                                       CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));

		closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem-	>getContentSize().width/2 ,
                            origin.y + closeItem->getContentSize().height/2));

	    // create menu, it's an autorelease object
	    auto menu = Menu::create(closeItem, NULL);
	    menu->setPosition(Vec2::ZERO);
	    this->addChild(menu, 1);

	    /////////////////////////////
	    // 3. add your codes below...

	    // 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(Vec2(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);

	    // add "HelloWorld" splash screen"
	    auto sprite = Sprite::create("HelloWorld.png");

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

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

		// Load your character
	    loadModel();

   		return true;
	}

void HelloWorld::menuCloseCallback(Ref* pSender)
{
    Director::getInstance()->end();
}

Custom Time/Frame Range

You can set custom time/frame ranges for the currently active animation. Say you wanted to limit the playback to the frame range of 10 to 20, you would do the following:

creature_manager->SetUseCustomTimeRange(true);
creature_manager->SetCustomTimeRange(10, 20);

Animation Blending

You can blend between 2 animation clips by doing the following:

creature_manager->SetBlending(true);
creature_manager->SetBlendingAnimations("default", "second");
creature_manager->SetBlendingFactor(0.5); // 0 to 1 blends between the 2 clips

Smooth Transitions with Auto Blending

If you want to smoothly transition from one animation state to the next, you can use the Auto Blending feature in the runtimes.

To enable Auto Blending, first do:

creature_manager->SetAutoBlending(true);

after you have added all animations into your creature manager object.

To use Auto Blending, a single call to:

creature_manager->AutoBlendTo(NEW_ANIMATION_NAME, 0.1f);

will result in the character smoothly transitioning to the target animation called NEW_ANIMATION_NAME. The second parameter determines the time delta of the transition, with a range of 0.0 to 1.0.

Overriding/Modifying Bone Positions

Sometimes you need to modify the bone positions of the character directly. For example, you might want the positions of the bones to follow a number of rigid bodies connected with springs/joints for ragdoll physics. In the cases where you need to set bone positions for your own custom requirements, you should do the following. First, write your custom bone override method. Here is an example that displaces the bones in y by some amount:

// This is an example of how you can use a callback function to modify the position of the bones
// on your character. In this example, we will displace all the bones by a fixed amount in y.
void
HelloWorld::bonesModifyCallback(std::unordered_map<std::string, meshBone *>& bones_map)
{
    for(auto& bone_data : bones_map)
    {
        auto cur_bone = bone_data.second;
        auto cur_start_pos = cur_bone->getWorldStartPt();
        auto cur_end_pos = cur_bone->getWorldEndPt();
    
        cur_start_pos.y -= 5;
        cur_end_pos.y -= 5;
    
        cur_bone->setWorldStartPt(cur_start_pos);
        cur_bone->setWorldEndPt(cur_end_pos);
    }
}

You will also need to tell the CreatureManager to use your custom bone modify callback like this:

// Example of how to register a callback function to modify the bones
std::function<void (std::unordered_map<std::string, meshBone *>&) > cur_callback =
    std::bind(&HelloWorld::bonesModifyCallback, this, std::placeholders::_1);
creature_manager_2->SetBonesOverrideCallback(cur_callback);

Character Instancing and Memory

If you need to instance multiple copies of a character(for example 2 dragons), you should create your animations like this instead:

// Create and load the animations
auto new_animation_1 = std::shared_ptr<CreatureModule::CreatureAnimation>(
                                                                        new CreatureModule::CreatureAnimation(json_data,
                                                                                              "default"));

auto new_animation_2 = std::shared_ptr<CreatureModule::CreatureAnimation>(
                                                                          new CreatureModule::CreatureAnimation(json_data,
                                                                                                                "pose2"));

You should then proceed to you create a new Creature object, a new CreatureManager object and a new CreatureRenderer object. You will add the created animations into your newly created CreatureManager object like this:

// Second creature instancing example. This shows you how to load a second creature.
// Because both the first and second creature share animation data, you end up
// saving memory.
auto creature_2 = std::shared_ptr<CreatureModule::Creature>(new CreatureModule::Creature(json_data));

CreatureModule::CreatureManager * creature_manager_2 = new CreatureModule::CreatureManager(creature_2);
creature_manager_2->AddAnimation(new_animation_1);
creature_manager_2->AddAnimation(new_animation_2);
creature_manager_2->SetActiveAnimationName("pose2");
creature_manager_2->SetIsPlaying(true);
creature_manager_2->SetUseCustomTimeRange(true);
creature_manager_2->SetCustomTimeRange(10, 60);

auto creature_renderer_2 = CreatureRenderer::Renderer::create(creature_manager_2,
                                                             CCTextureCache::getInstance()->addImage("character-dragon.png"));
creature_renderer_2->setColor(cocos2d::Color3B(255, 255, 255));
creature_renderer_2->setScale(30.0);
creature_renderer_2->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y - 200));

this->addChild(creature_renderer_2, 1);

The difference between this example and the previous is that the animations are first created, then added into their respective CreatureManagers. This means the memory allocated for the animations(the most expensive) are stored in a standard location. Multiple CreatureManager objects will add animations from a common pool of CreatureAnimation objects.

Speeding up Playback Performance with Point Caches

If you want to increase the speed of the playback animations and not pay the penalty for the posing engine, you can enable point caches on specific animations.

Point caching essentially stores away the vertices of the animation, allowing the playback to bypass the posing engine. Character rigs with very complex skeletons and deformations benefit greatly from point caching.

To enable point caching on a specific animation, do this:

my_creature_manager->MakePointCache("myAnimationName");

That is all that is required to create the cache. Remember that the cache is specific to the animation, not character. This means that if you have instanced multiple characters, you only pay the cost of a single cache for that animation. This results in both memory savings and playback performance speedups.