intent
oop
一次通知一堆独立对象处理一帧的行为
编码
motivationspa
玩家控制的角色正在执行一个任务,任务内容是偷取死了好久的巫师王骸骨上的珠宝。它犹豫地靠近地穴的入口。诅咒的雕塑并无向他雷电攻击。没有不死士卫巡逻门口。它直接走进去,拿到战利品,游戏结束。你赢了。
设计
好吧。那永远不会出现。
code
这个地穴须要几个守卫-咱们的英雄能够与之搏斗的敌人。首先,咱们须要一个守卫在门口巡逻。若是你忽略一些细节,最简单的实现守卫来回巡逻的代码可能就像这样:
对象
while (true) { // Patrol right. for (double x = 0; x < 100; x++) { skeleton.setX(x); } // Patrol left. for (double x = 100; x > 0; x--) { skeleton.setX(x); } }
问题是,虽然角色左右移动了,可是玩家看不到。程序被卡在无限循环中,这不是个好的体验。咱们真正想要的是每一帧移动一步。
游戏
咱们必须移除那些循环,并使用外部循环。这保证游戏在守卫巡逻时,依然能够响应输入,能够渲染。像:
事务
Entity skeleton; bool patrollingLeft = false; double x = 0; // Main game loop: while (true) { if (patrollingLeft) { x--; if (x == 0) patrollingLeft = false; } else { x++; if (x == 100) patrollingLeft = true; } skeleton.setX(x); // Handle user input and render game... }
后一种比前一种代码更复杂。左右巡逻上面是两个简单的for循环。如今咱们使用外部循环,每一帧都从离开的位置从新开始,并使用patrollingLeft标记方向。
ip
可是这个大约能够工做,咱们继续。这些骷髅守卫没什么更多动做,接下来咱们添加魔法塑像。他们射出箭状闪电,阻挡角色。
input
继续咱们的“最简单的实现方式”,像:
// Skeleton variables... Entity leftStatue; Entity rightStatue; int leftStatueFrames = 0; int rightStatueFrames = 0; // Main game loop: while (true) { // Skeleton code... if (++leftStatueFrames == 90) { leftStatueFrames = 0; leftStatue.shootLightning(); } if (++rightStatueFrames == 80) { rightStatueFrames = 0; rightStatue.shootLightning(); } // Handle user input and render game... }
你能够分辨出这不是咱们喜欢维护的代码。咱们会将每个游戏实体的大量变量和必要的代码放在游戏循环中。为了使他们一块儿工做,咱们把代码搅在一块儿。
解决这个模式很是简单,可能你已经知道了:每个实体应该封装本身的行为。这样可使游戏循环代码整洁,并且很容易添加和删除实体。
为此,咱们须要一个抽象层,经过定义一个update方法建立它。游戏循环维护一个对象的集合,可是不知道具体的类型。它知道的是它们都能update。这使得每一个对象的行为与游戏循环、其它对象的行为分离开来。
每一帧,游戏循环遍历对象调用update。经过调用update,全部对象同时表现行为。
游戏循环有一个动态对象集合,因此添加和删除都是很简单的-就是直接从集合中添加和删除便可。再也不有硬编码了,咱们甚至可使用文件来填充关卡,这正是关卡设计师想要的。
the pattern
游戏世界维护一个对象的集合。每个对象都实现了update方法,模拟一帧的行为。每一帧,游戏循环更新每个对象。
when to use it
若是把Game Loop比做面包,那么update就是黄油。与玩家交互的至关多的游戏对象采用这个模式或类似的模式。若是一个游戏有太空陆战队,龙,火星人,鬼,还有运动员,那么这是使用这个模式的好机会。
然而,若是一个游戏更加抽象,游戏对象不怎么活动更像是棋盘的棋子,那么这个模式不适合。像象棋这种游戏,没必要模拟全部的棋子,甚至没必要每一帧都更新一个卒子。
更新方法当这些状况时好用:
当游戏同时运行大量对象或系统时。
每个对象的行为都是独立的。
对象行为随时间流逝来模拟
keep in mind
这个模式至关简单,没有什么使人惊讶的内容。
可是,每一行代码都有衍生版本。
sliptting code into single frame slices makes it more complex
当你比较前两段代码,你会发现第二段更复杂。都是简单地使守卫左右巡逻,可是后者把这个过程分散到每一帧进行。
这个修改是必须的,由于要处理用户输入,渲染还有其余的事务,因此第一个不实际。可是记住将代码分散会增长很大的复杂性。
you have to store state to resume where you left off each frame
在第一个例子中,咱们并无一个变量表示向左向右。它隐式取决于哪段代码正在执行。
当咱们把它改为“一帧一次”的形式,咱们不得不建立一个patrolLeft变量追踪方向。当咱们执行完代码,执行位置丢失了,因此咱们不得不显式存储足够多的数据以便下一帧恢复。