version-1.6[实现蛇+眼睛+移动+死亡机制]

pull/1/head
dyh 3 years ago
parent c4b16d76db
commit 4c72bc4ea2

@ -0,0 +1,25 @@
export class Cell {
constructor(r,c){
this.r=r;
this.c=c;
//转化为canvas(相对单位)
// 圆心的位置
this.x=c+0.5;
this.y=r+0.5;
}
start(){
}
update(){
this.render();
}
render(){
}
}

@ -1,5 +1,6 @@
import { AcGameObject } from "./AcGameObject";
import { Snake } from "./Snake";
import { Wall } from "./Wall";
export class GameMap extends AcGameObject
@ -13,11 +14,18 @@ export class GameMap extends AcGameObject
this.L = 0;//小正方形的边长
this.rows = 13;
this.cols = 13;
this.cols = 14;
this.walls=[];
this.inner_walls_count = 20;
this.inner_walls_count = 0;
this.snakes = [
new Snake({id:0,color:"#4876ED",r:this.rows-2,c:1},this),
new Snake({id:1,color:"#F94848",r:1,c:this.cols-2},this)
]
}
check_connectivity(g,sx,sy,tx,ty)
@ -65,9 +73,9 @@ export class GameMap extends AcGameObject
let c = parseInt(Math.random()*this.cols);
// console.log(r,c);
//对称
if(g[r][c]||g[c][r])continue;//有障碍物
if(g[r][c]||g[this.rows-1-r][this.cols-1-c])continue;//有障碍物
if(r==this.rows-2 && c==1||r==1&&c==this.cols-2)continue;//左下角右上角的蛇可能会被盖掉
g[r][c]=g[c][r]=true;//没有
g[r][c]=g[this.rows-1-r][this.cols-1-c]=true;//没有
break; //跳出
}
@ -89,13 +97,15 @@ export class GameMap extends AcGameObject
return true;//连通
}
start(){//创建一次墙(除非刷新)
//直到创建成功
for(let i=0;i<=100000000;i++)
start(){
this.add_listening_events();//聚焦&keydown事件
//创建一次墙(除非刷新) 直到创建成功
for(let i=0;i<=100000;i++)
{
if(this.create_wall())break;
}
}
@ -107,8 +117,64 @@ export class GameMap extends AcGameObject
this.ctx.canvas.height = this.L * this.rows;//长
}
//判断两条蛇是否准备好走下一步
check_ready(){
for(let snake of this.snakes){
if(snake.direction === -1)return false;//无指令
if(snake.status !== "idle")return false;//不是静止状态[只能从静止移动]
}
return true;
}
next_step(){
//让两条蛇走入下一步
for(let snake of this.snakes){
snake.next_step();
}
}
//检测目标位置是否合法:没撞到两蛇身体 和 墙
check_valid(cell){
//墙
for(let wall of this.walls)
if(wall.r === cell.r && wall.c === cell.c) return false;
//
for(let snake of this.snakes){
let k = snake.cells.length ;
if(snake.check_tail_increasing === false)//不增,头尾一起动
{
k--;//少判断一个
}
for(let i = 0; i < k; i++){
if(snake.cells[i].r === cell.r && snake.cells[i].c === cell.c) return false;
}
}
return true;
}
//获取键盘输入
add_listening_events(){
this.ctx.canvas.focus();//cts[DOM] canvas[画板]
const [snake0,snake1] = this.snakes;
//为画板绑定keydown事件
this.ctx.canvas.addEventListener("keydown",e => {
if(e.key === "w") snake0.set_direction(0);
else if(e.key === "d") snake0.set_direction(1);
else if(e.key === 's') snake0.set_direction(2);
else if(e.key === 'a') snake0.set_direction(3);
else if(e.key === 'ArrowUp' ) snake1.set_direction(0);
else if(e.key === 'ArrowRight') snake1.set_direction(1);
else if(e.key === 'ArrowDown') snake1.set_direction(2);
else if(e.key === 'ArrowLeft') snake1.set_direction(3);
// console.log("keydown")
})
}
update(){
if(this.check_ready())this.next_step();//2个蛇准备好了下一步-->走下一步
this.update_size();
this.render();
}

@ -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();
}
}
}

@ -1,6 +1,6 @@
<template>
<div ref="parent" class="gamemap">
<canvas ref="canvas"></canvas>
<canvas ref="canvas" tabindex="0"></canvas>
</div>
</template>

@ -59,6 +59,7 @@ export default {
//
setup(){
const route = useRoute();
//
let route_name = computed( () => route.name );
return {
route_name

@ -50,6 +50,7 @@ const routes = [
]
//Hash有#
const router = createRouter({
history: createWebHistory(),
routes

Loading…
Cancel
Save