这章讲述编程语言随着时间变化而变化的过程。自从因特网从1990年出现开始,不断有大量的编程语言和编程工具在发展。其中著名的语言之一就是HTML,这种语言被用来创建网站。HTML和CSS与Javascript一起,可以用来创建动态网站,然后通过浏览器展示在我们面前。
在你与HTML和Javascript打交道之前,这部分文章将简要的对计算机和程序做个概述。在这之后,你将学会如何创建一个简单的HTML网页,网页中同时含有Javascript。
通常来说,一个计算机含有一个处理器和内存。对现在的电脑而言都是这样,包括游戏机,智能手机和平板电脑。我是这样定义内存的,可以从中进行读取或者写入,或两者兼得。内存有不同的种类,主要区别是数据存取和数据传送的速度。有些内存可以读取和写入你想要的足够多次的数据,有些内存只能读取数据,而有些内存只能写入数据。计算机中主要的处理器又被称作中央处理单元(CPU),另外一个与CPU相似的处理器是图形处理单元(GPU)。在今天有些场合看来,CPU自身不仅仅只再是一个单一的处理器,它其中还含有其他的核心。
输出和输入设备,比如鼠标,游戏手柄,键盘,显示器,打印机,触摸屏等等,这些东西咋一眼看好像完全独立于处理器和内存之外。然而,抽象的说,它们都是内存。比如触摸屏就是一个只读内存,打印机就是一个只写内存。
处理器的主要任务就是执行指令。执行这些指令的效果就是内存发生了改变。特别是在我这种广义的内存定义下,处理器执行的每条指令在某些方式上都在改变着内存。你不可能只让计算机只执行一条指令。一般来说,你有一个长长的指令单需要执行——“移动这部分内存,清除这部分内存,在显示屏上进行显示,检查是否有键盘输入”,这些被执行的指令就叫做程序。
概括说,一个程序就是很长的一段改变计算机内存的指令。然后,程序本身也储存在内存里。程序中的指令在执行之前,被储存在硬盘上,DVD上,或者USB这种FLASH设备中;或者储存在云端;或者储存在其他储存设备中。当程序需要执行时,程序就被迁移到计算机内存储器上。
那些组合在一起的形成程序的指令,需要用某种方式来表达。计算机不会通过白话英语就掌握输入的指令,这就是为什么你需要编程语言的原因,比如Javascript。在实际中,指令类似文本一样进行编写,但是需要遵循严格的规则进行书写,这些规则就是一系列为编程语言量身定制的。许多编程语言存在的原因就是当某些人想到些许表达指令的好的方式,他们就会努力把它变成一种新的编程语言。很难说到底有多少种编程语言,但是至少有上千种。
幸运的是,学全部不同的语言是不必要的,因为它们有很多的相似性。早些时候,编程语言的主要目标就是使用计算机中新的可能性。然而,现在更多的编程语言集中用有序代替混乱。编程语言之间拥有很多相似的特性,可以说作是属于同样的范式,这些范式来自于一系列的实践所得。
大量的编程语言属于指令式编程。因此,这些语言被叫做命令式语言。命令式语言是基于改变内存的指令,照此,这些语言适用于前面所描述的处理器-内存模型。Javascript就是命令式语言的例子。
在早些时候,电脑游戏编程是非常难的,需要超高的技能。一个游戏机,比如游戏卡带机只有128字节的内存,而且最多只能使用4096字节的ROM,这些ROM同时包含程序和游戏数据。这些东西相当的限制了游戏的可能性。比如,许多游戏有对称性的设计因为要均分内存的要求。这些机器运行的相当慢。
最早用汇编语言进行游戏编程。汇编语言是第一个指令式编程语言。每种类型的处理器都有它指定的汇编指令,所以对不同的处理器来说,汇编语言都不同。因为有限制的使用内存,游戏编程者都是专家,能榨干内存的最后几位和表现的像聪明的黑客一样有效率。最终游戏写好了,但是,这些游戏的代码都是难于理解或者除了原来游戏的编程者知道其他人都不知道的代码。幸运的是这不是一个问题,因为话说回来,那时候游戏都是一个人开发的。
有个大问题就是因为不同的处理器有不同的汇编语言,所以当一个新的处理器出来,所有已有的程序不得不重新写以适应最新的处理器。因此,急需出现独立于计算机处理器的编程语言。因此,Fortran和BASIC语言诞生了。BASIC语言在1970年代非常出名,因为那时候个人电脑已经诞生,比如1978年的APPLE II和1979年的IBM-PC以及它们的后代。不幸的是,这些语言没有被标准化,因为每个计算机品牌都用它自己的BASIC。
随着程序变得更加复杂,很显然需要一种更好的组织指令的方式。在过程程序设计中,相关的一些指令被放在一组程序中(或者叫做函数、方法,后者是更常见的一种叫法)。因为过程式编程语言仍然拥有指令,所以所有的过程式语言同时也是指令式语言。
一个非常著名的过程式语言就是C语言。这个语言来自贝尔实验室,运行在1970年代末的UNIX操作系统上。因为操作系统是个非常复杂的程序,贝尔实验室想用过程式语言来进行实现。于是发明了一个新的语言叫做C语言。Unix的哲学就是任何人都可以编写基于它的扩展,而且这些扩展也是用C语言实现。因此,C语言成为了1980年代最重要的过程式语言,即使在Unix世界之外。
C语言仍然大范围使用,虽然它慢但确实是对现代语言做出了影响,特别是游戏产业。这些年来,游戏程序越来越大,由团队做出来的游戏比个人做出来的越来越多了。最重要的是游戏代码可读了,可重复利用了且易于调试。最后,从终极观点来说,减少程序员在游戏上的编程时间变得必要了。虽然C语言比汇编语言好了太多,用它结构化的编写大的程序仍然非常困难。
过程式语言比如C语言允许你通过函数(方法)组合各种指令。而面向对象编程让程序员能把方法装进一个叫类的里面。类占用的内存就叫做对象。一个类可以描述比如吃豆人游戏中的幽灵。然后每个幽灵对应类的一个对象。这种编程的思维方式在游戏编程中是非常有用的。
每个人都在学习C语言编程,所以必须构思一个新的编程语言类似C,除了能让程序员使用类和对象,其他特性都要像C语言。这个语言叫做C++,第一个C++版本来自1978年,官方版本出现在1981年。
即使C++是一个标准的语言,但是C++写的基于WINOWS系统运行的程序无法在其他操作系统上运行。在苹果电脑,windows电脑或unix电脑上写一个程序是不同的,想让一个C++写的程序在不同系统中运行是很难的事情。最初,这不被认为是问题,但是当因特网变得流行起来后,让同一个程序在不同的操作系统上运行变得越来越重要了。
一个新的语言出现的时机成熟了:此语言能在不同的操作系统上使用。这个语言必须类似C++,但是也需要去除掉一些C的东西来实现简化。Java语言满足了这个语言角色。Java是1995年由硬件设备商Sun推出的。Sun公司那时使用了一个创新的商业模式:这个软件是免费的,然后公司通过提供支持来获得盈利。
Java一个特别的特性就是Java编写的程序在同一计算机上之间不会互相干扰。对于C++来说,这是很大的一个问题:如果一个错误出现,可能崩溃整台电脑。更坏的是,别有用心的程序员会借机传播病毒和间谍软件。
Java令人感兴趣的一方面是它可以运行在浏览器中。这种可能性使之可以在互联网间共享程序。但是运行Java程序需要安装插件,此外,对于Java程序没有可能性使之与浏览器产生交互。当然,浏览器的主要任务之一是展示HTML页面。HTML是文本格式语言,是超文本标记语言的缩写,它旨在提供一种方式通过一组标签来结构化文档,表明文档中不同的部分,比如标题或段落。HTML是1980年代由物理学家Berners-Lee发明的,他那时候在欧洲核子研究委员会工作。他想创造一个方式让其委员会成员可以简单使用和分享文档。因此,在一次会议中,他提议一个基于因特网的标记系统。Berners-Lee为HTML定义了少数标签。第一个HTML版本有18个标签,其中的11个仍然存在于现在的HTML中。
随着因特网变得公开,HTML成为了最常见的构造网站的语言。Mosaic,那时非常一个流行的浏览器,引进了一个新的标签,img,这个标签可以把图片嵌入到HTML中。另外,许多新的HTML语言版本被不同的组织起草制定,为了规范标准化元素,让其可以被大量的浏览器兼容。比如列表和表单。1995年,HTML的2.0版本由HTML工作组推出,这个版本将所有元素制定了单一的标准。在那之后,万维网协会开始维护和更新HTML标准。一个新的HTML标准,HTML3.2在1997年推出。在同年的12月,HTML4诞生。最后,HTML4.01在2000年成为了一个新的标准。现在,W3C实现了HTML的第5个版本,HTML5.
假如你从来没有做过网站,下面是一个简单的HTML页面:
<!DOCTYPE html>
<html>
<head>
<title>Useful website</title>
</head>
<body>
This is a very useful website.
</body>
</html>
开发浏览器的公司很快意识到它们需要让页面更动态化。最初的HTML标准(2.0)主要目的是标记文本。然而,网页浏览者需要按钮或者其他交互,因此如何表明一个用户与浏览器交互的规范就很有必要了。换句话说,网页需要变得更动态。当然,有Java程序,但是它们是完全独立运行的,因此不能修改HTML页面中的元素。
美国网景公司,开发了一个网景浏览器,在与微软的浏览器激烈竞争中争夺成为一个人人都用的浏览器。网景公司已经使用了Java语言在它一些已经存在的工具里,但是它又想设计出一个轻量级的,解释性的语言来面向那些不是专业的程序员(比如网页设计师)。这个语言可以与网页交互,动态读取或者修改网页内容。网景公司推出了一个叫做liveScript的语言来满足这种角色。不久之后,网景公司更名此语言为JavaScript,JavaScript被包含进网景浏览器版本2中。
JavaScript作为一个可以和浏览器动态交互的脚本语言迅速成功起来。微软也把它包含进了自家的浏览器Internet Explore 3.0,不过被叫做JScript,因为它与网景公司最初设计的有些许不同。1996年,网景公司报送JavaScript到欧洲计算机制造协会组织(ECMA),ECMA把它重命名为ECMAScript(尽管所有人都叫它JavaScript),在1999年,它终于成为了一个被所有浏览器支持的版本。最新的ECMAScript版本是5.1版本,在2011年推出。ECMAScript 6正在开发之中,增加了许多新的特性,比如类和函数参数的默认值。
由于JavaScript被大多数的浏览器支持,JavaScript已经变成了网页的主要编程语言。因为它最初被设计为轻量级的,解释性语言。但如今程序员用它开发更复杂的基于浏览器的应用程序。如今,JavaScript成为了唯一的与HTML绑定在一起的语言,其在不同的浏览器不同的平台中可以运行。和HTML5一起,它已经成为了网页开发的主要框架。
这本书的目标是教你游戏编程。游戏是非常有趣的(有时候富有挑战!),它们与不同的输出输入设备打交道,并且游戏创造的虚构世界相当的复杂。
直到1990年代初,游戏都还在为指定的平台开发。比如,为一个指定的游戏机写的游戏不能在其它的设备上运行,除非游戏开发者开发其游戏程序运行在不同的硬件设备上。对PC游戏来说,这种影响更糟。今日,操作系统提供了一个硬件抽象层让程序员不用理会不同类型计算机硬件间的区别。在那之前,每个游戏需要为不同的显卡或声卡提供一个驱动。因此,为一个游戏编写的代码不可能大面积为其它游戏所用。在1980年代,大型电玩相当的流行。但是为它们而编写的代码几乎没有可能复用在新的游戏中,因为计算机硬件在不断的变化之中。
随着游戏变得越来越复杂,而且操作系统也越来越独立于硬件。复用以前的游戏代码对游戏公司来说就变得越来越有意义。如果你能从你之前发行的游戏中找到渲染程序和碰撞检测程序,为何还要重新写一个呢?游戏引擎这个术语在1990年代被创造,当时毁灭战士和雷神之锤非常流行。所以当时它们的设计公司就决定许可部分游戏代码作为软件的一部分出售给其它的游戏公司。这些作为游戏引擎的代码进行销售是有利可图的,因为部分游戏公司想要支付大量的许可费来使用这些引擎到它们自己的游戏中。这些游戏公司就不用白手起家写它们自己的游戏代码,它们可以重复利用这些游戏引擎中的代码然后把注意力放在其他方面,比如图形构造,角色设计,等级设计等等。
如今有许多不同的游戏引擎可以利用。有些游戏引擎特别为一些游戏平台和操作系统设计。有些游戏引擎可以运行在任何平台上,而不用修改游戏代码。这对那些需要发布他们的游戏到不同的平台上的游戏公司是非常有用的。
现代游戏引擎为游戏开发者提供了许多功能,比如2D或3D渲染引擎,特别效果比如粒子,灯光,声音,动画,人工智能,可脚本嵌入等等。游戏引擎被频繁的使用,因为开发不同的工具是个巨大的工作而且游戏公司更加喜欢把那些时间和精力放在创造更好的环境和具有挑战性的东西上。
因为核心游戏功能和游戏本身(水平,角色等)的严格区分,许多游戏公司雇佣了比程序员更多的艺术家。然而,程序员改善游戏引擎仍然是有必要的,或者同时编写处理那些与游戏引擎无关的程序或者特定游戏的程序代码。此外,游戏公司经常开发软件来支持游戏开发,比如等级编辑程序,3D模型扩展用来加载模型和加载动画,原型建造工具等等。
对于JavaScript而言,没有一个人人都用的游戏引擎。许多人用JavaScript编写相当简单的游戏以确保游戏能运行到不同的设备上,特别是那些性能有限的设备上。因此程序员直接编写游戏使用HTML5元素比如canvas来代替使用游戏引擎。然而,游戏引擎是快速变化的。如果你在Google搜索中输入JavaScript游戏引擎,你将会找到很多游戏引擎来开发你的游戏。这本书的目的是教你如何开发游戏,但是不会使用引擎,因为我想教你这个语言的核心和它的潜力。这不是一个关于游戏引擎的故事。事实上,读完本书后,你可以构建自己的游戏引擎。我不是说你应该做那个,但是本书学到最后你能白手起家开发一个游戏并且理解一个游戏引擎是如何工作的。
这里有两种常见的开发游戏的方法。表1-1阐明了这些方法:第二种方法包含了第一种方法。当人们开始学习编程,他们首先书写代码,然后写一个紧凑的循环,然后测试,然后进行修改。相比之下,专业的程序员,花费大量的前期时间在编写代码之前进行游戏设计。
(省略图1-1)
当你想用JavaScript创造一个游戏,你需要写一个包含很多指令的程序。使用一个文本编辑器,你就可以编写你的脚本。当你写完这些代码后,你启动一个浏览器然后试着运行这个程序。当一切就绪,浏览器就会解释脚本并且执行它。
然而,大多数时间,事情没有那么简单。首先,你提供给浏览器或解释器的必须是有效的JavaScript代码。浏览器会坚持检查JavaScript代码是否符合JavaScript语言规范。如果不符合,它会抛出一个错误,然后脚本停止。当然,程序员十分努力书写JavaScript程序,但是难免出现拼写这种错误,而且编写程序的规则要求十分严格。因此,在解释这个阶段,你就会与错误邂逅。
当你解决几个小错误后,浏览器检查了全部的脚本并没有报错。下一步,浏览器运行脚本。很多情况下,你发现脚本没有按照你想的那样运行。当然,你尽力表达你想运行的脚本效果,但是概念上的错误很容易出现。
因此你回到编辑器中修改脚本,然后打开浏览器运行脚本期待不会有新的错误出现。你会发现之前的问题解决了,不料又出现了其他新的问题,仍然没有实现你想要的。你不得不重新继续修改脚本。欢迎来到程序员生活!
随着你的游戏变得越来越复杂,在做之前就写代码不是一个好主意。在开始实施之前(写和测试程序),有两个阶段需要经历。
首先,你需要设计游戏。你要创造什么类型的游戏?谁会是你游戏的观众?它是2D还是3D游戏?你想游戏是种什么玩法?游戏中会出现什么样的角色,它们都有怎样的能力?特别是当你和其他人一起开发游戏时,你不得不书写上述有关的设计文档,然后别人才知道你要开发什么样的游戏。即使你一人单独开发游戏,写游戏设计文档也是有好处的。设计游戏的阶段其实是开发游戏过程中最难的阶段之一。
一旦清楚游戏做什么,下一步就是为程序设计提供一个整体结构。这就叫做说明阶段。你还记得面向对象编程是把指令装在方法里,把方法装进类里吗?在说明阶段,你要有一个大局观,关于游戏需要的类和类中需要的方法。在这步,你只需要描述出方法将要做什么,而不是它怎么做。然而,你不能假想那些方法里不可能实现的事情,因为最终我们要实现方法。
当游戏说明完成,你就来到了游戏实现阶段,也就是编辑-解释-运行。在那之后,你可以让他人 玩你的游戏。在某些情况下,你会发现自己的某些点子没有运行的想象的那么好。因此,你会再次设计游戏,重新规划游戏和实现它。然后又让他人玩你的游戏。编辑-解释-运行包含在大规模编写游戏方式里:设计-说明-实现。虽然这本书主要注意力放在游戏实现方面,但是你最好在第三十章多读一点关于游戏设计的东西。
这部分,你将创造一些简单的JavaScript应用,在这节之前,你已经看到了一个基本的网页:
<!DOCTYPE html>
<html>
<head>
<title>Useful website</title>
</head>
<body>
This is a very useful website.
</body>
</html>
打开一个文本编辑器,比如notepad,复制粘贴这段代码。用.html后缀保存文件,双击文件用浏览器打开。你几乎看见的是一个空白的网页,如图1-2所示。在HTML里,标签用来结构化文档中的信息。你可能已经意识到它们了,因为它们被放到尖括号里面。不同类型的内容就被放在类似这样的标签里面。你可以通过标签前的斜线来区别这是一个开标签还是闭标签。比如,文档的标题被放在开标签title和闭标签titile之间。再说,title标签又是head标签的一部分。head又是html的一部分,因为它在html之间。正如你看到的那样,HTML的标签系统能让你有条理的组织文档内容。全部的HTML文档有一个树状结构,其中HTML元素是根部;根部包含了其他元素比如head与body,同理它们又包含了其他分支。
(省略图1-2)
一旦你创建了HTML文档,你就可以改变它的风格了。比如,你想改变HTML中不同内容的布局,或者你想用不同的字体或者背景色。样式定义是HTML文档的一部分。或者你可以写一个CSS文件来指定样式。
虽然这本书里不会详细说明CSS。但是我会尽量较少在浏览器中使用CSS。比如下面,简单的用CSS设置HTML与BODY边距为0.
html, body {
margin: 0;
}
如果你想让你的HTML页面使用CSS,你只需简单的在head标签里加上这行:
<link rel="stylesheet" type="text/css" href="game-layout.css"/>
我会在本书中大多数的游戏例子中用以前的CSS文件。在第十三章,我会扩展CSS便于游戏自动缩放匹配不同设备。
你可以在HTML文档中直接改变样式,而不是用CSS。实现的方法就是设置标签的属性。比如,下面的HTML页面有个style属性来改变背景色为蓝色。如图1-3所示。
<!DOCTYPE html>
<html>
<head>
<title>BasicExample</title>
</head>
<body style="background:blue">
<p>That's a very nice background</p>
</body>
</html>
(省略图1-3)
你可以改变不同的类型通过style属性值。比如,看下面的HTML:
<!DOCTYPE html>
<html>
<head>
<title>BasicExample</title>
</head>
<body>
<div style="background:blue;font-size:40px;">Hello,how are you?</div>
<div style="background:yellow;font-size:20px;">I'm doing great,thank you!</div>
</body>
</html>
如果你看文档中的body部分,你可以发现由两部分组成。每个部分由div标签关联,div用于将HTML文档划分为不同部分。你可以为不同的部分使用不同的样式。在这个例子中,第一部分有一个蓝色背景和40像素的字体,第二部分有黄色的背景和20像素的字体。(如图1-4)
(省略图1-4)
你也可以用JavaScript改变HTML的元素风格代替style属性。比如,你可以用下面的JavaScript代码来改变背景色。
<!DOCTYPE html>
<html>
<head>
<title>BasicExample</title>
<script>changeBackgroundColor = function(){
document.body.style.background = "blue";
}
document.addEventListener('DOMContentLoaded', changeBackgroundColor);
</script>
</head>
<body>
That's a very nice background.
</body>
</html>
这个例子呈现出的效果和第一个例子一模一样,但是在使用JavaScript和使用标签属性之间有一个重要的不同之处:JavaScript动态的改变了颜色。这是因为有下面这一行:
document.addEventListener('DOMContentLoaded', changeBackgroundColor);
在HTML和JavaScript中有不同的事件类型。比如,可以给HTML添加一个按钮然后当用户点击按钮时执行JavaScript代码。下面代码就是个例子(如图1-5所示):
<!DOCTYPE html>
<html>
<head>
<title>BasicExample</title>
<script>
sayHello = function(){
alert("Hello World!");
}
document.addEventListener('click', sayHello);
</script>
</head>
<body>
<button>
Click me
</button>
</body>
</html>
(省略图1-5)
这类型的动态交互因为浏览器支持JavaScript而变得可能。如果你想进行游戏编程,了解玩家如何与游戏进行交互是非常重要的。
关于新的HTML标准值得说的事情就是提供了几个标签可以让HTML更加灵活多样。其中一个非常重要的标签就是canvas标签,这个标签可以让你在HTML绘画2D或者3D图像。下面是一个简单的例子:
<!DOCTYPE html>
<html>
<head>
<title>BasicExample</title>
</head>
<body>
<div id="gameArea">
<canvas id="mycanvas" width="800" height="480"></canvas>
</div>
</body>
</html>
你可以看到主体部分有个叫gameArea的区域,这个区域有个canvas元素,它有很多属性。它有一个ID,还有宽和高。你可以通过JavaScript改变这个canvas的属性。比如,下面的例子用JavaScript改变了背景色:
<!DOCTYPE html>
<html>
<head>
<title>BasicExample</title>
<script>
changeCanvasColor = function(){
var canvas = document.getElementById("mycanvas");
var context = canvas.getContext("2d");
context.fillStyle = "blue";
context.fillRect(0, 0, canvas.width, canvas.height);
}
document.addEventListener('DOMContentLoaded', changeCanvasColor);
</script>
</head>
<body>
<div id="gameArea">
<canvas id="mycanvas" width="800" height="480"></canvas>
</div>
</body>
</html>
在changeCanvasColor函数中,首先看到canvas元素。这个元素就是你能用来画2D或3D图像的元素。在代码中有这样的元素是非常有用的,因为你可以轻松的取回canvas的值,比如宽和高。为了在画布上进行操作,你需要画布上下文。画布上下文提供了在画布上画图的函数。当你从画布上下文中获取信息,你需要表明你是想二维的还是三维的模式。这个例子中,得到的是二维的画布上下文。你使用二维画布在给定大小区域内填充了背景色。图1-6是代码运行后的效果。下面的章节将会将更多有关canvas元素东西和如何使用它来创造游戏。
(省略图1-6)
你可以用一个单独的JavaScript文件来代替在HTML中直接嵌入JavaScript脚本,例如:
<!DOCTYPE html>
<html>
<head>
<title>BasicExample</title>
<script src="BasicExample.js"></script>
</head>
<body>
<div id="gameArea">
<canvas id="mycanvas" width="800" height="480"></canvas>
</div>
</body>
</html>
然后在BasicExample.js包含下面代码:
changeCanvasColor = function(){
var canvas = document.getElementById("mycanvas");
var context = canvas.getContext("2d");
context.fillStyle = "blue";
context.fillRect(0, 0, canvas.width, canvas.height);
}
document.addEventListener('DOMContentLoader', changeCanvasColor);
在很多情况下,这样设计是很有必要的。把脚本代码与HTML文本分开来,可以让脚本代码运行在不同的网页上。这本书中的所有JavaScript代码都是与HTML分离开来的,由一个或多个JavaScript文件组成。
这章中,你学到了: