这章覆盖了游戏编程的基本原理。首先,你会了解到游戏的基本框架,就是一个游戏世界和一个游戏循环。你将会经过不一样的例子学到如何用JavaScript建立游戏框架,最终,我会讨论如何经过注释,布局和恰到好处的空白来增长代码可读性。程序员
这部分讨论了如何像搭积木同样建立游戏。我论述游戏世界的同时经过一个更新-绘画循环来向你演示这个过程,这个循环就是,更新游戏世界,显示游戏世界。编程
是什么让游戏有了如此的娱乐性能让你乐此不疲的探索这个虚构的世界,作你在现实世界中作不到的事情。你能够骑在龙背上,摧毁整个太阳系统,或者创造一个有至关复杂文明的角色,角色说着虚构的语言。这个想象力就被叫作游戏世界。游戏世界能够是一个小的区域好比俄罗斯方块这个游戏世界,也能够复杂到像侠盗猎车手和魔兽世界那样的虚构世界。canvas
当一个游戏运行在电脑或者智能手机上,这个设备维持着这个游戏世界的内部表征。这个表征不是你玩游戏在屏幕上看到的那些东西。它包含了大量描述对象的信息,一个敌人能有多少生命值,一个角色上有多少物品等等。幸亏,程序知道如何把这些内部表征显示到屏幕上。不然,玩游戏将会变得很是乏味,由于玩家不得不翻阅大量的数据查看他们是否拯救了女王或者已经死亡。windows
玩家永远不会看到游戏世界的内部表征,可是游戏开发者能够。当你想开发游戏,你须要考虑如何设计游戏内部。你游戏编程的乐趣之一就是你完成了这些内部设计。数组
另一个重要的事情是要意识到与现实世界同样,这个游戏世界随时处于改变中。怪物移动到不一样的地方,天气发生改变,车会跑的没油,敌人会挂掉等等。此外,玩家的操做会直接影响游戏世界的改变!所以简单的在计算机内存中储存单一的游戏世界内部表征是不够的。一个游戏须要持续不断的记录玩家的所做所为而且以此更新内部表征。此外,须要把游戏世界展示到玩家的眼前,经过电脑显示器,电视,或者智能手机的屏幕。处理这些事情的过程称为游戏循环。浏览器
游戏循环处理游戏的动态方面。在游戏运行过程当中有不少事情发生。玩家按下键盘或者点击触摸屏。一个不断改变的游戏世界包含了等级、怪物,和其它须要保持更新的角色。也有其它一些特殊效果,好比爆炸,声音等等。这些不一样的任务都须要经过游戏循环处理,这些东西能够分为两类:服务器
游戏循环会不断的执行这个任务,一个接一个(如图2-1)。例如,让咱们在吃豆人这个游戏里看如何处理角色的移动。吃豆人出如今迷宫中的某个地方而且朝一个固定的方向移动。第一个任务里(更新和维护世界),检测玩家是否按下了方向键。若是按下了,则须要更新吃豆人的位置。也许,由于吃豆人的移动,它吃掉了一个豆,这个豆代表获得了分数。你须要检测这个豆是不是吃豆人吃掉的最后一个豆子,由于吃完豆子代表已经经过了本关卡。最终,若是吃掉了一个大豆,则幽灵就禁止了。而后你须要更新剩下的游戏世界。幽灵的位置须要更新,还要决定积分结果显示在哪里,须要检测吃豆人是否与幽灵发生了碰撞等等。你会发现即便在像吃豆人这样简单的游戏里,许多工做都须要在第一个任务里作。从如今开始,我把这些不一样的跟更新和维护游戏世界相关的任务叫作更新行为。markdown
(省略图2-1)app
与第二部分任务相关的就是向玩家展现这个游戏世界。在吃豆人这个游戏里,意味着要绘画出迷宫,幽灵,吃豆人和一些向玩家展现的重要信息,好比玩家获得了多少分,还剩下几条命等等。这些东西能够被放在屏幕上不一样的位置,好比顶部或底部。这部分也被叫作抬头显示部分(HUD)。现代3D游戏有更复杂的绘画任务。这些游戏须要处理灯光和阴影,反光,相似爆炸的视觉效果等等。框架
前面的章节教会了如何建立一个简单的JavaScript程序。在那个程序里,指令被装进一个函数里,相似下面:
changeCanvasColor = function(){ document.body.style.background = "blue"; }
这样书写程序考虑到的是JavaScript是过程式语言:指令由函数组合在一块儿。第一步是用JavaScript写一个简单的游戏循环。以下所示:
var canvas = undefined; var canvasContext = undefined; function start () { canvas = document.getElementById("myCanvas"); canvasContext = canvas.getContext("2d"); mainLoop(); } document.addEventListener('DOMContentLoaded', start); function update () { } function draw() { } function mainLoop () { canvasContext.fillStyle = "blue"; canvasContext.fillRect(0, 0, canvas.width, canvas.height); update(); draw(); window.setTimeout(mainLoop, 1000 / 60); }
上面的脚本中有多个函数。由于下面一条指令,当HTML加载时执行start函数:
document.addEventListener('DOMContentLoaded', start);
在start函数里,你获取画布和画布上下文;把这两个东西储存在变量里以便在其它程序里可使用。而后执行另一个函数mainLoop。这个函数里有其它指令,其中两条指令处理背景颜色。而后调用update函数,以后是draw函数。这两个函数又含有其它指令。最后一条指令是下面这条:
document.addEventListener('DOMContentLoaded', start);
这句指令的意思在一个肯定的时间后(1000/60,大约16.6毫秒)再次执行mainLoop函数。当mainLoop函数被再次调用,背景色再次发生改变且update函数和draw函数也再次被调用。在这里,update函数和draw函数都是空的,可是能够向里面添加东西用来更新和绘画游戏世界。须要注意的是在循环之间使用setTimeout函数并非最好的解决方案。有时这个方法的影响能超出你控制以外,好比在缓慢的电脑,在浏览器中打开的其余东西,或者同时其余一些须要处理器运行的应用在运行等等。当你须要处理敏感的时间操做(好比玩家须要5分钟后复活),此时你不是再依靠setTimeout函数而是根据系统的事件调度或者在update函数中检测是否发生这些事件来进行处理。
当你运行这个例子程序,update和draw函数被不断的执行:更新,绘画,更新,绘画等。此外,这一切发生的很快。这个例子运行速度大概是60帧。这种循环称做是固定的时间循环,它在一些小游戏中是很是流行的。你也能够设计不一样的游戏以不一样的循环方式运行着而不是每秒60帧这样。
这本书教会了你不少不一样的方法来填充update和draw函数。在这个过程当中,我会介绍许多有用编程技术,对游戏和其余应用程序都有用。下面的部分将更细节的讲解基础游戏应用。那时,你将会为这个游戏基本框架添加其余的指令。
这节细节的讲程序结构。在早些时候,许多程序员使用文本而不是图形编程。这种基于文本的应用叫作控制台程序。除了在屏幕上输出文字,它也能够接收来自用户的文本输入。因此,全部与用户的交互都在一个问答表列表里(Do you want to format the hard drive (Y/N)? Are you sure(Y/N)? and so on))。在基于窗口的操做系统变得流行以前,基于文本的接口对应文本编辑程序,电子表格,数学应用甚至游戏都是常见的。这些游戏被叫作文字冒险游戏,文字描述了这个游戏世界。玩家经过输入命令与游戏世界交互。
用JavaScript编写控制台应用是能够实现的。虽然看上去颇有趣,但我是仍是把注意力放在现代图形游戏上。
控制台应用只是应用程序类型的其中一种。其余常见的类型是Windows应用。这类型应用在一个屏幕里包含了窗口,按钮和其余用户图形接口(GUI)。这类型应用通常是事件驱动的,好比按下按钮或者选择一个菜单。
另一个应用类型是APP,运行在智能手机或平板电脑上。这类型的应用屏幕空间通常有限,可是有更多的互动。好比GPS能够找到设备的地点,传感器能够知道设备的方向,还有触摸屏幕。
当开发应用程序,让其运行在不一样的平台上是一个很大的挑战。建立windows应用和app是很是不一样的。而且复用不一样类型的应用代码是很是难的。所以,基于网页的应用变得愈来愈流行。在这种状况下,应用被放在服务器端,用户在浏览器中运行程序。这里有许多这种例子:好比基于网页的Email程序或者社交网站。这本书里,学的就是基于网页的应用。
记得在一个过程式程序里,指令作着这个程序里实际的工做:它们一个接一个的执行。改变着内存或者屏幕显示,这样就注意到它们的存在。在BasicGame程序里,不是全部的行都是指令。好比一个指令context.fillRect(0, 0, canvas.width,canvas.height)。
由于Java是过程式语言,指令能够被放在函数里。指令不是必须就要被放在函数里的。好比下面的一条指令就不属于函数:
var canvas = undefined;
然而,函数很是有用。它能够避免代码的零散化,由于指令只在一处地方出现,而且可让程序员经过函数名轻松的进行调用。函数中的指令在两个花括号之间。这些指令被叫作函数体。在函数体外,书写函数头部,好比下面这样:
var canvas = undefined;
头部包含了函数名。你能够为函数取任何名字。你能够看到gameloop函数有两个部分,draw和update。这两部分也在函数里面。在函数名以前须要function这个单词。在函数名以后是一对花括号。
若是你不知道编程语言规则那么使用相似JavaScript这样的语言编程是很是困难的。这本书会用被叫作语法图的东西来阐述编程语言的组成结构。一个编程语言的语法参考正式的规则,这些规则决定这是否是一个有效的程序(换句话说,程序能让编译器或解释器读懂)。相比之下,程序的语义参考了它实际的意思。为了区分语法和语义的区别,请看这句话:all your base are belong to us。在语法上来讲,这句话有问题。可是语义上至关清楚。
解释器能够检查程序的语法:违背语法的程序都会被解释器报错。不幸的是,解释器不能检查程序的语义是否为程序员心中所想的那样。因此程序语法的正确并不表明语义上的正确。若是语法都不正确的话,程序确定不能运行。语法图帮你形象化编程语言的规则。好比,下图就是一个关于JavaScript如何定义函数的语法图(图2-2)。
(省略图2-2)
(省略)
gameloop里面有update和draw函数。由于函数就是指令的集合,每当update函数被调用,函数中的指令就跟着被执行。draw函数也是如此。
假若有这样一个例子,想象你设计个气球跟随鼠标移动的简单游戏。当你移动鼠标,气球也跟着移动。在update和draw函数里,你能够这么作。在update函数里,你须要获取鼠标当前位置并把它储存起来。在draw函数里,你须要把气球显示在刚才储存的位置上。固然,你不知道这些指令是否存在,而且你也不知道这些指令是什么样子。也许你会想这些指令为何这样运行。你没有移动气球,你只是简单的把它画在update里面储存的位置上。在一个很快的速度里反复调用update和draw函数。由于如此快的速度,在不一样的位置画出气球感受看起来就像气球在移动(实际上不是这样)。这就是全部的游戏的游戏世界绘画的方式和玩家怎样被游戏的世界吸引。本质上,就是以很快的速度的在不一样的位置上进行画图。
这节处理程序源代码的布局。你首先会了解到如何为你的代码添加注释。而后你会学到如何写出清晰的代码经过使用空格,缩进,单行或多行。
(省略)
(省略)
(省略)
在这章里,你学到了: