AlexZ33 / game-h5

1 stars 0 forks source link

如何建立游戏主循环框架? #6

Open AlexZ33 opened 3 years ago

AlexZ33 commented 3 years ago

1. 理解游戏循环

image

从单机游戏到复杂的大型多人在线游戏,游戏中的主循环部分都包含着类似的几个部分,正是有了循环中的这些游戏部分,才有了丰富多彩的游戏世界

实际上,游戏的运行就是整个游戏循环的运行,在整个游戏循环过程中需要完成如图过程:

初始化游戏:在整个游戏的开始部分,需要做一些游戏初始化的工作,以便游戏更加快速的运行,比如需要加载游戏运行的各种资源文件,读取游戏配置的各项数据等。 • 游戏主循环部分

➢ 读取外部操作:整个游戏的运行离不开我们和游戏世界的实时交互,甚至来自于游戏世界本身的交互,所以游戏世界中必须监控来自外部的操作,以随时改变游戏的状态。这些外部操作主要来自外部设备或者网络数据,比如键盘、摇杆、触屏、话筒、方向盘等。

➢ 更新游戏场景:在获取了游戏外设的操作后,游戏世界必须根据这些改变游戏世界的数据进行更新整个游戏的场景。比如,在《超级玛丽》中,按下了跳跃按钮,在水果忍者中,用手指果断地画了一个十字斩等,这时候,就必须在游戏循环中根据外部操作更新玛丽的动作,让它跳得更高。事实上,这个更新的过程,并不一定需要等到外来的操作,有可能是来自内部的游戏的其他对象引发的,比如,《超级玛丽》中,玛丽不小心碰到了怪物,也有可能是来自于网络游戏的服务器,在网络游戏中,客户端的游戏世界还需要根据服务器的数据进行更新。事实上,这部分也可以看成游戏的主要逻辑处理部分,一旦满足退出游戏逻辑,那么游戏主循环就结束,结束游戏。

➢ 显示游戏场景:为了让用户有成就感,让用户有更多的视觉享受,在进行了游戏更新场景的操作以后,需要做的事情就是把游戏中的场景显示(渲染)出来,不管是战神中的血腥,还是仙剑中凄美动人的爱情,最后都需要通过画面进行渲染出来。

以上是游戏循环中的3个重要的部分

• 结束游戏循环:游戏主循环中,如果满足退出游戏逻辑,则游戏结束。

AlexZ33 commented 3 years ago

2. 游戏引擎介绍

游戏引擎,玩家所体验到的剧情、关卡、美工、音乐、操作等内容都是由游戏的引擎直接控制的,它扮演着发动机的角色,把游戏中的所有元素组合在一起,指导有序的工作。

简单地说,引擎就是“用于控制所有游戏功能的主程序,从计算碰撞、物理系统和物体的相对位置,到接受玩家的输入,以及按照正确的音量输出声音等”。

随着游戏的规模越来越复杂化,游戏引擎已经逐渐从游戏中分离出来,有很多公司专注于游戏引擎的研发,而不去制作独立的游戏。游戏引擎这个概念也是随着游戏软件的发展逐步成熟起来,早期的时候人们开发游戏的周期比较长,一般是8~10个月,为什么这么久呢?一来是早期开发游戏的成本比较高,不论是电子游戏还是电脑游戏,由于受到硬件条件的限制,基本上开发的主要语言是C和汇编,这就对程序员的要求比较高;二是人们开发出一款游戏后,再开发新的游戏,有时候需要从头开始编写。随着人们对游戏开发领域越来越熟练,一些有经验的开发人员会逐渐把游戏中相同的部分独立出来,再开发同样类型的新游戏的时候直接重用这块独立的部分,以便节省时间和开支。

