首先要说明的是资源格式。资源格式一般存放如下格式
资源
|
--- part0
|
----part1
|
----part2
|
...
一个很好的例子就是模型信息文件,在一个模型信息文件中,存放了整个物体的所有部分,每一个部分又是单独的信息。这样做的目的在于游戏中的换装。
还有一个例子就是特效文件,比如将一个门派的技能做为一个特效文件,而这个特效文件中包含了所有技能的技能特效。又或者说,当我们要实现不同等级的技能有不同特效的时候,就可以将一个技能做一个特效文件,而特效文件中存放的则是不同的等级的特效。
接下来就是要说明逻辑和渲染部分的划分。有些游戏是直接将渲染和逻辑嵌在一起的。这样做我觉得非常不好,不便于后期扩充。加上渲染部分的东西在逻辑里乱飞,也会干扰清析度。并且,你不能假设所有写逻辑的同伴都喜欢和图形沾亲带故的。因为有必要将渲染部分和逻辑划分开来。
一种常见的分法就是将渲染部分交给场景管理器,而由场景管理器引出一个场景对象。好比Ogre中的Entity 这个Entity是挂接在场景切点上的。 在这个Entity中,就包含了我们需要显示和绘制的东西,如模型,特效,位置,材质等等东西。逻辑在使用的时候,只需让逻辑对象派生(Class导出方式),或者嵌入(导出接口方式)这个场景对象,即可实现信息的共享。我采用的是接口导出方式。
class ISceneObject;
class CGameObject
{
...
ISceneObject *mSceneObject;
};
划分情况就是如此了。那我们如何与场景对象接口通信呢。场景对象接口需要提供一些什么操作呢? 那些改变基础属性的,
如位置,方向,大小,移动,控制动画等都是很直观的。 那我们如何改变一个场景对象的渲染信息? 改变这些渲染信息的时候,又应该以什么样的方式来进行?
在此我们想到,逻辑中,一个对象都有他自身的各种属性,如果这个对象需要被渲染,肯定也需要一些渲染信息,如采用什么哪些模型的part,模型上有哪些特效等。 直接的方案就是按照上面讲的资源划分方式提供如下信息表。
struct SRenderInfo
{
TCHAR[64] szResFile;//存储信息的文件名
TCHAR[64] szResName;//信息文件中的资源名
};
那么,当我们想要为场景中的一个人物增加一件装备的时候,我们就可以像这样提供一个接口。ISceneObject::AddPart(const
TCHAR* modelInfoFile,const TCHAR* partName)=0;
而假设现在我有一个“肩膀”,肩膀是“天尊套装”中的“肩膀”。那么这个装备的SRenderInfo mRenderInfo结构的值是{_TEXT("天尊套装"),_TEXT("肩膀")} 我们调用mSceneObject->AddPart(mRenderInfo->szResFile,mRenderInfo->szResName);便可以修改一个模型的肩膀,外部并不用关心这个模型的任何渲染过程。
那我们要增加一个特效呢? 要知道,特效是随时都在调用的,当你释放一个技能的时候,特效被增加,当你技能放完了,特效又会被删除。 (有个例外的情况就是光环类技能,这种技能的特效是会随着技能的关闭才删除的)。 总的来说,我们就需要ISceneObject::AddEffect(const TCHAR* effectFile,const TCHAR* effectName)=0;接口。
那我们或许会想做些优化,比如当我穿戴同样的装备的时候,就可以不用再调用AddPart了。嗯,或许你会说这种东西用不着优化,那好吧。那特效呢? 成十成百的人放特效的时候,如果他们都总是按着一个技能不停的放。你觉得会不会有必要呢?
减少了特效的切换,减少了许多API的调用,模型不用重新计算他的特效信息,不需要重新挂接。 或许,可以用字符串比较的方式来断定。比如们可以在AddEffect内部检查是不是已经有相同的特效存在了,是不是在同一位置。。这样我们就可以不用做更多的工作。当然,这样是可以的。 而外部呢,你有信息在字符串乱飞的同时保证所有东西都正确吗?
其实上面的解释都不足以让我搬出下面的东西,但是有时候它会让程序写着简单。
想像一下,在配置文件中, 我们总是会给资源一个索引,然后才是他真正的内容。我们很少看到一个东西用字符串来索引的,当然,也有,我没有说没有。我们是不是可以试着将所有信息存放起来,然后用一个索引来索引它们呢? 比如现在有50个玩家出现在你的周围,而他们穿的装备中有50%是共同的,如果我们采用刚刚的方式,就会出现许多个sizeof(TCHAR)*64*2.并且,装备模版,特效模版的信息
如果没有和他们放一起,会是多么复杂的一件事。 但是,如果通过索引的话,仅仅是一个16位索引,就可以解决问题。其实我们一开始就可以这样做,除了模型显示信息外,还有一些基础信息都是共同的,这就是我们经常提到的装备模版。 当我们采用索引后,一切就变得简单了。 用索引索引出这些信息,再传给场景对象,看起来多么容易的一件事啊。
其实我现在不是这样做的,我是在场景管理内部做了一个资源信息表,这个表不管其它的,只负责资源ID,资源文件和资源部分的对应关系。 而场景管理是不会加载任何的资源信息的。 因为我根本不知道一个游戏的资源配置文件会写成什么样,会用什么写(XML? INI,EXCEL?)。于是,对外提供了一个注册这些资源信息到内部的接口。而当我们上面的接口则变成了
ISceneObject::AddPart(int resId);
ISceneObject::AddEffect(int resId);
看到了吗? 我并没有用modelId,和effectId.说明什么呢,说明所有资源采用的是统一编号。 这样做貌似不太好。但这样在场景管理中就不再需要维护多种资源的资源表了。而上面这两个函数的实现,则是根据资源索引取得资源信息,然后就可以正式进行操作了。
或许,我们可以把这个映射交给写逻辑的人处理,只是不知道他们愿不愿意。
之所以写出来,是因为我在犹豫,到底哪种方案更好,或者有没有更好的方案来解决这个事情。。。。