概述

阅读本文档,您可以了解如何如何在您的自定义C/C ++引擎中使用Creature运行库。该运行库使用C++语言。我们将通过一个示例来说明如何加载导出的JSON文件并在场景中进行播放。

获取运行库

您可以直接从Creature游戏运行库窗口下载运行库,或点击 这里从版本库获取运行库。要获取您的自定义引擎,您需要获取包含核心Creature运行库库文件的Cocos2d-x运行库。我们将忽略Cocos2d-x特有文件。

编译程序库

Cocos2d-x运行库除核心运行库文件外,还包括以下程序库:

  • gason(包含标头文件,并可编译单个gason.cpp源)

  • glm(仅含标头,请确保已为其设置路径)

请确保您的项目中已包含这两个源形式程序库。

应关心文件

  • MeshBone.cpp & MeshBone.h:Creature运行库的占用引擎。您可以在此访问整个骨架层次结构、其对应的骨骼以及网格区域。

  • CreatureModule.cpp & CreatureModule.h:负责管理Creature角色并构造加载到动画文件中的角色的层。

所含标头

应包括以下标头:

#include "MeshBone.h"
#include "CreatureModule.h"

加载和初始化

假设已导出一个名为dragonTest.json的龙动画文件,及其相应的纹理图谱character-dragon.png

首先加载文件资源:

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);

上述操作将从磁盘加载JSON数据并进入内存。然后,创建可以使用这些加载的资源的实际对象:

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");    

在上述示例中,JSON文件有2个动画剪辑:默认剪辑和第二剪辑。因此,您需要从Creature管理器对象创建2个动画,使它们可以播放。

已完成加载,可以设置活动状态的动画用于播放:

creature_manager->SetActiveAnimationName("default");

上述操作将默认剪辑设置为当前活动的动画。

使运行库适应您自己的引擎

为了在您的引擎中播放和显示Creature角色,您需要访问播放姿势机制以及最终Creature角色网格的拓扑、点和纹理坐标。

Creature管理器:管理动画和加载角色的类。您可以使用该类,以一定的时间增量推进动画。您的游戏循环或渲染器应在每个“游戏刻度”与该类的更新函数进行交互。

Creature:包含加载Creature的类。可以从Creature管理器访问该类。此类包含游戏引擎在屏幕上显示所需的所有几何体和渲染数据。

步骤 1:构造函数

让我们带着这一问题看看它在实践中是如何工作的。假设您的引擎中有一个渲染器类,它可以显示加载的角色。

实际上,我们可以检查Cocos2d-x渲染器,并以此为例,看看它的运作方法。该渲染器的构造函数使用过程如下:

Renderer::Renderer(CreatureModule::CreatureManager * manager_in,
                   cocos2d::Texture2D * texture_in)
: manager(manager_in), texture(texture_in), debug_draw(false)
{
    setGLProgram(cocos2d::ShaderCache::getInstance()->getGLProgram(cocos2d::GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR));
    scheduleUpdate();
}

在上述过程中,您可以看到:该构造函数使用一个输入Creature管理器对象来初始化成员变量管理器。这样一来,您即可在类中访问管理器以及内部角色。

步骤 2:更新和推进动画

每个游戏引擎都有更新或游戏刻度调用。在这种情况下,我们运行:

void
Renderer::update(float delta)
{
    manager->Update(delta);
}

通过上述操作通知管理器以一定的时间增量更新其Creature角色,即可使动画通过增量值向前推进。

步骤 3:显示网格

Creature对象返回以下内容:指数、贴图坐标和点。这些属性足以渲染出角色网格。具体方法如下:

