— AI分享站

Archive
2011-10 Monthly archive

football city stars online logo

官方网站由此进入

《全民足球》是育碧开发的首款休闲类网络游戏。游戏以街头足球为主题,基于城市街区的真实地图概念,支持8名玩家进行同场竞技。游戏拥有独一无二的角色扮演加足球竞技的游戏模式,玩家可创建各具特点的角色球员,通过比赛和任务,提升其属性技能和天赋,获取奖励和成就。玩家并可基于所在城市街区创建足球俱乐部,和同城玩家一起创造辉煌。

拥有高品质全物理运算的核心游戏性,支持2对2, 3对3和4对4玩家间比赛,3大游戏模式: 单人训练, 自由对战和组队对战,守门员为AI。

玩家可以按自己喜好创建球员角色,自定义项包括性别、场上位置、惯用脚、身高、肤色、脸型、发型和着装等。

玩家也可以在游戏内商店购买其他服装和形象来装扮角色。

独一无二的角色扮演+足球游戏模式,3个球员职业: 前锋, 中场和后卫,6大球员属性: 力量, 速度, 传球, 射门, 带球和防守,每个只有拥有各具特色的技能和天赋。

所有的角色系统均可按玩家喜好升级。

团队配合可以增加士气能量。当士气槽积满后, 会触发团队高潮时刻,在团队高潮时刻, 球员属性会大幅提升, 部分技能也会触发更强大更炫酷的动作。

任务系统有一系列目标任务让玩家在了解游戏的同时获得经验和游戏币。主线剧情任务帮助玩家从玩家从普通球员成为一个球星。

真实地图系统在游戏中内建了和真实世界一致的地图,让玩家更容易和同城同区的其他玩家进行游戏。

玩家从自己所在的街区开始游戏, 通过角色升级逐步探索所在城市乃至世界。

其他社交功能包括: 好友系统, 聊天, 邮箱, 足球俱乐部系统等。

玩家可以在游戏内商店购买服装, 道具, 技能等物品,在购买服装前可以进行试穿。玩家同样可以把商品作为礼物赠送给其他玩家。

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More

最近在做项目前期的一些调研的工作,研究并参考了几个引擎和框架的设计,包括内部引擎,商业引擎,和开源引擎,通过比较和学习后,觉得对于游戏中的实体实现,用“组合”的设计模式会比用“继承”的更为便利,想到我早些时候的一个项目里用到的一个引擎,也是实现了组合式的实体,而且对于AI程序员来说也是和“游戏实体”打交道最多的,一个好的设计可以大大的提高代码的质量和可维护性。所以,我觉得很有必要在这里记录一下,也和大家一起分享一下我的一些心得。

在学习面向对象编程的时候,一个很重要的概念就是“万物皆对象”,我们可以把现实世界的物体抽象成一个个的Object,并且通过继承的方式实现多样化的对象集。这个是面向对象编程的一些很重要的概念。我想学过的同学都应该对此非常的熟悉。

游戏中也是一样的,一般游戏内都会抽象出一个称之为“游戏实体”(Entity)的类,当然也有直接叫Object的,不管名字怎么样,概念上是相通的,总之都是对于游戏中可能的任何物体一种提炼。在引擎中用继承的方式扩展Entity是一种比较常见和直观的方式,可以充分利用到面向对象编程中的多态的优势,虽然可能大家都比较熟悉这种方式,但我想还是用一个比较简单的例子来帮助理解,假设我们仅仅做三个类层次的话,可以从Entity这个基类中先继承出,静态实体(Static Entity)和动态实体(Dynamic Entity),静态实体表示没有运动信息的实体,比如一些静态的建筑,地图的网格等等,动态实体表示带运动信息的实体,比如人物,机械,怪物等等,这个作为第二个层次,最下面的一个层次可以是具体的物件,就像我上面所具的那些例子。

