首页 > 代码库 > cocos2d-x模型加载的重构,我眼中的面向对象

cocos2d-x模型加载的重构,我眼中的面向对象

        这两天在写cocos2d-x加载ogre的mesh模型的东西,完成了一半了,还差动画文件没接进来。这篇文章并不是教如何加载模型文件的,因为那种东西没什么可教的,无非就是加载文件,然后解析出自己想要的顶点、关键帧等数据,最后给coco2d-x中的MeshData赋好值。这里我想表达一些我有关代码格式、代码设计、代码重构的想法,我会分一些有关或者无关的问题,来逐一分析。


一、为什么要支持多种模型格式?cocos2d-x自己定义一种新的格式c3d、c3t这样好不好?

       首先,我认为,一个成熟的游戏引擎不需要支持多种模型格式,至少从渲染和内部代码上是不需要的,因为那样没有意义。支持的少了,总会有遗漏的;支持的多了又会显得非常臃肿。我记得之前见过一个解析模型格式的开源库,静态库就有一百多兆。对一个游戏引擎来说这是完全没有意义的。

       但是这里有一个问题,就是cocos2d-x的3D部分并不成熟,并不具备竞争力。所以从引擎的发展角度上说,能够支持几种常见的格式是非常有必要的,这会让人感觉到简单易上手。比如fbx格式,支持这个就意味着unity很多模型可以直接用了;比如mesh格式,支持这个意味着《火炬之光》《天龙八部》的模型可以直接显示了。倒不是说鼓励盗用资源来说商业游戏,但是这种支持可以让新手和大众更容易接受这个引擎的3D能力。作为一个不成熟的引擎,这方面的妥协才意味着成熟的表现。

       最后,我不得不说,cocos2d-x的3D部分,至少是模型加载部分设计的很丑陋,很凌乱,而且写的很不灵活。我一直很好奇,那些做开源游戏引擎或者是公司内部商业游戏引擎的开发者难道都喜欢闭门造车吗? 我们很容易就可以找到值得参考借鉴的解决方案。我也很好奇,为什么一个公司可以花费三五年时间来研发一个游戏引擎,而且一直给人一种半成品的感觉,一直人骂?要是我的话,直接把OGRE封装一下,用一个月时间商讨需要提供的接口和特性,基础都摆在那里了,要是一年时间还开发不出一个渲染引擎的话,那我就不做程序员了。诚然,但凡有些技术的,都会认为自己的设计才是漂亮的,其他人写的东西都跟屎一样。但是没有成功的产品来支撑的话,一些吐槽都是大言不惭。

       我自己也很喜欢吐槽这个吐槽那个,包括这篇文章说白了也是吐槽,但是我承认自己是个好高骛远的屌丝。而那些拿着百万年薪的大牛,也做同样的事情,那他们是否值得钦佩?

       总结一下,这部分说的就是,其实cocos2d-x没有必要定义新的模型格式的,直接支持流行的三五种格式更加有利于推广,这样就不用操心和维护相应的工具链了。而且我确实没有在其3D部分看到什么技术优势,所以更应该放下身段走平民路线。


二、项目开发中的势与术

       这个是我在看网络小说中看到的。所谓势就是兵势、国势,所谓术就是战术、奇术。 很多名将都有经典的战术,而且这些“计谋”确实引导了胜利。但是这些经典战术之所以流芳百世就是因为他们罕见,九死一生,以弱胜强。然而真正伟大的统帅永远会让自己站在优势的一方而不是弱势的一方。“势”上的失败需要用更多的“术”上的成功来弥补,而这其中只要有一个“术”失败了,那就全盘皆输。

       引申到游戏开发中,我的观点就是:方向要选对,目标要明确,否则再牛的人也无法力挽狂澜。

       很多牛人,技术上无可指摘,但是看不清楚方向。遇到问题总会想着用自己的技术来解决,或者总是认为自己的解决方案才是最好的,这也是很多牛人喜欢自己造轮子的原因,因为他们确实可以在其他轮子上找到无数缺点。他们造出了完美的轮子,成为了“专家”,获得了名望,但是却没有让他们造的汽车大卖。这在我看来就是方向的错误。 当然,再如何说,他们的成就也是我辈凡人高瞻仰止的。


三、关于面向对象和设计模式

       这个是学c++的基础,也是面试中最常问的东西。这里并不是说要写一篇论文《面向对象-从理论到实践》,而是说说我对于漂亮代码的看法。

       在上大学的时候我问过一个牛人学长,“经常看书中写优美的代码,那究竟什么样的代码是优美的?”。他没有回答出来。 这个问题应该是看得代码多了,写的代码多了,才会有一些体会,而且还是自己的体会。 每个人的审美观不同,有的人喜欢引用各种模式,来让自己的设计无比灵活;有的人喜欢炫耀各种技术,来让代码显得无比高端;有的人喜欢展示自己的基础多么扎实,写的代码非常符合教科书的条款。

       (待续)


四、这就是重构

       下面这个例子就是标题说的重构,看着很简单,但是这就是我们实际开发中需要的技能。 我们需要的不是对照着108个设计模式,套到我们的项目中,而是看到“代码的坏味道”,然后修正它们。

       比如现在cocos2d-x的模型加载是在一个Bundle3D类里面,加载时根据文件扩展名和文件头调用对应的函数,比如loadMeshData loadMeshDataJson等等。Sprite3D文件中也有根据扩展名来判断是用Bundle3D来读取,还是用ObjLoader来读取。

       实际上一个小的改变就可以让这块儿的代码变得灵活起来。

       设计一个Bundle3DLoader,然后支持哪种模型的加载就对应实现一个Loader,比如Bundle3DMeshLoader,Bundle3DFbxLoader等等。一个Factory负责管理Loader。外部使用Factory获取到对应的loader,然后调用loadMesh、loadMaterial等函数来完成实际模型的加载。

       示例如下:

class Bundle3DLoader
{
public:
	virtual bool isSupportFile(const std::string filePath) = 0;
	virtual void reset();

	virtual bool load(const std::string& path);
	virtual bool loadMeshData(const std::string& id, MeshData* meshdata);
	virtual bool loadSkinData(const std::string& id, SkinData* skindata);
	virtual bool loadMaterialData(const std::string& id, MaterialData* materialdata);
	virtual bool loadAnimationData(const std::string& id, Animation3DData* animationdata);

	virtual bool loadMeshDatas(MeshDatas& meshdatas);
	virtual bool loadNodes(NodeDatas& nodedatas);
	virtual bool loadMaterials(MaterialDatas& materialdatas);

	static std::string getFileExt(const std::string filePath);
};

       这样,添加一个新的模型格式就直接实现一个对应的Loader就可以了,而且还可以在引擎外部实现,然后通过Factory来添加管理。

       这里没有什么高级的模式,但是确确实实的在改变代码的可读性。对应一个文件一个模型加载的实现,要比3000行的Bundle3D可读性大大提高。 这就是面向对象,这就是重构,它不神秘,它很简单,它为工程服务。凡是写了十几个类,一大坨继承关系来实现一个所谓的面向对象,都不是好对象。


cocos2d-x模型加载的重构,我眼中的面向对象