事实上,游戏引擎可以看作一个模板或者框架,它和游戏的具体实现内容无关,就比如简历表,我们可以提供一种标准的模板,大家都使用这个模板填写简历,制作简历表的人员可以无需考虑简历具体的内容,因为具体的内容需要由具体的使用人员去完成。正如在J2EE中有许多优秀的框架(如Spring、Hibernate、Struts、JBoss Seem)一样,,游戏领域经过了多年的发展同样也有着非常多的优秀引擎框架,如Doom引擎、Unreal引擎、Quake引擎、bigword引擎、cryEngine和unity3d引擎等。这些引擎大多是商业化的游戏引擎,基于这些引擎开发的游戏也被广大游戏迷们津津乐道,如半条命、Doom、雷神之锤、孤岛危机等。当然,随着游戏行业的分工越来越细致,游戏引擎也分得更加细化,

下面的部分我们将会根据以上所述的游戏主循环概念,建立一个简单的基于canvas的渲染引擎

AlexZ33 commented 3 years ago

3. 渲染引擎

3.1 建立游戏主循环框架

理论上来讲,基本的主游戏循环可以采用以下的最简单的伪代码实现:

image

当然,以上的伪代码在HTML5中并不适用,事实上如果在HTML5中使用JavaScript完成这个游戏循环,估计我们能看到的是网页是死一般的寂静,因为,JavaScript执行的这个循环中无法接收任何的系统或者外部的响应,这个主循环会阻塞页面进程,除非游戏循环中断。有过Windows下开发游戏经验的都知道通常情况下,这个游戏主循环实际上是放在Windows主窗体的消息循环里面,在Windows的消息循环中执行游戏循环的主体,所以不会导致整个应用程序的阻塞。那么,通常在HTML5中如何使用JavaScript执行游戏主循环呢?

答案是:使用定时器

JavaScript中有两个常用的函数用来实现定时器功能,这两个方法分别解释如下。

setInterval(()=> {

  var r = Math.random() * 25 | 0,
   g = Math.random() * 25 | 0,
  b = Math.random() * 25 | 0,
   c = Number((r<<16)+(g<<8) + b).toString(16);
   document.body.style.backgroundColor="#" + c; 

}, 500)

(1)定时器并不是实现了多线程在后台执行,事实上JavaScript是单线程机制(除了使用HTML5中的Worker以外),浏览器中执行的所有代码都被放进了一个队列中顺序执行,关于浏览器对外部资源的加载、DOM的加载、DOM的渲染和事件监听等都会放入这个执行队列,定时器所做的事情只是在指定的时间点上,把需要执行的代码插入这个队列,并不保证到了时间点会立即执行,比如以下代码,我们的想法是,点击按钮后,1秒后弹出对话框:

image

事实上,当你点击按钮,1秒后并不一定弹出对话框,还得取决于那个for循环方法执行了多久,因为这段代码执行的队列如图 image

可以看出,当点击btn的时候,浏览器会把btn的click事件方法插入到执行队列中,当执行click方法的时候,碰到了setTimeout方法,于是把定时器执行的方法插入到执行队列后,虽然设定的是1秒后执行,但这个1秒后至少是必须在btn的click执行完毕后的1秒才开始执行

(2)使用setTimeout只会调用函数一次,如果要定时调用函数,则使用setInterval更加方便简洁,但使用setInterval函数的时候无法精确保证每次执行的时间间隔,虽然setInterval函数是表示每隔指定的时间点进行执行函数,实际上表示的是在指定的时间点上往执行队列中插入执行函数代码。如果setInterval本身的函数体执行时间大于函数执行的间隔时间,就会发生多个函数体好像连续执行了,出现跳帧现象。

这种情况下,如果我们希望保证函数执行的间隔,那么我们可以使用链式setTimeout函数替代,代码如下:

image

(3)定时器方法中的时间参数并不是无限小,按照一般的想法,可以给定时器的执行间隔设置无限小,比如,每一毫秒执行,setInterval (fn, 1)。实际上按照HTML5给出的规范中指出,如果给的时间参数小于4,则会自动设置成4毫秒,即使是4毫秒,很多浏览器执行的时候依然达不到,一般能达到10毫秒左右,对很多程序已经足够,如果省略时间参数,则浏览器会按照最小的时间间隔执行。

AlexZ33 commented 3 years ago

image