游戏Entity设计不完全整理

在游戏引擎中,Entity通常被翻译成实体,也常用诸如GameObject、Actor、SimulationObject、Unit、Character等名字。相比于对图像声音引擎的热情,Entity层多年来一直备受冷遇,但最近几年随着大型游戏的发展,Entity层设计的重要性已经达到和图像声音的同等水平,而且已经出现了多种通用型Entity架构。当然,这里伴随着争议和分歧。

直接模式(The C Approach)
这是最早、最简单、也是任何人都能直接想到的模式。这种方式下一个Entity就是一个简单的struct:

struct Mob
{
int level, hp, mp, attack, …;
};

这种情况下往往需要对不同类型的Entity定义不同的struct,比如Player、Mob、Item、Doodad等。Entity的数据库可以直接使用现成的数据库系统或者从数据文件读取,比如csv文件。一般会选择Excel编辑数据库,然后导出csv。Excel的强大表格和统计计算功能是调节游戏数据平衡的得力工具。以致这种最古老而简单的Entity模式以强大的生命力一直活到现在。

那么为什么Developers会要去探索其他方式呢?最大的问题是这种方式下各种Entity完全不同质。比如Player、Mob、Doodad都有位置,都要做碰撞检测,但他们却是不同的类型,不能使用同一份代码;Player和Mob都有hp和mp,也有基本相同的处理逻辑……当然,这个可以用hack的方法解决:只要各个struct前若干个成员类型和顺序完全相同,就可以将指针cast成统一的一个Entity类型来处理。不过,任何人都能想到更好的办法,用类!

继承模式(Inheritage)
这也是Entity、GameObject等这些名字的由来,他们就是基类。于是我们可以得到一颗继承关系树,例如:

Entity

Character

Player

Mob

Missile

Laser

GuidedMissile

Item

Entity对象的逻辑大多也是直接包含在类里的。但也有人采用了数据对象和逻辑对象的分离,其好处是对相同的数据可以替换不同的逻辑。不过,个人比较怀疑这种分离是否值得,因为类的派生本身就是可以拥有不同的逻辑。

另外,Entity间的交互除了用标准的直接访问方式外,经常使用消息模式。消息模式和Windows的消息模式类似,Entity间通过消息交互。虽说窗口编程画了多年时间才摆脱了当年肥大的switch的消息处理模式,在Entity层使用消息模式还是有意义的,因为消息很容易广播且可以直接在网络上发送。执着于OO的人会将消息写成Command对象,但原理仍然是一样的。

同时,联网游戏出现,指针不能在不同的机器上标识对象,我们必须用稳定的ID来标识对象,于是我们有了EntityManager来分配ID和管理对象集合,或者叫GameObjectManager、ObjectManager等。这在一段时期内几乎成了完美的方案。

随着游戏内容的丰富性的迅速膨胀,传统的由程序员来实现游戏逻辑功能的模式也越来越力不从心。脚本语言的集成将大部分创意性工作从程序员的担子上拿了下来,交还给游戏设计人员。为了给脚本提供足够的控制权,Entity结构上必须有充分的灵活性。

数据驱动(Data-Driven)
现在有句很流行的话,“唯一不变的是变化。(The only constant is change.)”数据驱动使得变化对引擎的影响最小化。数据驱动不能算是一种独立的Entity模式,不如说它是一种设计思想,其目的就是将内容制作和游戏引擎的制作分离开来。与上面所说的填充Entity属性数据库的不同之处在于,它还要能通过数据来设计游戏逻辑。

游戏设计人员需要的一项最基本功能就是自定义人物属性,所以与其在类里写死属性成员,不如使用属性表(Attributes/Properties)。通常使用一个哈希表(Hashtable),或者std::map,或者Dictionary,或者就是个数组,随个人喜好,其实就是要一个key-value的映射表。然后为脚本和编辑器提供对属性表的操作。