void
Renderer::doDraw(const cocos2d::Mat4& transform, uint32_t transformFlags)
{
    getGLProgramState()->apply(transform);
    cocos2d::GL::bindTexture2D(texture->getName());
    
    cocos2d::Color3B nodeColor = getColor();
    
    manager->GetCreature()->FillRenderColours(nodeColor.r,
                                              nodeColor.g,
                                              nodeColor.b,
                                              getDisplayedOpacity());
    
    glEnableVertexAttribArray(cocos2d::GLProgram::VERTEX_ATTRIB_POSITION);
    glEnableVertexAttribArray(cocos2d::GLProgram::VERTEX_ATTRIB_COLOR);
    glEnableVertexAttribArray(cocos2d::GLProgram::VERTEX_ATTRIB_TEX_COORDS);
    
    glVertexAttribPointer(cocos2d::GLProgram::VERTEX_ATTRIB_POSITION,
                          3,
                          GL_FLOAT,
                          GL_FALSE,
                          0,
                          manager->GetCreature()->GetRenderPts());
    
    glVertexAttribPointer(cocos2d::GLProgram::VERTEX_ATTRIB_COLOR,
                          4,
                          GL_UNSIGNED_BYTE,
                          GL_TRUE,
                          0,
                          manager->GetCreature()->GetRenderColours());
    
    glVertexAttribPointer(cocos2d::GLProgram::VERTEX_ATTRIB_TEX_COORDS,
                          2,
                          GL_FLOAT,
                          GL_FALSE,
                          0,
                          manager->GetCreature()->GetGlobalUvs());
    
    glDrawElements(GL_TRIANGLES,
                   manager->GetCreature()->GetTotalNumIndices(),
                   GL_UNSIGNED_INT,
                   manager->GetCreature()->GetGlobalIndices());
    
    if(debug_draw)
    {
        cocos2d::Director* director = cocos2d::Director::getInstance();
        director->pushMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
        director->loadMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);
        
        cocos2d::DrawPrimitives::setPointSize(5.0);
        glLineWidth(5.0f);
        cocos2d::DrawPrimitives::setDrawColor4F(1, 1, 1, 1);
        
        drawDebugBones(manager->GetCreature()->GetRenderComposition()->getRootBone());
        
        director->popMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    }
    
}

通过上述操作您可以看到,所需的动作只是简单地发送点、贴图坐标和指数到您的渲染管道,以及绘制数据作一系列的三角形。

自定义时间/帧范围

您可以为当前活动的动画设置自定义时间/帧范围。假设您想将播放限制在10到20的帧范围内,您可以执行以下操作:

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

动画混合

您可以执行以下操作来混合2个动画剪辑:

creature_manager->SetBlending(true);
creature_manager->SetBlendingAnimations("default", "second");
creature_manager->SetBlendingFactor(0.5); // 在混合时,可选数值为从0到1

覆盖、修改骨骼位置

某些情况下,您需要直接修改角色的骨骼位置。例如,为了添加布娃娃物理效果,您可能希望骨骼位置与若干由弹簧或关节连接在一起的刚体保持一致。在这种情况下,您需要为自己的自定义要求设置骨骼位置,您可以执行以下操作。首先,编写自定义骨骼覆盖方法。在下面的例子中,修改了骨骼y方向上的位置:

// 这是一个如何使用回调函数修改您角色中的骨骼位置的示例
// 在这个例子中,我们将所有的骨骼在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);
    }
}

您还需要告诉Creature管理器使用您的自定义骨骼修改回调,如下所示:

// 如何注册回调函数来修改骨骼的示例
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);

角色实例和内存

如需实例一个角色的多个副本(如2条龙),您可以创建这样的动画:

// 创建和加载动画
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"));

然后,您可以继续创建一个新的Creature对象、一个新的Creature管理器对象和一个新的Creature渲染器对象。您可以添加创建的动画到您新创建的Creature管理器对象中,如下所示:

// 第二个Creature实例示例。这将告诉你如何加载第二个Creature。
// 因为第一和第二个生物共享动画数据,您最终将
// 节省内存。
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);

该示例和之前示例的区别在于:先创建动画,后将其添加到各自的Creature管理器中。这就意味着将为(最昂贵的)动画分配的内存存储在标准位置。多个Creature管理器对象将从Creature动画对象的公共池添加动画。

使用点缓存提高播放性能

如果您希望提高动画播放速度,也不想占用引擎资源,您可以在特定的动画上启用点缓存。

点缓存实际上直接存储动画的顶点,可以使播放不必占用引擎。在具有非常复杂的骨架和变形的角色绑骨上使用点缓存将非常高效。

要对特定动画启用点缓存,请执行以下操作:

my_creature_manager->MakePointCache("myAnimationName");

上述即为创建缓存的所有操作。请注意:缓存适用于特定的动画,而不是角色。这就意味着如果您实例化了多个角色,也只需使用该动画的单个缓存。即可节省内存并提升播放性能