阅读 88

用CANVAS画一个贪吃的吃豆人

为了怀念一下心中的吃豆人形象,尝试用canvas画一个吃豆人,Just a try!

想像下,吃豆人的样子:

  1. 通体黄色;
  2. 闭嘴的时候就是实心圆加条黑线;
  3. 张嘴的时候就是实心球缺了1/4;

开始写:

  1. 首先设置一下画布相关参数:
const WIDTH = 1024;
const HEIGHT = 768;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 设置一下画布的长、宽
canvas.width = WIDTH;
canvas.height = HEIGHT;
复制代码
  1. 设置颜色、边距等参数:
const COLOR = {
	wall: '#1E90FF',
	me: '#FFFF00',
	bean: '#fff',
	monster: '#FF4500'
};
const MARGIN = 25;  // 控制好边距
复制代码

吃豆人游戏用,有吃的豆子、吃豆人、墙、怪物等参数: 3. 定义游戏的类:

class PacmanStory {
	constructor() {
	  this.beanSize = 4;  // 豆子的大小
	  this.mySize = MARGIN / 2;  // 我的半径大小
	  this.mapWidth = 27 * MARGIN;   // 吃豆部分 (用MARGIN为单位,方便计算)
	  this.myPosition = {x:  MARGIN, y:  MARGIN};  // 吃豆人的起始位置
	}
}
复制代码
  1. 开始画吃豆人闭嘴的样子吧:
_renderMeCloseMouth(x, y) {
  ctx.beginPath();
  ctx.fillStyle = COLOR.me;
  ctx.arc(x, y, this.mySize, 0, Math.PI * 2, false);
  ctx.fill();
  ctx.closePath();
  // 画嘴上的一条线
  ctx.strokeStyle = '#000';
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x + this.mySize, y);
  ctx.stroke();
  ctx.closePath();
}
复制代码
  1. 接下来就是张嘴的样子:
_renderMeOpenMouth(x, y) {
  ctx.fillStyle = COLOR.me;
  ctx.moveTo(x, y);
  ctx.arc(x, y, this.mySize, 0, Math.PI * 2, false);
  ctx.fill();
  ctx.beginPath();
  ctx.fillStyle = '#000';
  ctx.moveTo(x, y);
  ctx.lineTo(x + Math.cos(45), y - Math.sin(45));
  ctx.arc(x, y, this.mySize, -Math.PI /4, Math.PI /4 ,false);
  ctx.lineTo(x + Math.cos(45), y + Math.sin(45));
  ctx.fill();
  ctx.closePath();
}
复制代码
  1. 最后将两个状态定时连起来:
_renderMoveMe() {
  setTimeout(() => {
	this._clearMap();
	this._renderFood();
	this._renderMeOpenMouth(this.myPosition.x, this.myPosition.y);
  }, 200);
  setTimeout(() => {
	this._clearMap();
	this._renderFood();
	this._renderMeCloseMouth(this.myPosition.x, this.myPosition.y);
  }, 400);
}
复制代码
  1. 之后就是要一直动: 顺便提一下,canvas实现动画的效果,就和电影一帧一帧连接,依靠人类眼睛的视觉缺陷,实现图片在动的效果。
init() {
  let t = setInterval(() => {
  if (this.myPosition.x >= this.mapWidth - 2* MARGIN || this.myPosition.y >= HEIGHT) {
	clearInterval(t);
  } else {
	this.myPosition.x += MARGIN; // 向右移动
	this._renderMoveMe();
  }
  }, 400);
}
复制代码

做了边界的控制。

好了这样的一个简单的吃豆人效果就做好了。当然,如果要完成吃豆人的游戏,还需要做很多的工作。

绘制吃豆地图:
const canvasMap = [
	[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
	[0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0],
	[0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0],
	[0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0],
	[0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0],
	[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
	[0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0],
	[0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0],
	[0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0],
	[0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0],
	[0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0],
	[0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0],
	[0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0],
	[0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0],
	[1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1],
	[0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0],
	[0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0],
	[0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0],
	[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0],
	[0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0],
	[0,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,1,0],
	[0,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,1,0],
	[0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0],
	[0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0],
	[0,1,1,1,1,1,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,0],
	[0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0],
	[0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0],
	[0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0],
	[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
];
复制代码
_renderFood() {
  for (let i = 0; i < canvasMap.length -1; i++ ) {
    for (let j = 0; j < canvasMap[i].length + 1; j++) {
        if ( canvasMap[j][i] === 1) {
            ctx.fillStyle = COLOR.bean;
            ctx.fillRect((i+1) * MARGIN, (j+1) * MARGIN, this.beanSize, this.beanSize);
        }
    }
  }

}
复制代码

还有很多工作都没有写完,接下来将边学边写边更新。