画出蛇的身体
我需要一个写一个Snake类,用来在地图上创造一条蛇,它有一些基本的属性,这里列举一下,direction(蛇移动的方向)、status(蛇当前的状态)、eps(用于蛇移动时判断精度)、speed(蛇移动的速度)、step(蛇移动的步数)、eyes_direction(蛇眼睛的方向)、next_cell(用于蛇移动的中间变量)等等。
这里给出渲染蛇身体以及蛇眼睛的代码
render() {
let ctx = this.gameMap.ctx;
let L = this.gameMap.L;
ctx.fillStyle = "red";
if (this.status === "dead") {
ctx.fillStyle = "white";
}
for (let cell of this.cells) {
ctx.beginPath();
ctx.arc(cell.x * L, cell.y * L, (L / 2) * 0.8, 0, 2 * Math.PI);
ctx.fill();
}
// 矩形填充蛇身体, 让蛇身看起来饱满
for (let i = 1; i < this.cells.length; i++) {
const a = this.cells[i - 1],
b = this.cells[i];
if (Math.abs(a.x - b.x) < this.eps && Math.abs(a.y - b.y) < this.eps)
continue;
if (Math.abs(a.x - b.x) < this.eps) {
ctx.fillRect(
(a.x - 0.4) * L,
Math.min(a.y, b.y) * L,
L * 0.8,
Math.abs(a.y - b.y) * L
);
} else {
ctx.fillRect(
Math.min(a.x, b.x) * L,
(a.y - 0.4) * L,
Math.abs(a.x - b.x) * L,
L * 0.8
);
}
}
// 给蛇头加上眼睛;
let eye_color = "black";
ctx.fillStyle = eye_color;
for (let i = 0; i < 2; i++) {
let eye_dx =
(this.cells[0].x + this.eye_dx[this.eyes_direction][i] * 0.2) * L;
let eye_dy =
(this.cells[0].y + this.eye_dy[this.eyes_direction][i] * 0.2) * L;
ctx.beginPath();
ctx.arc(eye_dx, eye_dy, L * 0.05, 0, Math.PI * 2);
ctx.fill();
}
}
控制蛇的移动
控制蛇在地图上移动的核心函数,update_move()
首先计算出当前蛇头的位置与目标蛇头的位置之间的欧几里得距离,即distance。
分别判断当移动完成后的处理以及还在移动中的处理
将移动完成时,status值为"idle",蛇头值为next_cell,next_cell值为null
因为位移是距离与方向的组合,所以要计算move_distance,即每个帧蛇要移动的距离,再乘上方向,也就是角度,即(dx / distance) * move_distance
同时,在适当时尾巴也要移动,具体是根据游戏的规则来。
update_move() {
let dx = this.next_cell.x - this.cells[0].x;
let dy = this.next_cell.y - this.cells[0].y;
let distance = Math.sqrt(dx * dx + dy * dy);
// 停止移动
if (distance < this.eps) {
this.status = "idle";
this.cells[0] = this.next_cell;
this.next_cell = null;
if (!this.check_tail_increasable()) {
this.cells.pop();
}
} else {
let move_distance = (this.speed * this.time_delta) / 1000;
this.cells[0].x += (dx / distance) * move_distance;
this.cells[0].y += (dy / distance) * move_distance;
// 尾巴移动
if (!this.check_tail_increasable()) {
let k = this.cells.length;
let tail = this.cells[k - 1];
let tail_target = this.cells[k - 2];
let tail_dx = tail_target.x - tail.x;
let tail_dy = tail_target.y - tail.y;
tail.x += (tail_dx / distance) * move_distance;
tail.y += (tail_dy / distance) * move_distance;
}
}
}
控制蛇的下一步移动
将next_cell值为direction对应的方向,this.next_cell = new Cell(this.cells[0].r + this.dr[d], this.cells[0].c + this.dc[d]);
处理一些全局变量,step++,status="move",eyes_direction=d,
设置新的蛇头等。
这里给出next_step()
的代码
next_step() {
const d = this.direction;
this.next_cell = new Cell(
this.cells[0].r + this.dr[d],
this.cells[0].c + this.dc[d]
);
this.step++;
this.status = "move";
this.eyes_direction = d;
let k = this.cells.length;
for (let i = k; i > 0; i--) {
this.cells[i] = JSON.parse(JSON.stringify(this.cells[i - 1]));
}
}
canvas事件监听
需要在canvas标签上加上tabindex="0"
这个属性,表示在页面加载完成后聚焦键盘事件。
需要在Map类中的start()函数中调用add_event_listender函数,即在地图加载后的第一时间为游戏类注册事件监听。
这里直接给出具体代码
add_listener_event() {
this.ctx.canvas.focus();
this.ctx.canvas.addEventListener("keydown", (e) => {
e.preventDefault();
let key = e.key;
let d = -1;
if (key === "w") d = 0;
else if (key === "d") d = 1;
else if (key === "s") d = 2;
else if (key === "a") d = 3;
if (d >= 0) this.snake.set_direction(d);
});
}
start() {
this.add_listener_event();
}
评论区