游戏设计⼊门——游戏程序框架设计
游戏开发这个世界太⼴阔了,这篇⽂章中,我只在程序实现的抽象逻辑上,开⼀个⼝⼦进⾏⼀些肤浅的阐述。
当我去设计⼀个游戏,⼀个玩法,包含规则,游戏对象等等⼀系列的游戏系统,⽤纸可以写下来,⽤嘴可以说出来,但是当我想要⽤程序去实现这⼀堆东西的时候,就有些⽆从下⼿。
⽐如我们⼤家都会下五⼦棋,它有⼀个明确的规则“⼀⼈⼀字交替放置棋⼦,率先5颗连成⼀线获胜”,在现实世界中,我们不⽤考虑五⼦棋的硬件配置,拿⼀张⽩纸,画上横纵格⼦,两⼈分别执笔就可以完成⼀场五⼦棋的游戏,或者是使⽤正规的五⼦棋盘和棋⼦。⽆论⽤什么玩,要实现“玩”是很简单的。
但是在做⼀个五⼦棋视频游戏的时候,那就需要把“棋⼦”“棋盘”这些东西⽤软件去实现,
进⼀步,作为抽象概念的“游戏规则”“胜负判断”也需要软件去实现,这就需要更多的“⽤计算机思维去思考”。
于是我花了⼀些时间,凭借有限的本科计算机基础以及⼀些⾃我东拼西凑的⾃学,总结了⼀下游戏框架的⼀个通⽤设计⽅案。
⼀.随便开个头
⾸先阐述⼀下⼤的抽象概念,这⾥也不⽤被“抽象”⼆字吓到了,什么是抽象,就是归纳总结出来的结论之类的东西,就像把⾃⾏车,飞机,汽车都抽象为“交通⼯具”⼀样,这⾥归纳总结⼀下这些玩意的共性,于是“交通⼯具”这⼀个抽象概念就诞⽣了。
视频游戏程序框架,我认为她(she)就是⼀个循环(Loop)过程性,“开始游戏——杀敌/解密/闯关/…——游戏胜利/游戏失败——开始游戏”,对吧,关键字,循环(Loop)的,过程的。
进⼀步对此概念进⾏细化,衍⽣出GameState(游戏状态)和GameController(游戏/规则控制器),⾄于为什么会有这两个概念?我是参考了UE4提供的GameState类和GameMode类,同时也参考了前辈们⽤Flash写的游戏逻辑,⾄此我总结为GS和GC两⼤块,⾄于更⾼级的学术阐述,受限于我学识⽔平⽬前还做不到。
⼆.GameState
GameState就是对游戏状态的描述,最基本的两个,GamePlay(开始游戏)GameOver(游戏结束),这⾥GameOver不能理解成玩家平时看见的GameOver,玩家平时看见的GameOver⼤多表⽰游戏失败,⽽在这⾥,这个概念只是表⽰游戏结束,因为⽆论你游戏胜利还是游戏失败,游戏都结束了(GameOver)。
在此之上,可以进⾏扩展,例如现在游戏都会有主菜单,也有暂停,或者有多⼈模式,游戏设置等等,于是我们可以在GameState中添加MainMenu,GamePause,MultiPlay,GameOption等等。
使⽤GameState进⾏游戏状态的管理有什么⽤?最重要的就是给你⼀个清晰的编程实现游戏的思路,或者说给你⼀个⼊⼿点,⾄于⽅便管理游戏进程,⽅便后续游戏程序功能性扩展,降低游戏程序各系统耦合性等等,实在太多了。
⾄于GameState如何使⽤?这东西就是⽤来切换的,作为标识游戏⽬前状态的⼀个Flag。
之前在阅读《游戏⼈⼯智能编程案例精粹》这本书中,其中详解了FSM(有限状态机)在游戏AI的应⽤和扩展,以此联想,对于GameState的处理使⽤FSM岂不是再合适不过了。《lua游戏开发实践指南》中提到“对于Singleton,⽆论什么情况下,只要它们提供⽅便就使⽤它们”,虽然这句话说的太满,不太符合中国⼈的思维,也不符合辩证法(笑),但也某种程度上表明Singleton的实⽤性和⼴泛性。所以更进⼀步,使⽤Singleton(单例模式)的FSM处理GameState,以我来看是极好的。
当然,以上是抽象概念的阐述,最终⽤到游戏程序设计上,还得按照⼀些编程语⾔的语法和特性来实现,这⾥,我也给出了⼀份Unity的C#的原型代码,可以当作伪代码吧。
Unity的C#的原型代码:
publicclass S_GameState : MonoBehaviour {
publicenum GameState
{
GamePlay,
GamePause,
GameOver,
GameReady,
GameInit
}
publicstatic S_GameState Instance;
private GameState m_GameState;
void Awake()
{
Instance = this;
}
public GameState GetGameState()
{
return m_GameState;
}
publicvoid ToGamePlay()
{
m_GameState = GameState.GamePlay; }
publicvoid ToGamePause()
{
m_GameState = GameState.GamePause; }
publicvoid ToGameOver()
{
m_GameState = GameState.GameOver; }
publicvoid ToGameInit()
{
m_GameState = GameState.GameInit;
}
}
补记:
⾄于FSM和Singleton,请教⾕歌⽼师是⼀个很好⽅法,不过,这⾥我稍微阐述⼀些FSM的应⽤,FSM早些年⽤于游戏AI的构建,毕竟游戏AI不同于科研领域的AI,游戏AI不是为了让玩家⽆法战胜⽽设⽴的,基本上游戏AI就是⼀套规律的集合,⽐如《⿊暗之魂3》的第⼀个BOSS——古达,其中⼀个规律就是半⾎之后会变⾝,变⾝时候有动画,不会攻击,玩家可以很安逸的砍空⼀条体⼒,然后全⾝⽽退,这个规律,打⼏百遍古达都不会变。
回到原来话题,FSM⼴泛应⽤于游戏AI的地位现在基本被⾏为树取代了,然后FSM现在就专职做起了⾓⾊动画的管理,⽐如第三⼈称玩家⾓⾊的各种动画切换,UE4和Unity都使⽤FSM进⾏动画状态切换的管理。
三.GameController
说完了GameState,就该说⼀下GameController了
GameController就是⽤来管理游戏规则的了,⽐如开始游戏了,要⽣成玩家⾓⾊,⽣成敌⼈,⽣成地图等等;玩家按了ESC,要暂停游戏了;玩家通关了,妙极,GameOver;
所以GameController⽐起GameState要更加具体,基本上GameController就是要处理GameState切换
之后⼀系列⼯作,依旧是五⼦棋,⽐如GameState从MainMenu切换到了GamePlay,那么GameController就要加载⼀个载⼊画⾯,载⼊结束后,消掉载⼊画⾯,同时显⽰⼀个棋盘等待。当然,OnLoading也可以作为⼀个GameState,⾄于要不要这个OnLoading的GameState,就归到弹性选项⾥⾯好了(笑)。
这⾥就需要⼀个游戏状态的检测,如果游戏某⼀时刻切换了状态,那么作为GameController就得做点事情了,所以要快!准!狠!
于是在⼀个独⽴线程⾥⾯整个⼀while(true)来不断循环判断是否游戏状态进⾏了切换
当然GameController也可以使⽤⼀个Singleton来实现,我认为是不错的,不仅可以异步执⾏,⽽且很快,充分满⾜快准狠的需要,⽽且分离了游戏的渲染线程和逻辑线程,并⾏优化/劣化好像很不错(笑)——对于⼀般的⼩型游戏,多线程可能会杀鸡⽤⽜⼑,反⽽劣化的游戏性能。
不过现在游戏引擎都体统了⼀个每帧调⽤的函数,所以将游戏状态检测放在⾥⾯也是很好的,以下,提供⼀个Unity的C#代码,以供参考。
Unity的C#的原型代码:
void Awake()
{
Instance = this;
}
void Start()
{
m_StartWait = new WaitForSeconds(m_StartDelay);
m_EndWait = new WaitForSeconds(m_EndDelay);
StartCoroutine(GameStateOperator());
}
IEnumerator GameStateOperator()
{
while(true)
{
switch(S_GameState.Instance.GetGameState())
{
case S_GameState.GameState.GameInit:
GameInit();
break;
case S_GameState.GameState.GameReady:
GameReady();
break;
case S_GameState.GameState.GamePlay:
单人开发选ue4还是unityGamePlay();
break;
case S_GameState.GameState.GamePause:
GamePause();
break;
case S_GameState.GameState.GameOver:
GameOver();
break;
}
yieldreturnnull;
}
}
补记:
这⾥使⽤了Unity的伪线程——协程,⽅便使⽤多线程进⾏参考,同时也为了让代码结构清晰⼀些。
四.状态切换
GameState提供了游戏状态来切换,GameController提供了状态切换后的⼯作处理,那么问题来了,什么玩意⼉来切换GameState呢?
⾃然是游戏对象了,GameObejct,玩家⾓⾊,Npc,某个⼦弹,某些触发器,键盘操作等等。回到上⾯看看GameState的切换函数使⽤了public,也是这个原因,反正都是static了,稍微裸⼀点,我觉得颇为不错(笑)。
回到原来话题,⽐如玩家⾓⾊也许有⼀个HP的属性,当HP==0时,GameOver,调⽤⼀下GameState的状态切换函数,将状态切换称GameOver,这时,GameController检测到了GameState切换了,开始做出响应,很完美。
当然GameController也可以⽤⼀⽤状态切换,⽐如⼀些类似关卡倒计时计算需要写在GameController
⾥⾯,当倒计时为0是,游戏结束,这时候就需要GameController调⽤切换函数了。同样的,对于玩家⾓⾊的HP检测放到GameController⾥⾯也未尝不可,不过这显然不符合OOP的设计原则,这么明显的增加耦合性,软件⼯程⽼师要⽓死。但是我就是要⽓死他(笑)。
补记:
对于游戏对象的分析和设计,我觉得需要综合OOP思想和设计原则,虽然设计主流游戏类型,⼤多有原型了,⽐如UE4就很⼈性化,提供了各种类型游戏原型,这⾥UML之类⼯具使⽤起来也是极好的。当然不能忘了UI的设计,UI是要契合游戏程序的,有效利⽤GameState是很好的⼊⼝点。下⼀篇我应该会说说UI设计的东西。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论