cocos2d-x 如何管理游戏资源

      在游戏的开发过程中,前期的规划 往往比 后期的“优化”更为重要!比如多分辨率适配,如果前期没有规划好,可能导致的情况是,画面只在当前测试开发机或者一部分机型正常显示。做了多套资源适配,可以使在合适的机型使用对应的图片资源,避免在高清屏幕使用低质量的图片,在低分辨率屏幕因为图片太大而浪费硬件资源。机制与策略分离,可以让你设计出简单有效的接口。模块化的设计可以让你组织好各种逻辑流程,条理分明 ~ 前期的规划工作可以有很多,一叶也在摸索之中,以使游戏的开发尽量变的简单灵活且可控。最简单的也是最容易忽略的地方,跟我们打交道最多的要数精灵了,从图片创建一个精灵,很简单的开端,将以此展开行动 ~

      本文使用 Cocos2d-3.0alpha1 版本,创建了一个 C++ 项目,介绍在 C++ 中,如何处理资源相关的内容,如果读者使用脚本,也可以参考本文中资源管理理念而忽略语言特性,你可以在Github1 上面看到本文所有源码。(注: 本博文略有更改)


名字系统



      也许你可称之为 “命名规范”,但显然它无法表达我所想说的内容,很多人在创建精灵的时候喜欢直接使用资源名称,而没有任何定义,这是一个不好的习惯,如果游戏资源不存在,缺少,或者修改名字,如此你需要在多出引用的地方一一修改。游戏开发中的变数总是无法预料,合理的“名字系统”可以节省很多人力。

我们设定一个文件,这里名为 “Resources.h” 的文件,在其中定义所有的资源名称,在游戏开发中,尽量只 使用此处的名称,如图片名称,字体名称,声音资源等。这样做有以下好处,只是简单说几点:

  • 如果对资源做出修改,我们可以修改此处定义,以保证同步,避免缺失,命名错误,错误引用等问题
  • 在图片名定义修改时,编译器会编译出错,并自动帮助我们 “找” 出引用的地方,方便修改
  • 由于有常量定义的缘故,我们的 IDE 会自动补全所有以定义变量名称,减少出错的可能,提高效率


使用脚本来自动生成文件常量定义显然是个行之有效的途径,这种机械式的操作交给脚本就行了,它总能出色的完成任务,首先来看看项目的 Resources 目录内容:


            cocos2d-x 如何管理游戏资源_第1张图片


      以上是资源文件,那么通过脚本所生成的 “Resources.h” 文件又是什么样子的呢,脚本在 Github 仓库中可以找到(注意:资源名中最好不要有空格,以免留下“隐患”):

#ifndef cocos2dx_resource_Resources_h
#define cocos2dx_resource_Resources_h

#include <iostream>
#include <vector>
#include "cocos2d.h"
using namespace std;


//using namespace std;
// search paths
//static const std::vector<std::string> searchPaths = {
//	"fonts",
//	"images",
//};



// files
static const char si_CloseNormal[]		 = "CloseNormal.png";
static const char si_CloseSelected[]		 = "CloseSelected.png";
static const char sjs_file_list[]		 = "file_list.json";
static const char si_HelloWorld[]		 = "HelloWorld.png";
static const char st_MarkerFelt[]		 = "Marker Felt.ttf";
static const char sp_ghosts[]		 = "ghosts.plist";
static const char si_ghosts[]		 = "ghosts.png";
static const char sp_grossini_family[]		 = "grossini_family.plist";
static const char si_grossini_family[]		 = "grossini_family.png";
static const char si_JungleLeft[]		 = "JungleLeft.png";

////// texture //////

// ghosts.plist
static const char si_child1[]		 = "child1.gif";
static const char si_father[]		 = "father.gif";
static const char si_sister1[]		 = "sister1.gif";
static const char si_sister2[]		 = "sister2.gif";

// grossini_family.plist
static const char si_grossini[]		 = "grossini.png";
static const char si_grossinis_sister1[]		 = "grossinis_sister1.png";
static const char si_grossinis_sister2[]		 = "grossinis_sister2.png";


// json key
static const char file_name[]		 = "file_name";
static const char file_index[]		 = "file_index";
static const char texture_name[]		 = "texture_name";
static const char texture_plist[]		 = "texture_plist";
static const char texture_image[]		 = "texture_image";