继承的好处是直观,而且分工合作也不错,大家的协作可以互不干扰,但“继承式”的实体结构有一个问题,我想用过此类模式的同学应该也有所体会,就是随着开发的进行,基类的“体积”会日益庞大,因为当我们发现子类间有一些共通的地方时,我们可以选择的办法之一,就是把这个共通的地方写到他们共有的基类里面去,我们用上面的例子举例的话,当“人物”和“怪物”有一些属性是共通的时候,我们就不得不把这些代码移到“动态实体”这个类中,当然这还不是最糟,当“人物”和“建筑”有共通的东西时,我们就只能把代码写到最高层的“Entity”中了,而且这些共通的代码可能“怪物”这个类并不需要,但因为它也继承自Entity,所以也“被迫”的包含了这些代码。所以基类代码的可维护性就变的很差,虽然,好的继承关系的设计可以一定程度上缓解这样的问题,但并不能从根本上解决。

为了解决这样的问题,所以,现在很多的引擎架构里,都提出了“组合式”实体的概念,“组合式”的概念就类似与小孩搭积木,通过用不同部件来“组合”出不同的物体。

比如,对于“静态的建筑”这个实体来说,它可以由“空间属性模块”,“显示模块”组成,“空间属性模块”包括了位置,朝向等信息,“显示模块”包括了这个建筑的模型,贴图等和渲染相关的信息。那对于“人物”这个实体来说,他的模块就更多了,除了“空间属性模块”,“显示模块”,还会包括,“人物AI模块”,“动画模块”,“物理模块”等等。对于“怪物”来说,它可能包含了和“人物”差不多的信息,但我们会给它一个“怪物AI的模块,而不是“人物AI模块”。但这时,也许游戏设计师需要一个“高级的人形怪物”,它更聪明,需要有像人一样的AI,如果按照原来“继承式”的设计方式,我们可能需要从“怪物”这个类里继承,然后再从“人物”这个类里拷贝AI部分的代码,或者将这部分代码移到“动态实体”类中,以便于在两个类中共用,但在“组合式”的设计中,我们要做的,仅仅是将那些模块重新组合,用“人物的AI模块”来替代原来的“怪物AI模块”,这样我们就得到了一个全新的实体。

这就是“组合式”实体概念,这些用来“组合”的元件,称之为“组件(Component)”,或者“轨道(Track)”,这些组件是可继承的,所以从本质上来说,它将基于实体的继承,转移到了“组件”的继承上,将实体和属性分开,达到了简化实体类的作用。明白了它的概念,那从实现上来说就更简单了,我们可以在实体类中,包含一个存有组件基类指针的数组,然后在构建实体的时候,将相关的组件添加进来就可以了。然后在更新实体的时候,将所有在这个实体上的组件一并更新即可,代码我就不在这里写了。

“组件式”实体的概念和实现并不是很复杂,但效果却是惊人的,它用一个简单的设计模式,使得代码更易维护,而且,不同的程序员可以更合理的分工实现各自的组件部分。像AI程序员,就可以专注与对于AI组件的开发了,比如,可以做一系列的AI组件,对应不同的难度的AI,只要将这些组件和实体相连,就可以实现虽然怪物的外观相同(因为“显示组件”是一样的),但AI截然不同的效果了。对于3D程序员也是这样,比如,我可以先做一个真实效果的渲染组件,但可能我还想试试卡通渲染效果,那只要再实现一个这样的组件,将原来那个替换掉就可以了。

当然,“组合式”实体的开发中,也有些问题值得大家思考,最重要的一个问题就是,“如何在组件间传递数据?",最实际的一个例子就是,“空间组件”中存有实体的位置信息,那其他的组件开发者肯定是需要得到这些信息的,所以在组件间传递数据的功能是必不可少的,我不推荐直接开放诸如GetComponent这种直接得到组件指针的接口,那如何安全而有效的传递数据呢,这个仁者见仁,智者见智问题就留给大家讨论了。

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More

第一部分

上次我们说到,游戏中的运动系统一般有两种方式,“动画配合运动”以及“运动配合动画”。对于第一种方式,由于是采用运动函数或者经验数据表,所以可以很简单的将“未来时间”带入其中,来预测未来某一时刻的运动结果。但对于第二种情况,因为所有的运动结果都是从动画中取得的,如果不知道动画信息,就无法知道相应的运动结果,所以,简单的将时间带入是不能作出预测的,对于这样的情况,如果我们要做预测的话,就不得不将动画的因素考虑在里面。

