|
|
|
@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
import { AcGameObject } from "./AcGameObject";
|
|
|
|
|
import { Cell } from "./Cell";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class Snake extends AcGameObject{
|
|
|
|
|
constructor(info,gamemap) {
|
|
|
|
|
super();
|
|
|
|
|
|
|
|
|
|
this.r=info.r;
|
|
|
|
|
this.c=info.c;
|
|
|
|
|
this.id=info.id;
|
|
|
|
|
this.color=info.color;
|
|
|
|
|
|
|
|
|
|
this.gamemap=gamemap;
|
|
|
|
|
|
|
|
|
|
//存入蛇身的Cell cells[0]是蛇头
|
|
|
|
|
this.cells = [new Cell(this.r,this.c)]
|
|
|
|
|
|
|
|
|
|
this.speed = 5;//5格/s
|
|
|
|
|
|
|
|
|
|
this.direction = -1;// -1表示没有指令 0 1 2 3 上 右 下 左
|
|
|
|
|
this.status = "idle"; // idle静止 move移动 die死亡
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.dr = [-1, 0, 1, 0]; // 4个方向行的偏移量
|
|
|
|
|
this.dc = [0, 1, 0, -1]; // 4个方向列的偏移量
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.next_cell = null;//下一步
|
|
|
|
|
this.step=0;
|
|
|
|
|
|
|
|
|
|
this.eps = 1e-2;//误差
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.eye_direction = 0;
|
|
|
|
|
if (this.id === 1) this.eye_direction = 2;
|
|
|
|
|
|
|
|
|
|
this.eye_dx = [
|
|
|
|
|
[-1, 1],
|
|
|
|
|
[1, 1],
|
|
|
|
|
[1, -1],
|
|
|
|
|
[-1, -1],
|
|
|
|
|
];
|
|
|
|
|
this.eye_dy = [
|
|
|
|
|
[-1, -1],
|
|
|
|
|
[-1, 1],
|
|
|
|
|
[1, 1],
|
|
|
|
|
[1, -1],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
start(){
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//为后端准备接口
|
|
|
|
|
set_direction(d){
|
|
|
|
|
this.eye_direction = d;
|
|
|
|
|
this.direction = d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//下一步(蛇头走)
|
|
|
|
|
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.step++;
|
|
|
|
|
|
|
|
|
|
const k = this.cells.length;
|
|
|
|
|
for (let i = k; i > 0; i -- ) { // 初始元素不变 每一个元素往后移动一位
|
|
|
|
|
this.cells[i] = JSON.parse(JSON.stringify(this.cells[i - 1]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.gamemap.check_valid(this.next_cell)) this.status = "die"; //死亡
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//蛇是否增长
|
|
|
|
|
check_tail_increasing(){
|
|
|
|
|
if(this.step <= 10)return true;
|
|
|
|
|
if(this.step % 3 ===1)return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update_move(){
|
|
|
|
|
//蛇头移动
|
|
|
|
|
// this.cells[0].x += this.speed * this.timedelta / 1000;//右
|
|
|
|
|
// this.cells[0].y -= this.speed * this.timedelta / 1000;//上
|
|
|
|
|
|
|
|
|
|
//相对单位 ==>计算sin cos
|
|
|
|
|
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() === false ) {//蛇不变长,尾部变化 蛇变长,尾部不变化
|
|
|
|
|
this.cells.pop();//删除尾部
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else//蛇头移动
|
|
|
|
|
{
|
|
|
|
|
//绝对单位
|
|
|
|
|
const move_distance = this.speed * this.timedelta / 1000;
|
|
|
|
|
this.cells[0].x = this.cells[0].x + move_distance * dx / distance;
|
|
|
|
|
this.cells[0].y = this.cells[0].y + move_distance * dy / distance;
|
|
|
|
|
|
|
|
|
|
if (this.check_tail_increasing() === false) { //蛇不变长,尾部变化 蛇变长,尾部不变化
|
|
|
|
|
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 = tail.x + move_distance * tail_dx / distance;
|
|
|
|
|
tail.y = tail.y + move_distance * tail_dy / distance;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update(){
|
|
|
|
|
if(this.status === "move")this.update_move();
|
|
|
|
|
this.render();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render(){
|
|
|
|
|
const L = this.gamemap.L;
|
|
|
|
|
const ctx = this.gamemap.ctx;
|
|
|
|
|
|
|
|
|
|
if(this.status === "die")this.color = "white";
|
|
|
|
|
ctx.fillStyle = this.color;
|
|
|
|
|
|
|
|
|
|
//@@@@@@@@@@粗略身体@@@@@@@@
|
|
|
|
|
for(let cell of this.cells){
|
|
|
|
|
ctx.beginPath();
|
|
|
|
|
ctx.arc(cell.x*L, cell.y*L, L * 0.4 ,0, Math.PI * 2);
|
|
|
|
|
ctx.fill();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//@@@@@@@@@@细致身体@@@@@@@@
|
|
|
|
|
for(let i = 0; i < this.cells.length -1 ;i ++ ){
|
|
|
|
|
const a = this.cells[i] , b = this.cells[i+1];
|
|
|
|
|
//两个cell近似重复
|
|
|
|
|
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)//上下关系
|
|
|
|
|
{
|
|
|
|
|
// (a.x - 0.4) * L ===>相对单位*单位长度
|
|
|
|
|
// Math.min(a.y, b.y) * L===>最左上角的 y值【因为a,b格子排放不知道】
|
|
|
|
|
// L * 0.8 ===>x方向上的宽度
|
|
|
|
|
// Math.abs(a.y - b.y) * L ===>不能只是L【因为卡顿生成头部,每一帧的值都不同】
|
|
|
|
|
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.5)* L , L , L);
|
|
|
|
|
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.eye_dx[this.eye_direction][i] * 0.15) * L;
|
|
|
|
|
const eye_y = (this.cells[0].y + this.eye_dy[this.eye_direction][i] * 0.15) * L;
|
|
|
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
|
ctx.arc(eye_x, eye_y, L * 0.05, 0, Math.PI * 2);
|
|
|
|
|
ctx.fill();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|