• 游戏中的对象资源信息管理


    首先要说明的是资源格式。资源格式一般存放如下格式

     

    资源

     |

     --- 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 INIEXCEL?)。于是,对外提供了一个注册这些资源信息到内部的接口。而当我们上面的接口则变成了

    ISceneObject::AddPart(int resId);

    ISceneObject::AddEffect(int resId);

    看到了吗? 我并没有用modelId,effectId.说明什么呢,说明所有资源采用的是统一编号。 这样做貌似不太好。但这样在场景管理中就不再需要维护多种资源的资源表了。而上面这两个函数的实现,则是根据资源索引取得资源信息,然后就可以正式进行操作了。

     

    或许,我们可以把这个映射交给写逻辑的人处理,只是不知道他们愿不愿意。

    之所以写出来,是因为我在犹豫,到底哪种方案更好,或者有没有更好的方案来解决这个事情。。。。

  • 相关阅读:
    【译】.NET Core 3.0 中的新变化
    【译】最大限度地降低多线程 C# 代码的复杂性
    【wif系列】C#之单例模式(Singleton Pattern)最佳实践
    【译】在C#中实现单例模式
    【译】.NET 跨平台界面框架和为什么你首先要考虑再三
    WPF自定义空心文字
    WPF捕获未处理的异常
    C# 中 SQLite 使用介绍
    C# WebService动态调用
    Java实现将中文转成拼音和ASCII码
  • 原文地址:https://www.cnblogs.com/qilinzi/p/1940478.html
Copyright © 2020-2023  润新知