为了怀念一下心中的吃豆人形象,尝试用canvas画一个吃豆人,Just a try!
想像下,吃豆人的样子:
- 通体黄色;
- 闭嘴的时候就是实心圆加条黑线;
- 张嘴的时候就是实心球缺了1/4;
开始写:
- 首先设置一下画布相关参数:
const WIDTH = 1024;
const HEIGHT = 768;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 设置一下画布的长、宽
canvas.width = WIDTH;
canvas.height = HEIGHT;
- 设置颜色、边距等参数:
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}; // 吃豆人的起始位置
}
}
- 开始画吃豆人闭嘴的样子吧:
_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();
}
- 接下来就是张嘴的样子:
_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();
}
- 最后将两个状态定时连起来:
_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);
}
- 之后就是要一直动: 顺便提一下,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);
}
}
}
}