parent
03d1906bea
commit
997e5e6532
5 changed files with 270 additions and 18 deletions
@ -0,0 +1,9 @@ |
|||||||
|
export class Cell{ |
||||||
|
constructor(r,c) { // 传入蛇的坐标(r,c)
|
||||||
|
this.r = r; |
||||||
|
this.c = c; |
||||||
|
this.x = c + 0.5; // 将蛇的左上角坐标(r,c)转化为画布上的圆心坐标(x,y)
|
||||||
|
this.y = r + 0.5; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,173 @@ |
|||||||
|
import { AcGameObject } from "./AcGameObject"; |
||||||
|
import { Cell } from "./Cell"; |
||||||
|
|
||||||
|
export class Snake extends AcGameObject { |
||||||
|
constructor(info,gamemap) { // 传入蛇的信息和地图
|
||||||
|
super(); |
||||||
|
|
||||||
|
this.id = info.id; // 哪条蛇
|
||||||
|
this.color = info.color; // 蛇的颜色
|
||||||
|
this.gamemap = gamemap; // 方便调用地图的有关参数
|
||||||
|
this.cells = [new Cell(info.r,info.c)] // cells存放蛇的坐标,cells[0]存放蛇头
|
||||||
|
|
||||||
|
this.speed = 5; // 定义蛇的速度,每秒走5个格子
|
||||||
|
this.direction = -1; // -1表示没有指令,0,1,2,3分别表示左右下左
|
||||||
|
this.status = "idle"; // idle表示静止,move表示移动,die表示死亡
|
||||||
|
this.next_cell = null; // 下一步的目标位置
|
||||||
|
|
||||||
|
// 0,1,2,3:上右下左
|
||||||
|
this.dr = [-1,0,1,0]; // 行方向的偏移量
|
||||||
|
this.dc = [0,1,0,-1]; // 列方向的偏移量
|
||||||
|
|
||||||
|
this.step = 0; // 蛇当前的回合数
|
||||||
|
this.eps = 1e-2; // 允许误差为0.01
|
||||||
|
|
||||||
|
this.eye_direction = 0; // 左下角的蛇的眼睛朝上
|
||||||
|
|
||||||
|
if (this.id === 1) this.eye_direction = 2; // 右上角的蛇的眼睛朝下
|
||||||
|
|
||||||
|
// 定义蛇左右眼的x和y坐标的偏移量
|
||||||
|
this.eyes_dx = [ |
||||||
|
[-1,1], |
||||||
|
[1,1], |
||||||
|
[-1,1], |
||||||
|
[-1,-1] |
||||||
|
]; |
||||||
|
|
||||||
|
this.eyes_dy = [ |
||||||
|
[-1,-1], |
||||||
|
[-1,1], |
||||||
|
[1,1], |
||||||
|
[-1,1] |
||||||
|
]; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
start() { // 第一帧调用该函数
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
update() { // 更新下一帧
|
||||||
|
if (this.status === "move") { // 只有下一步的状态为移动才可以移动
|
||||||
|
this.update_move(); // 蛇进行移动
|
||||||
|
} |
||||||
|
this.render(); |
||||||
|
} |
||||||
|
|
||||||
|
check_tail_increasing() { // 前10步每步增加1,后面每三步增加1
|
||||||
|
if (this.step <= 10) return true; |
||||||
|
if (this.step % 3 === 1) return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
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.direction = -1; // 清空当前方向
|
||||||
|
this.status = "move"; // 改为移动
|
||||||
|
this.eye_direction = d; // 更新蛇的眼睛方向
|
||||||
|
this.step++; // 回合数加一
|
||||||
|
|
||||||
|
const k = this.cells.length; |
||||||
|
for (let i = k; i > 0; i--) { |
||||||
|
// 每个数组元素先后移动一位,相当于复制了一份第一个元素
|
||||||
|
// 1,2,3 => 1,1,2,3
|
||||||
|
this.cells[i] = JSON.parse(JSON.stringify(this.cells[i-1]));
|
||||||
|
} |
||||||
|
|
||||||
|
if (!this.gamemap.check_vaild(this.next_cell)) { // 下一步不合法, 蛇直接去世
|
||||||
|
this.status = "die"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
set_direction(d) { // 通过读取用户输入来设置方向
|
||||||
|
this.direction = d; |
||||||
|
} |
||||||
|
|
||||||
|
update_move() { // 蛇进行移动
|
||||||
|
// // 蛇向右移动5个格子
|
||||||
|
// this.cells[0].x += this.speed * this.timedelta / 1000; // 注意每一帧不一定是1s
|
||||||
|
|
||||||
|
const dx = this.next_cell.x - this.cells[0].x; |
||||||
|
const dy = this.next_cell.y - this.cells[0].y; |
||||||
|
|
||||||
|
const distance = Math.sqrt(dx * dx+dy * dy);
|
||||||
|
if (distance < this.eps) { // 移动到了目标点
|
||||||
|
this.cells[0] = this.next_cell; // 添加一个新蛇头
|
||||||
|
this.next_cell = null; |
||||||
|
this.status = "idle"; // 走完了,停下来
|
||||||
|
if (!this.check_tail_increasing()) { // 蛇不变长,去掉蛇尾
|
||||||
|
this.cells.pop(); // 弹出蛇尾
|
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
const move_distance = this.speed * this.timedelta / 1000; //移动距离
|
||||||
|
this.cells[0].x += move_distance * dx / distance; |
||||||
|
this.cells[0].y += move_distance * dy / distance; |
||||||
|
|
||||||
|
if (!this.check_tail_increasing()) { // 蛇不变长,蛇尾需要移动到目标位置
|
||||||
|
const k = this.cells.length; |
||||||
|
const tail = this.cells[k-1],tail_target = this.cells[k-2]; |
||||||
|
const tail_dx = tail_target.x - tail.x; |
||||||
|
const tail_dy = tail_target.y - tail.y; |
||||||
|
tail.x += move_distance * tail_dx / distance; |
||||||
|
tail.y += move_distance * tail_dy / distance; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const L = this.gamemap.L; // 单位距离
|
||||||
|
const ctx = this.gamemap.ctx; // 画布
|
||||||
|
|
||||||
|
ctx.fillStyle = this.color; // 填充颜色
|
||||||
|
|
||||||
|
if (this.status === "die") { |
||||||
|
ctx.fillStyle = "white"; |
||||||
|
} |
||||||
|
|
||||||
|
for (const cell of this.cells) { // 将蛇用圆表示
|
||||||
|
ctx.beginPath(); |
||||||
|
// 前两个参数时圆心坐标,后面的
|
||||||
|
ctx.arc(cell.x * L,cell.y * L,L / 2 * 0.8, 0, Math.PI * 2); |
||||||
|
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) { |
||||||
|
// 矩形起始点的 x轴坐标、y轴坐标、矩形width、矩形height
|
||||||
|
// 用Math.min()是因为这两个矩阵上下关系不知道
|
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// 画出蛇的眼睛
|
||||||
|
|
||||||
|
ctx.fillStyle = "black"; |
||||||
|
|
||||||
|
for (let i = 0; i < 2 ; i++) { |
||||||
|
const eye_x = (this.cells[0].x + this.eyes_dx[this.eye_direction][i] * 0.2) * L; |
||||||
|
const eye_y = (this.cells[0].y + this.eyes_dy[this.eye_direction][i] * 0.2) * L; |
||||||
|
|
||||||
|
// 画眼睛
|
||||||
|
|
||||||
|
ctx.beginPath(); |
||||||
|
ctx.arc(eye_x,eye_y,L*0.05,0,Math.PI*2); |
||||||
|
ctx.fill(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue