原文链接

译文来自我的博客

简介

如果你想用canvas做个游戏,那么来对地方了。

但是但是你至少知道javascript怎么拼写(╯‵□′)╯︵┻━┻

既然没问题,那先来一下或者下载

创建canvas标签

废话不多说,我们必须创建一个canvas标签,简单起见,用一下不喜欢的jQuery

var CANVAS_WIDTH = 480;
var CANVAS_HEIGHT = 320;

var canvasElement = $("<canvas width='" + CANVAS_WIDTH + 
                      "' height='" + CANVAS_HEIGHT + "'></canvas>");
var canvas = canvasElement.get(0).getContext("2d");
canvasElement.appendTo('body');

游戏循环

为了能够让游戏平滑动画,我们用30帧的频率。

var FPS = 30;
setInterval(function() {
  update();
  draw();
}, 1000/FPS);

现在我们可以先给这两个函数放置play,重要的是setInterval函数会定期照顾他们的。

hello world

现在我们有了这个循环,让我们开始画东西吧~

function draw() {
  canvas.fillStyle = "#000"; // Set color to black
  canvas.fillText("Sup Bro!", 50, 50);
}

注意:确认修改之后刷新一下,万一哪里不对,代码变的少还能看出哪里不对。

如果没错,那么显示的是静止的字母,虽然好看,但我们已经有了动画循环,所以我们应该很容易让他动起来。

var textX = 50;
var textY = 50;

function update() {
  textX += 1;
  textY += 1;
}

function draw() {
  canvas.fillStyle = "#000";
  canvas.fillText("Sup Bro!", textX, textY);
}

现在如果没出错,那么字母应该在移动,但是有残影出现。想想为什么会这样,因为我们没有清除之前的画面呢,so 我们加点清除画布的代码。

function draw() {
  canvas.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
  canvas.fillStyle = "#000";
  canvas.fillText("Sup Bro!", textX, textY);
}

现在可以看到字母在屏幕上移动了,恭喜你,你已经快入门了。让我们继续。

创建玩家

接下来创建一个物体用来给玩家控制,我们创建了一个简单的object:

var player = {
  color: "#00A",
  x: 220,
  y: 270,
  width: 32,
  height: 32,
  draw: function() {
    canvas.fillStyle = this.color;
    canvas.fillRect(this.x, this.y, this.width, this.height);
  }
};

我们简单地着色了这个物体,当我们清除画布的时候,画上这个物体。

function draw() {
  canvas.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
  player.draw();
}

键盘控制

使用jQuery HotKeys

使用jQuery HotKeys,这个插件提供了简单的键盘输入检测
我们可以这么绑定事件

$(document).bind("keydown", "left", function() { ... });

不用想哪个按键是哪个号码真舒服,我们刚才实现了“当玩家按上的时候,做一些事情”,碉堡的插件!

玩家的移动

键盘输入检测已经完成了,但我们还要处理键盘输入之后要做什么。
你可能会想使用事件驱动的方式去处理键盘输入,但是这样做系统不一,按键效果不一样,而且脱离了动画循环呢,这样做就可以跨系统了,保证了一致性,也让游戏更平滑了。

有一个好消息,我们有一个key_status.js的文件,提供了类似keydown.left等等。去下载的文件里找

现在我们可以去查询是否有按键了,然后我们就这么写:

function update() {
  if (keydown.left) {
    player.x -= 2;
  }

  if (keydown.right) {
    player.x += 2;
  }
}

这样玩家可以控制了。

你可能注意到玩家可以跑出屏幕,让我们限制一下玩家的位置,而且似乎控制速度有点慢,我们顺便加加速。

function update() {
  if (keydown.left) {
    player.x -= 5;
  }

  if (keydown.right) {
    player.x += 5;
  }

  player.x = player.x.clamp(0, CANVAS_WIDTH - player.width);
}

clamp这个函数可以在下载的util.js里看到

然后我们加点炮弹进去。

function update() {
  if (keydown.space) {
    player.shoot();
  }

  if (keydown.left) {
    player.x -= 5;
  }

  if (keydown.right) {
    player.x += 5;
  }

  player.x = player.x.clamp(0, CANVAS_WIDTH - player.width);
}

player.shoot = function() {
  console.log("Pew pew");
  // :) Well at least adding the key binding was easy...
};

添加更多物体

子弹
我们需要一个数组放子弹

var playerBullets = [];

接下来我们创建一个子弹原型

function Bullet(I) {
  I.active = true;

  I.xVelocity = 0;
  I.yVelocity = -I.speed;
  I.width = 3;
  I.height = 3;
  I.color = "#000";

  I.inBounds = function() {
    return I.x >= 0 && I.x <= CANVAS_WIDTH &&
      I.y >= 0 && I.y <= CANVAS_HEIGHT;
  };

  I.draw = function() {
    canvas.fillStyle = this.color;
    canvas.fillRect(this.x, this.y, this.width, this.height);
  };

  I.update = function() {
    I.x += I.xVelocity;
    I.y += I.yVelocity;

    I.active = I.active && I.inBounds();
  };

  return I;
}

但玩家射击时,我们应该实例子弹,然后添加到子弹数组中.

player.shoot = function() {
  var bulletPosition = this.midpoint();

  playerBullets.push(Bullet({
    speed: 5,
    x: bulletPosition.x,
    y: bulletPosition.y
  }));
};

player.midpoint = function() {
  return {
    x: this.x + this.width/2,
    y: this.y + this.height/2
  };
};

我们需要把子弹的动画添加到没帧的动画里,为了能让子弹变成无限的效果,我们过滤了子弹数组,只保留了激活的子弹.同时删除了已经撞到敌人的子弹.

function update() {
  ...
  playerBullets.forEach(function(bullet) {
    bullet.update();
  });

  playerBullets = playerBullets.filter(function(bullet) {
    return bullet.active;
  });
}

最后一步就是画子弹了.

function draw() {
  ...
  playerBullets.forEach(function(bullet) {
    bullet.draw();
  });
}

敌人
现在我们要像添加子弹一样添加敌人.

 enemies = [];

function Enemy(I) {
  I = I || {};

  I.active = true;
  I.age = Math.floor(Math.random() * 128);

  I.color = "#A2B";

  I.x = CANVAS_WIDTH / 4 + Math.random() * CANVAS_WIDTH / 2;
  I.y = 0;
  I.xVelocity = 0
  I.yVelocity = 2;

  I.width = 32;
  I.height = 32;

  I.inBounds = function() {
    return I.x >= 0 && I.x <= CANVAS_WIDTH &&
      I.y >= 0 && I.y <= CANVAS_HEIGHT;
  };

  I.draw = function() {
    canvas.fillStyle = this.color;
    canvas.fillRect(this.x, this.y, this.width, this.height);
  };

  I.update = function() {
    I.x += I.xVelocity;
    I.y += I.yVelocity;

    I.xVelocity = 3 * Math.sin(I.age * Math.PI / 64);

    I.age++;

    I.active = I.active && I.inBounds();
  };

  return I;
};

function update() {
  ...

  enemies.forEach(function(enemy) {
    enemy.update();
  });

  enemies = enemies.filter(function(enemy) {
    return enemy.active;
  });

  if(Math.random() < 0.1) {
    enemies.push(Enemy());
  }
};

function draw() {
  ...

  enemies.forEach(function(enemy) {
    enemy.draw();
  });
}

加载和添加图片

虽然目前这些方块飞来飞去看起来很酷,但有图片就更酷了。我们使用了一个叫sprite.js的文件,可以从下载的文件里看到。

player.sprite = Sprite("player");

player.draw = function() {
  this.sprite.draw(canvas, this.x, this.y);
};

function Enemy(I) {
  ...

  I.sprite = Sprite("enemy");

  I.draw = function() {
    this.sprite.draw(canvas, this.x, this.y);
  };

  ...
}

碰撞检测

我们已经有了很多敌人飞来飞去了,但他们没有交互呢mb打不到他们,我们是时候加点碰撞检测了.
让我们使用一个简单的方法检测:

function collides(a, b) {
  return a.x < b.x + b.width &&
         a.x + a.width > b.x &&
         a.y < b.y + b.height &&
         a.y + a.height > b.y;
}

我们需要检测如下两种碰撞:

  1. 玩家子弹和敌方飞船

  2. 玩家和敌方飞船

让我们给update加入处理碰撞之后的处理

function handleCollisions() {
  playerBullets.forEach(function(bullet) {
    enemies.forEach(function(enemy) {
      if (collides(bullet, enemy)) {
        enemy.explode();
        bullet.active = false;
      }
    });
  });

  enemies.forEach(function(enemy) {
    if (collides(enemy, player)) {
      enemy.explode();
      player.explode();
    }
  });
}

function update() {
  ...
  handleCollisions();
}

现在我们需要给敌方飞船和玩家添加爆炸效果,爆炸的同时会移除

function Enemy(I) {
  ...

  I.explode = function() {
    this.active = false;
    // Extra Credit: Add an explosion graphic
  };

  return I;
};

player.explode = function() {
  this.active = false;
  // Extra Credit: Add an explosion graphic and then end the game
};

声音

为了可玩性,我们将要添加声音效果进去,我们用到sound.js这个文件,让事情变得非常简单。

player.shoot = function() {
  Sound.play("shoot");
  ...
}

function Enemy(I) {
  ...

  I.explode = function() {
    Sound.play("explode");
    ...
  }
}

使用这些API就能很快地完成一个简单的游戏.

告别

再说一下游戏地址,也可以下载

well,我希望你开始喜欢用js和html5写简单的游戏,随着学习的深入,将来会有更多地挑战呢.

参考文献

HTML5 Canvas Cheat Sheet

HTML5 Game EnginesSF怎么自动读取gist的信息。。我在SF删了这个链接

译后感

第一次完整的翻译一篇文章,真蛋疼有些句子感觉不好翻,就随便糊弄一下,BTW,我也按这个教程写了例子,好像写完还有很多问题呢(逃

顺便吐槽一下,SF编辑器怎么没有删除线,╭(╯^╰)╮

7 条评论
binnng · 2013年10月25日

赞!先收藏以后再看,博主继续努(dan)力(teng)哈

回复

Fakefish 作者 · 2013年10月25日

真客气真客气~

回复

代码诗人芋头 · 2014年02月11日

欢迎楼主来前端乱炖也发一遍。。。。

回复

敲代码的长颈鹿 · 2014年02月11日

收藏按钮在哪呢

回复

狗屌 · 2014年02月11日

好文啊,业界良心

回复

Raymond_Yip · 2014年08月28日

赞!~果断收藏

回复

Yangfan2016 · 2016年10月24日

好文,正在学canvas动画

回复

载入中...
Fakefish Fakefish

4.1k 声望

发布于专栏

Fakefish

我说的都是错的,除非你证明我是对的。

47 人关注