Cocos2d Runtimes

Overview

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

Please remember that because we are using both C++ and Objective-C, the files calling into this runtime should be of extension .mm . Just rename your .m file to .mm will make it work.

Grabbing the runtimes

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

Header Includes

The following headers need to be included:

#include "CreatureModule.h"
#import "CCreatureRender.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:

NSString * jsonPath = [[NSBundle mainBundle] pathForResource:@"dragonTest" ofType:@"json"];
std::string realPath([jsonPath UTF8String]);

CCTexture * inputTexture = [CCTexture textureWithFile:@"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:

creature_render = [[CCreatureRender alloc] initWithData:creature_manager andTexture:inputTexture];
creature_render.scale = 12;
creature_render.position = CGPointMake(300, 100);

Finally, we add the object to the scene:

[self addChild:creature_render];

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

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:

CCLOG(@"Loading Creature");

NSString * jsonPath = [[NSBundle mainBundle] pathForResource:@"dragonTest" ofType:@"json"];
std::string realPath([jsonPath UTF8String]);

CCTexture * inputTexture = [CCTexture textureWithFile:@"character-dragon.png"];

// Load Creature
CreatureModule::CreatureLoadDataPacket json_data;
CreatureModule::LoadCreatureJSONData(realPath, json_data);

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

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

creature_manager->AddAnimation(new_animation_1);
creature_manager->SetActiveAnimationName("default");
creature_manager->SetIsPlaying(true);
creature_manager->SetTimeScale(82.0);

creature_render = [[CCreatureRender alloc] initWithData:creature_manager andTexture:inputTexture];
creature_render.scale = 12;
creature_render.position = CGPointMake(300, 100);

[self addChild:creature_render];

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

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.