动态的逻辑主要就靠脚本了,必须为脚本提供足够的事件和方法。个人推荐用Lua脚本,因为它是在游戏领域内用得最多的通用脚本语言,其引擎很小、速度很快、易于集成,尽管语法过于松散。不要迷信宣传去用庞大、极慢、难于集成的Python。为脚本提供事件,其实也就是调用脚本里的函数,这里如果使用了前面所述的消息模式,那么就只要调用一个脚本方法,传递不同的消息参数就行了。当然也有人觉得这样很丑陋而更愿意为不同的事件注册不同的函数。

当有了数据驱动后,Entity的继承树就基本失去意义了,因为一个Entity是什么已经不是程序里决定的了,而是通过数据和脚本设计出来的。但数据和脚本又不是全部,一个Entity的核心内容和需要高效处理的部分,如碰撞检测,还是要程序来完成。于是我们需要重新设计Entity类,困难的局面也就由此开始。

一个直观的想法是一个统一且唯一的Entity类,它包含了所有的基本功能,如显示、碰撞检测、运动、背包等,然后由数据决定哪些组件被启用。比如一个玩家角色可能会启用绝大部分组件,而一颗树只启用显示和碰撞检测组件。但也伴随着缺点:一、这个类太大了;二、对于树木等一些简单的Entity也要浪费其他组件的私有数据所占的内存。那么一个简单的折中是部分使用继承、部分使用数据定制。例如Entity只提供最基本的组件,再派生出CharactorEntity提供足够人物角色使用的组件。

组件模式(Component-Based Entity)
提到组件,那么很自然的就过渡到组件模式,就是把显示、运动、攻击、背包、队伍、声音等基本功能都做成独立的组件,由数据来决定向Entity里添加哪些组件。由此可以得到另外一个扩展,就是既然可以有引擎内置的组件,那就也可以有脚本制作的组件,实现脚本模块的复用。这种模式在GDC2002正式提出,到现在主流的引擎都有这种设计。

这种模式在理论上很完美,但实践上还是有不少疑问。最常见的问题就是组件间的依赖关系。理想情况下,各个组件是完全独立的,但实践中必然有所依赖。比如运动速度、攻击强度等和角色的基本属性有关,运动组件需要角色的包围盒来测试是否碰撞,AI组件需要分析角色当前状态和发出运动、攻击命令,角色动作状态变化时改变显示组件属性,攻击组件需要访问队伍信息组件以禁止攻击队友等等。处理这种依赖关系主要要解决两个问题:

<!--[if !supportLists]-->一、 <!--[endif]-->谁依赖谁。比如是敏捷属性改变而去修改移动速度,还是运动组件读取敏捷属性来计算移动速度。如果要游戏设计人员自由定义基本属性的话,就要选择前者,因为基本属性组件会是脚本组件。

<!--[if !supportLists]-->二、 <!--[endif]-->如何取得另一组件的指针/引用。常见的方法是给每个组件类型一个唯一ID,然后用该ID在Entity上查询注册了的组件,如果找到返回其指针/引用,否则返回null。当然,每次访问都做这个查询会很浪费CPU,如果Entity的组件不会在运行时动态添加删除的话(除非在编辑器里,否则很少有人会这么做),可以在Entity初始化后让每个组件缓存它所要用的其他组件指针。那么当所依赖的组件不存在怎么办,一般情况下都是无声地忽略。

当Entity由很多组件组成后,交互的消息需要发给每一个组件。这里又一次体现出消息机制的优势,你不需要在Entity里为每一个事件函数写一个loop来调用组件的相应事件函数。但这里也出现了一个问题,消息到达各个组件的顺序。很多时候这个顺序不会有什么影响,但个别时候不同的顺序会导致完全不同的逻辑发展方向。

此外,Entity的序列化存储也变得比较复杂,经典的Excel导出csv的模式难以奏效,因为这里需要结构化的存储,所以需要结构化的数据文件如XML来存储,或者完全用脚本来包含所有数据和构造Entity。

据个人经验,使用数据驱动的继承模式时很是向往组件模式,感觉上它一个非常自然的扩展方向,但顾忌其引入的额外的复杂性,尤其是需要游戏设计人员具有一定的编程能力,所以一直不敢全盘接过使用。但退一步,在引擎里仍然使用组件思想,但Entity的组件构成在编译时固定,可以达到某种妥协,这和采用继承与数据驱动的折中类似。

混入模式(Mix-in)
这是又一种常见的折中模式,即使用C++的多重继承,将各个组件类混入一个Entity类。如:

class Mob: public GameObject, public Renderable, public Movable, public Attackable
{

}

这种方法因其简单而且非常符合多重继承设计思想而被很多引擎采用。当然缺点也是只能在支持多重继承的语言里使用,而且当组件很丰富时,dynamic_cast就变成一个代价高昂的操作。 

功能性与复杂性(Functionality vs Complexity)
编程领域最古老的原则之一就是要“简单快速”。但随着问题的复杂化,程序也随之变得越来越复杂。好的方法应该能有效的降低或隐藏复杂性。但是,没有不带副作用的药(No silver bullet.),在取得更强大的功能的同时总会带来额外的复杂性。我们需要做出权衡,在必要时牺牲一些功能,也就是要估算性价比。

一般游戏内容制作人员不会或不太会编程,编程人员也不善于游戏的内容创造和数值平衡,过于复杂的系统会导致需要两面兼顾的人才,会大大增加做出一款游戏的难度。

时间: 2024-12-11 18:37:04

游戏Entity设计不完全整理的相关文章

浅谈游戏WEB设计的一些细节问题

本期特约:海外美术团队 Lyson 1.浏览器的差别跟用户分辨率差别是我们首先考虑的问题: 2.游戏页面设计,需贴主题.游戏背景等主线关系: 3.布局跟文案很重要: 一.浏览器的差别跟用户分辨率差别是我们首先考虑的问题: 首先在这里想要再次为新人提下,现在霸占互联网最小分辨率的还会是http://www.aliyun.com/zixun/aggregation/12560.html">1024用户(特别是中国),加之IE.FF主流浏览器的比例,由此特地整理了一个解说小图. 点击查看原始大小

浅谈——韩国游戏网站设计

一直以来游戏网站带来的印象是整体的,华丽的,因为游戏的本质就是多彩美丽地艺术品,每一款游戏本身的独特元素,造就了一个个具有独特魅力的游戏网站.网络游戏在中国的发展是迅速的,千年,传奇,天堂,MU,RO,很多韩国游戏在中国很受欢迎,本土的游戏也深受韩国游戏的影响和启发,游戏网站也有相同的发展经历,从韩国风格的素材,模板,到构图,色彩,插画创意等. 首尔被称为世界设计中心之一,有叹为观止的建筑,令人羡慕的工业设计,融入日常生活的艺术,有如此多的美学作为标杆,网站设计自然也有美丽之处,更重要之处是有浓

J2ME伪高手先锋开讲—扫雷游戏的设计

设计 J2ME伪高手先锋开讲--扫雷游戏的设计 首先我要装得像高手一样,来假装把系统稍微分析一下. 一般,按照java得开发模式,这种程序一般是分为三个模块来开发. 如下三个: 一个程序运作的主文件,也就是一个midlet的继承: 一个界面的表示类,也就是一个canvas的继承,界面上应该有些菜单,如new.exit 什么的,那就应该要 implements一个 commandListener消息监听类(大家可以把java的消息监听理解为一个线程,一直像倭寇那样对看得顺眼的东西虎视耽耽,当然这里

设计参考:12个设计精彩的国外游戏网站设计

现在"国产"网游已经有很多经典之作了,但是无可否认,国产游戏与国际巨头还是有些差距的,游戏网站亦是如此.游戏网站设计对游戏本身是很重要的,好的游戏网站至少要能够体现出游戏的特色和优势.一个设计出色的游戏网站,无疑能够吸引更多的用户来尝试游戏. 今天网页教学网从国外大量的游戏网站中精选出12个特别出色的网站,供大家欣赏,也希望能给大家的设计带来某些灵感. 暗黑 3 雷顿教授与不可思议的城镇 大坏蛋 吉他英雄:非凡精选 最终幻想XIII 拳无虚发!! 网球大满贯 极速竞赛 模拟人生 3 铁

游戏开发设计要注意哪些问题

  游戏开发设计必须注意哪些问题?游戏开发设计的黄金法则!未来是一个游戏的时代,所以现在很多人都开始从事游戏开发这方面的工作,可是在当下的时代新的游戏不断的被开发出来,可是有多少游戏能够真的吸引到玩家呢,究竟其中存在什么问题,下面我们一起来看一下前辈们总结出的相关经验吧. 1.避免游戏登录时的频繁的点击 在新游戏出来是许多的玩家都想进去看下游戏的可玩程度,之前大家登录是却需要点击好多的点击,大家的激情就被你那频繁的点击给抹掉一半了,等到进去是发下游戏不是很理想时,基本上激情给抹灭的差不多了.一般

游戏新手引导设计(上)

在游戏市场中,游戏产品琳琅满目,特色玩法五花八门,但新手引导却千篇一律.枯燥的引导大幅的降低了用户体验,导致用户留存率的下降,因此如何 把填鸭式的教学变得新颖有趣,如何把冗长的引导时间变得短小精湛,也是游戏交互设计的探索方向.通过对现有一些游戏的体验,我总结了一点设计新手引导的思 路,希望对大家有所帮助. 如何提高新手引导的引导效率,那就要从引导内容入手,因为我们可以通过优化引导内容来提高引导效率. 一.从产品功能角度分析引导内容 需要引导的内容可以分为两大类:玩家必须掌握的基本技能和吸引玩家的

游戏新手引导设计(中)

上一篇游戏新手引导设计(上)我们介绍了如何通过分析产品特点和目标用户的能力来对新手引导的内容进行"瘦身",这次我们就来探讨一下如何将瘦身过的内容准确的传递给用户,并且在引导过程中激发用户的参与热情,即:从引导时机.提示样式.分级开放和创造需求,四个方面分享一下如何设计新手引导的形式. 一.引导时机-在有需求时引导 玩过游戏的人都知道,游戏里的新手引导分为强制引导和情景引导两种,强制引导是指用户在进入游戏时无法跳过的引导方法,而情景引导则是当用户使用某些功能时出现的引导.很多人都喜欢情景

游戏新手引导设计:新手引导标准与检查方法(下)

前面我们分享了游戏新手引导的设计经验 -- 游戏新手引导设计(上).游戏新手引导设计(中),这次我想和大家分享一下新手引导的检查方法.说起检查方法,就要从检查标准谈起. 一. 评价新手引导的标准 游戏新手引导的设计目的是通过帮助用户掌握游戏玩法,从而提高留存率.由于留存率一直以来都是一个包含众多因素,剪不断理还乱的事情,因此想用它去检查新手引导确实有些难度.既然新手引导实现提升留存的手段是帮助用户掌握游戏玩法,所以只要用户掌握了游戏玩法,也可以说是实现了新手引导的设计目的.基于这种理念,我们可以

国内游戏专题设计点评

  我想肯定有人会说,我黑完了京东,又打起游戏厂商的主意了.呵呵,很不好意思,这些资料和文章大纲,我是在2014年3月份就准备好了,只是由于工(tuo)作(yan)太(zheng)忙.所以才一致没有写而已. 自己看截图,都是三月份的专题,谁这么有本(wu)事(liao)在11月份截到3月份的各大厂商的专题页面这么屌. 不得不说一下,之前三月份截图的那些作品,可以吐槽的点还是非常多的,经过大半年时间,很多公司已经明显慢慢赶上了腾 讯的步伐,虽然还有差距,但是进步明显.尤其是网易,当时藏地传奇第一版