蛇的移动
......
//游戏控制器,控制其他所有类
class GameControl {
......
//判断蛇是否还活着
isAlive = true;
......
//游戏初始化
init() {
//绑定键盘按下事件
document.addEventListener('keydown', this.keyDownHandler.bind(this))
//调用run方法,使蛇移动
this.run();
}
......
//蛇移动的方法
run() {
/**
* 根据方向使蛇的位置改变
*/
let x = this.snake.X;
let y = this.snake.Y;
switch (this.direction) {
case "ArrowUp":
case "Up":
//向上 top减少
y -= 10;
break
case "ArrowDown":
case "Down":
y += 10;
break
case "ArrowLeft":
case "Left":
x -= 10;
break
case "ArrowRight":
case "Right":
x += 10;
break
}
//修改蛇的x和y
this.snake.X = x;
this.snake.Y = y;
//开启定时调用,开始每300ms调用一次
//蛇的移动应该越了来越快 根据 level计算
//如果蛇还活着,再执行setTimeout
this.isAlive && setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30);
}
}
export default GameControl;
这样我们就可以控制蛇的移动了
蛇撞墙检测
把蛇撞墙的检测写在 Snake 类中。由于蛇每次移动只移动一个方向,上、下、左、右,也就是一次只会改变 x 或者 y 的值。这里修改 set X 和 set Y 方法
//设置蛇头的坐标
set X(value: number) {
//如果新值和旧值相同,直接返回,不再修改
if(this.X === value){
return;
}
//x 的合法范围 0-290
if(value < 0 || value > 290){
//进入判断说明蛇撞墙了,抛出异常
throw new Error("蛇撞墙了!");
}
this.head.style.left = value + "px";
}
set Y(value: number) {
if(this.Y === value){
return;
}
//Y 的合法范围 0-290
if(value < 0 || value > 290){
//进入判断说明蛇撞墙了,抛出异常
throw new Error("蛇撞墙了!");
}
this.head.style.top = value + "px";
}
然后在 GameControl 中调用 set X 和 set Y 的时候捕获异常
......
//游戏控制器,控制其他所有类
class GameControl {
......
//蛇移动的方法
run() {
......
try {
//修改蛇的x和y
this.snake.X = x;
this.snake.Y = y;
} catch (e) {
alert(e);
this.isAlive = false;
}
......
}
}
export default GameControl;
这样就完成了蛇的撞墙检测
蛇吃食物检测
......
//游戏控制器,控制其他所有类
class GameControl {
......
//蛇移动的方法
run() {
/**
* 根据方向使蛇的位置改变
*/
let x = this.snake.X;
let y = this.snake.Y;
switch (this.direction) {
......
}
//检查蛇是否吃到了食物
this.checkEat(x,y);
......
}
//检测蛇是否吃到了食物的方法
checkEat(x:number,y:number){
if(x === this.food.X && y === this.food.Y){
console.log("吃到了食物");
//食物的位置要进行重置
this.food.change();
//分数增加
this.scorePanel.addScore();
//蛇增加一节
this.snake.addBody();
}
}
}
export default GameControl;
到这里我们就可以吃到食物了,每吃到食物,分数增加1分,左上角就会出现一个蛇的身体,同时食物的位置发生改变。接下来处理蛇的身体问题
蛇的身体移动
class Snake {
......
//设置蛇头的坐标
set X(value: number) {
......
//移动身体
this.moveBody();
this.head.style.left = value + "px";
}
set Y(value: number) {
......
//移动身体
this.moveBody();
this.head.style.top = value + "px";
}
......
//移动身体的方法
moveBody(){
/**
* 将后边的身体设置为前边身体的位置
* 第4节 = 第3节的位置
* 第3节 = 第2节的位置
* 第2节 = 蛇头的位置
*/
for(let i=this.bodies.length-1;i>0;i--){
//获取前边身体的位置
let x = (this.bodies[i-1] as HTMLElement).offsetLeft;
let y = (this.bodies[i-1] as HTMLElement).offsetTop;
(this.bodies[i] as HTMLElement).style.left = x+"px";
(this.bodies[i] as HTMLElement).style.top = y+"px";
}
}
}
......
这样每吃一次蛇的身体就会在后边增加一节了,不过现在还存在几个bug,接下来一个一个解决
禁止调头
现在有个bug就是当蛇在向左走的时候,我们按右,蛇会继续向右走,这样是不对的
当我们修改 x 时,是在修改水平坐标,蛇在左右移动,蛇在向左移动时,不能向右,反之亦然
class Snake {
......
//设置蛇头的坐标
set X(value: number) {
//如果新值和旧值相同,直接返回,不再修改
if (this.X === value) {
return;
}
//x 的合法范围 0-290
if (value < 0 || value > 290) {
//进入判断说明蛇撞墙了,抛出异常
throw new Error("蛇撞墙了!");
}
//如果有第2节身体 并且 第二节身体的值等于新值,也就是头到了第二节身体的位置
if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {
//console.log("水平方向发生了调头");
//如果value的值大于现在的x,说明值变大,就是向右走了
//需要让蛇继续向左走
if (value > this.X) {
value = this.X - 10;
} else {
value = this.X + 10;
}
}
//移动身体
this.moveBody();
this.head.style.left = value + "px";
}
set Y(value: number) {
if (this.Y === value) {
return;
}
//Y 的合法范围 0-290
if (value < 0 || value > 290) {
//进入判断说明蛇撞墙了,抛出异常
throw new Error("蛇撞墙了!");
}
//当我们修改y时,是在修改垂直坐标,蛇在上下移动,蛇在向上移动时,不能向下,反之亦然
if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
if (value > this.Y) {
value = this.Y - 10;
} else {
value = this.Y + 10;
}
}
//移动身体
this.moveBody();
this.head.style.top = value + "px";
}
......
}
......
禁止蛇穿过自己身体
思路很简单,只要检查蛇头和身体坐标是否重复即可,所以在蛇头的坐标更新后检查
class Snake {
......
//设置蛇头的坐标
set X(value: number) {
......
//检查是否撞到自己
this.checkBody();
}
set Y(value: number) {
......
//检查是否撞到自己
this.checkBody();
}
......
//检查蛇头和蛇身体是否相撞
checkBody() {
for (let i = 1; i < this.bodies.length; i++) {
let bd = this.bodies[i] as HTMLElement;
if(this.X === bd.offsetLeft && this.Y === bd.offsetTop){
//撞到身体,游戏结束
throw new Error("撞到自己了~~");
}
}
}
}
......
为了测试方便,我们在初始化记分牌的时候,让它最高等级是10,然后每1分升一级,修改 GameControl.ts
this.scorePanel = new ScorePanel(10,1);