举个简单的例子,一个人做一个跑动中转身停下的行为,假设他当前时刻T1,处于A1位置,速度是V1,朝向是F1(假设和速度方向一致),他的目标状态是速度是0(没有速度),朝向是F2,位置不指定。这时当我们给出未来时间T2,应该怎样来做这个运动预测呢?

PredictableLocomotionEx

和第一种运动系统不同的是,这里我们需要一个额外的模块,称之为“动画选择模块”。在我们实际要去完成这个运动目标的时候,我们会用“动画选择模块”去选出每一步的动画,比如最直观就是“直线跑”,然后会产生一个“急停”使速度从V1到0,最后是“原地转身”使朝向从F1到F2,当这些动画做完后,我们就可以得到这个人最终的“目标位置”。由于是运动配合动画的,所以对于他的最终位置,我们完全是靠这些动画实际的运行结果而得到的。

PredictableLocomotionAnimseq

当我们要做预测的时候,显然我们也是需要用“动画选择模块”做预先的动画预测的,所以,在设计可预测的运动系统的时候,就需要将“动画选择模块”独立出来。如下图所示:

PredictableLocomotionSystem

动画选择模块的输入是“当前状态”和“目标状态”,输出是一个动画的序列,这就有点像以前我们讨论过的“计划器”(Planner),相当于为我们的运动做了一个计划(对于这样的计划器的实现,就不在这次的讨论中了)。有了这样的计划后,当我们将时间T2带入,就可以通过读取动画信息的方式来获得T2时刻的运动信息了,见下图,AT指动画的时间(Animation Duration)

PredictableLocomotionTimeSeq

对于给定的“当前状态”和“目标状态”,都会有一组动画序列与之对应,所以如果是基于目前的运动状态所作的预测,那我们可以重用我们已有的动画序列来提高效率。如果是对于假想的运动状态的预测,那我们就需要用“动画选择模块”重新做一个新的动画序列,然后再得出未来时间的运动状态,这也是为什么我们需要独立的“动画选择模块”的原因,但如果频繁做较长步骤的假想预测,可能会产生一些性能上的问题,这是需要注意的地方,当然,这也取决于“动画选择模块”实现的复杂度和动画的丰富程度,比如在上个例子中,如果我们有一个“急停转身”的动画,那我们就可以减少动画序列的个数,也就是减少了计划的步长了。

另外,在实践中,我们会采用两种运动方式混用的情况,比如对于“直线跑”,我们会用运动函数来实现,而对于“转身”,“急停”这样的行为,我们会采用第二种方式来实现,对于这样的运动系统,也可以用到这样的“计划器”,只是这个计划中的某一步换用函数方式罢了,对于上面例子,我们可以参考下图:

PredictableLocomotionTimeSeq2

除了预测未来时间的运动状态外,可能我们还会预测到达某一个状态所要用到的时间,当我们有了上面的系统后,这也会非常容易做到,比如上面的例子,如果我们要预测他到“目标状态”需要多少时间,那我们只需要把动画序列中每一个动画的时间求和就可以了,T = T1 + AT(Run) + AT(Scram) + AT(Turn)。

可预测的运动系统对于某些游戏可能是一个非常重要的系统,希望上面的讨论对大家有所帮助。

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More

前端时间举家出游了一次,加上国庆期间一直跑东跑西,博客的更新就一直没跟上,距上一篇文章也是好久了呢,有时感觉一个人维护也有点小累,所以如果大家有好的想法,好的分享,也可以投稿给我,我想能有这样一个分享的平台,让志同道合的朋友一起讨论学习,也算是为中国游戏技术的发展贡献点绵薄之力,虽然我是付不起稿费的,但看着文章的浏览数和回复也是挺有成就感的事情,对吧 :)

好,言归正传,这次想大家来讨论讨论如何来做一个“可预测的运动系统”。首先来说说什么是游戏里的运动系统。

对于游戏里某一个智能体(就是带AI的物体),当AI决策结束,并且告诉行为层要干什么,行为层就会让它“动”起来,动的过程中可能是没有动画的,比如一辆车会前进,会后退,也可能是有动画的,比如一个人会走,会跳。显然一辆车的运动方式和一个人的运动方式是不同,那具体控制它如何运动,就是有专门的“运动系统”来负责了。

严格意义上来说,“运动系统”的实现并不在AI的范畴内,而是属于“游戏物理引擎”部分,但在某些时候,作为AI程序员,会对运动系统提出一些特殊的要求,比如今天我们会说到的“可预测性”,这些要求会用来来辅助AI系统的决策。在我现在的项目中,我们对预测的要求相当高,希望在决策的时候能做一个精确的运动预测,但比较遗憾的是,现有的引擎由于这样那样的原因,并不能提供这样一个精准预测,预测结果和运行结果存在较大的差距,所以我在抱怨的同时(因为我用的最多嘛),也在思考如何做这样一个系统,把想到的一些东西也记录在这里。

一个比较简单的“可预测的运动系统”就是匀速直线运动,我们可以根据匀速直线运动的公式,来预测将来任意时刻的位置,速度等运动信息。当然,游戏里的运动系统要复杂的多。一般来说,游戏里的运动可以大致分为两种:

  • 动画配合运动
  • 运动配合动画

第一种就是运动和动画是分离的,我可以用任何的运动函数来移动物体,动画只是配上去的效果而已,这种方式有动画和没有动画,对于它的运动效果来说是不变的,动画只是一种锦上添花的表现,因为运动方式都是自己定义的,所以我们可以很容易控制它运动效果,但缺点是,可能会产生“滑步”的现象,就是人感觉在太空漫步一样,可以想象我们用跑步的速度配一个走路动画的效果。

第二种是物体的运动效果是跟着动画的,如果动画中移动了1米,那我在播放这个动画的时候,这个物体也就移动了1米,这种方式就不存在一个所谓的“运动函数”的概念,因为都是动画数据驱动的,所以运动系统基本上就是从动画中取得当前物体的位置,这样的好处就是避免了“滑步”的问题,但是对于程序员来说,它不可控,完全取决于数据。

所以,当我们想要做一个“可预测的运动系统”的时候,就不得不考虑上面的两种情况,因为在不同的游戏中,运动系统的实现是不同的,有的用了第一种,有的用了第二种,有的是在第一和第二中之间切换的。

为了更好的表述“可预测的运动系统”,我需要定义一个运动状态的结构来描述当前物体的运动状态:

 1: struct PhyXState
 2: {
 3:     Vec3 m_v3Position;
 4:     Vec3 m_v3Velocity;
 5:     Vec2 m_v2Facing;
 6: };

这个结构里包括位置(3维向量),速度(3维向量)和朝向(2维向量),这是描述物体运动状态的三个基本量,对于通用的“可预测的运动系统”来说,它的输入输出就可以这样来描述:

PredictableLocomotionSystem1

输入是当前状态(Current PhyXState),目标状态(Target PhyXState),当前时间(Current Time)和想要预测的未来时间(Future Time),输出就是在未来时间的状态(PhyXState at Future Time)。特别要指出的是,如果预测的运动状态是基于当前的运动行为,那我们可以不传入“目标状态”,“当前状态”和“当前时间”,仅仅传入“未来时间”即可,因为对于当前运动行为而言,其内部已经保存了“目标状态”,“当前状态”和“当前时间”了。但作为通用描述,还是将这三项列在其中。

我们仔细考虑的话,会发现,其实运动系统天生是带有些许预测功能的,当每一帧在更新的时候,就是向运动系统传入了这些参数,然后得出了当前运动物体应该处于的运动状态,在这些参数中唯一值得注意的是“未来时间”,在正常的游戏循环中,“未来时间”是被指定为:

未来时间 = 当前时间 + 步长(Time Step)

也许你会觉得问题似乎是解决了,因为这样的话,不是就可以精确预测任意时候的运动状态了吗?

但回想一下我上面所说的游戏里用的两种运动方式,那问题就并不是这么简单了,对于第一种运动系统,确实不怎么修改就可以实现精确的预测,因为这种运动系统一般是基于数学函数,或者经验数据表(就是预先算出不同输入下的运动数据值,然后通过查表的方式来返回结果),要做的仅仅是封装一个接口,但对于第二种,或者对于混用的运动系统,我们就需要考虑更多的问题了,……。

(待续)

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More