这一章只学习本教程需要用到的 JavaScript。
你不需要现在就掌握所有语法,也不需要背诵一长串术语。先把代码读懂、改动、运行起来,遇到新问题再补充新知识。
3.1 变量:给数据取名字
变量可以理解成一个有名字的盒子,用来保存数据。
let score = 0
let playerName = '小猫'
const maxLife = 3
这里有三个变量:
score保存分数;playerName保存玩家名字;maxLife保存最大生命值。
let 表示这个变量之后可能改变,const 表示这个变量创建后不再重新指向别的值。
游戏运行时,分数会增加,所以使用 let:
score = score + 10
也可以写成:
score += 10
3.2 数字、文字和布尔值
游戏中经常会用到三种数据:
let speed = 5
let title = '星际小猫'
let isGameOver = false
- 数字用于位置、速度、分数和生命;
- 字符串用于名称、提示文字和按钮文字;
- 布尔值只有
true和false,适合表示开关状态。
例如:
if (isGameOver) {
console.log('游戏结束')
}
3.3 条件判断:让程序做选择
条件判断的基本形式是:
if (score >= 100) {
console.log('达到奖励分数')
} else {
console.log('继续努力')
}
程序会先判断 score >= 100 是否成立。如果成立,执行第一段代码;否则执行 else 中的代码。
游戏中经常需要做类似判断:
if (player.x < 0) {
player.x = 0
}
这段代码可以防止玩家跑到屏幕左边之外。
3.4 循环:重复处理多个对象
假设屏幕上有很多敌人,我们不希望重复写很多遍代码。可以把敌人放进数组,再用循环逐个处理。
const enemies = ['红色敌机', '蓝色敌机', '绿色敌机']
for (const enemy of enemies) {
console.log(enemy)
}
这段代码会依次打印三个敌人的名字。
游戏里更常见的写法是:
for (const enemy of enemies) {
enemy.y += enemy.speed
}
意思是:让每一个敌人都向下移动。
3.5 函数:把一组动作打包
函数是一组可以重复调用的代码。
function addScore(points) {
score += points
}
addScore(10)
addScore(20)
points 是参数。调用 addScore(10) 时,函数会把 points 当成 10。
函数也可以返回结果:
function isOutsideScreen(x, width) {
return x < 0 || x > width
}
const outside = isOutsideScreen(500, 375)
return 会把计算结果交还给调用函数的地方。
把代码拆成函数有两个好处:
- 每个函数负责一件事,阅读更容易;
- 同一段逻辑可以多次使用。
3.6 对象:描述一个游戏角色
如果只用一堆变量描述玩家,很快会变得混乱:
let playerX = 100
let playerY = 500
let playerSpeed = 4
let playerLife = 3
可以把相关数据放到同一个对象中:
const player = {
x: 100,
y: 500,
speed: 4,
life: 3
}
读取和修改对象属性:
player.x += player.speed
player.life -= 1
对象非常适合表示游戏中的实体。玩家、敌人、子弹和道具,都可以拥有自己的位置、速度和状态。
3.7 数组:保存一组对象
const bullets = []
bullets.push({
x: 100,
y: 500,
speed: 8
})
bullets 是一个数组,里面可以放很多子弹对象。
之后可以遍历它们:
for (const bullet of bullets) {
bullet.y -= bullet.speed
}
在后面的章节中,我们会学习怎样删除已经飞出屏幕的子弹。
3.8 类:批量创建结构相同的对象
当玩家、敌人和子弹需要更多属性时,可以使用 class:
class Bullet {
constructor(x, y) {
this.x = x
this.y = y
this.speed = 8
}
update() {
this.y -= this.speed
}
}
const bullet = new Bullet(100, 500)
bullet.update()
constructor 会在创建对象时运行。this 表示当前创建出来的这个对象。
你可以暂时把类理解成一个模具:
class Bullet是模具;new Bullet(100, 500)是用模具做出一颗子弹;this.x和this.y是每颗子弹自己的数据。
现在不需要深入理解原型链、继承和装饰器。先会创建、更新和使用游戏对象就够了。
3.9 调试:让程序告诉你发生了什么
最简单的调试工具是 console.log:
console.log('当前分数:', score)
console.log('玩家位置:', player.x, player.y)
运行游戏后,在开发者工具的 Console 面板里可以看到这些内容。
当画面不对时,可以暂时打印:
- 某个变量当前是什么值;
- 某个函数是否被调用;
- 某个判断是否成立;
- 某个数组里到底有多少个对象。
例如:
console.log('敌人数量:', enemies.length)
如果你认为敌人应该有 3 个,但日志显示是 0,就说明问题出在敌人创建或添加的地方,而不是绘制代码。
3.10 阅读报错信息
看到报错时,优先关注三部分:
- 报错类型;
- 报错文件;
- 报错行号。
例如,xxx is not defined 通常表示你使用了一个还没有创建的变量。Cannot read properties of undefined 通常表示你想读取一个不存在对象的属性。
不要只看最后一句中文提示,也不要立刻把整段代码删掉。先找到报错行,再检查这一行使用的变量和函数。
常见坑
坑一:把 = 和 === 混在一起
= 是赋值,=== 是比较:
score = 100
score === 100
第一句是把分数改成 100,第二句是在判断分数是否等于 100。
坑二:变量名拼写不一致
playerSpeed、playerspeed 和 player_speed 是三个不同的名字。代码编辑器不会自动替你猜测想写哪个。
坑三:数组下标从 0 开始
const colors = ['red', 'blue', 'green']
console.log(colors[0])
打印出来的是 red,不是第二个元素。第一个元素的位置是 0。
坑四:函数没有调用
写出函数并不代表函数已经执行:
function startGame() {
console.log('开始游戏')
}
startGame()
最后一行才是真正调用函数。
本章小结
本章学习了本教程最常用的 JavaScript 基础:变量、条件、循环、函数、对象、数组、类和调试。
这些知识已经足够支持我们开始写真正的小游戏。后面遇到新语法时,再结合具体功能学习,不需要现在一次性背完。
动手练习
完成下面三个小任务:
- 创建一个
player对象,包含x、y、speed和life四个属性; - 写一个
addScore函数,让分数增加指定数值; - 创建一个包含三个敌人的数组,用循环打印每个敌人的名字。
下一章预告
下一章我们会正式进入 Canvas:把背景、文字、图形和图片画到屏幕上,并让一个小方块真正动起来。