#endif

      看到通过脚本,我们生成了所有文件的常量定义,这让得我们可以在游戏中任意使用,但是请注意,这里生成的文件名称是没有包含路径的,所以在定义文件之前,也自动生成了目录列表 searchPaths,顾名思义,设定了一个目录列表,以便找寻资源,我们可以在程序的开始处使用 :

      CCFileUtils::sharedFileUtils()->setSearchPaths( searchPaths ); 

      来设定游戏的资源目录列表,这样我们就可以不用关心资源所在的目录了,你甚至可以根据需要合理的调整资源目录。

AppDelegate.cpp

        const char* directorys[] =
    {
    		"fonts", "images"
    };
    
    std::vector<std::string> searchPaths;
    
    for ( int i = sizeof(directorys)/sizeof(directorys[0])-1; i >= 0; i-- )
    {
         searchPaths.push_back(directorys[i]);
    }
    
    CCFileUtils::sharedFileUtils()->setSearchPaths( searchPaths );

注意:通过设置 searchPaths 可以让我们不用关系资源的路径所在,那么意味着资源名称必须唯一,否则可能会出现引用问题。其次,是如果使用了多套资源方案,请注意 searchPaths 的先后顺序关系。本文暂不考虑多套资源。关于忽略资源目录的做法,如果有不同看法者,欢迎留言讨论,对我来说,忽略路径是利大于弊的 ~

以上通过脚本自动生成了文件列表,但是这显然不够,我们看到资源当中有两张 打包 资源图片(可以使用 TexturePacker 对图片资源进行打包,具有占用更小空间,优化运行效率等诸多好处,后面还会介绍此点) plist 文件。我们当然也是需要使用打包中资源的,所以脚本需要能够自动解析 plist 文件,并提取出 TexturePacker 打包的资源名称,请看如下定义,同样是自动生成在 “Resources.h” 文件之中:

// ghosts.plist
static const char si_child1[]		 = "child1.gif";
static const char si_father[]		 = "father.gif";
static const char si_sister1[]		 = "sister1.gif";
static const char si_sister2[]		 = "sister2.gif";

// grossini_family.plist
static const char si_grossini[]		 = "grossini.png";
static const char si_grossinis_sister1[]		 = "grossinis_sister1.png";
此时我们就能用以下代码来创建精灵了,都引用了资源名称定义,并且使用两种方式创建了精灵:

    //直接由图片创建精灵
    CCSprite * hello = CCSprite::create(si_ghosts);
    hello->setPosition(ccp(size.width/2,size.height/2));
    this->addChild(hello);
    
    // 从打包资源创建精灵
    CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(sp_grossini_family, si_grossini_family);
    CCSprite * sprite = CCSprite::createWithSpriteFrameName(si_grossinis_sister1);
    sprite->setPosition(ccp(size.width/2,size.height/2));
    this->addChild(sprite);
      上面我们使用两种方式创建精灵,为什么会有两种方式?也许你可以看看  『子龙山人』 翻译的文章  『在cocos2d里面如何使用Texture Packer和像素格式来优化spritesheet』

      其中详细的介绍了图片资源打包优化的相关细节问题,一个游戏最多的就是图片资源,优化空间最大的也是图片资源,里面详细的介绍了优化图片资源占用空间 50% 以上,如何使游戏运行内存占用优化近 50%,以 cocos2d 为例,但 cocos2d-x 同样能够适用,而且能通过脚本自动打包。 所以合理的对图片资源进行打包优化是非常有必要的。但如何处理这个流程确实不好定夺,因为不同资源的使用方式不同,因为这两种方式的存在,导致我们编写代码的逻辑不同,这需要提前预定好,所以我们考虑如下开发流程:

      在游戏开发前,对所有资源打包后提供给 编写游戏人员,也就是说在写程序之前,游戏资源就已确定,那些以打包,哪些未打包都已经知道,如前面一样,通过两种方式创建精灵。但是这样的结果是,前期规定好了的,后期就无法改动,或者说很难改动,牵一发而动全身啊 ~ 这就需要加大 前期的规划 力度,以确保后期不会出现太大太多事与愿违的情形。这种情况下的 后期优化 将会非常蹩脚。况且加大前期规划的力度,可能会对整个项目的进程有所影响,如比编写人员的动工会稍缓,人力资源分配不合理。

      有关图片资源类型的“透明”处理,可参考http://blog.leafsoar.com/archives/2013/11-27.html


      若是使用脚本的朋友,实现方式类似的。

你可能感兴趣的:(C++,cocos2d-x)