游戏效果
文件目录
准备1:新建index.html,编写游戏静态页面
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>贪吃蛇</title>
</head>
<body><div class="main"><div class="stage"><div id="snake"><div></div></div><div id="food"><div></div></div></div><div class="score-panel"><div>SCORE: <span id="score">0</span></div><div>level: <span id="level">1</span></div></div></div>
</body>
</html>
准备2:使用less,修改样式,编写CSS
//设置变量
@bg-color : #b7d4a8;
*{margin: 0;padding: 0;box-sizing: border-box;
}
body {font:bold 20px "Courier"
}
.main {width: 360px;height: 420px;background-color: @bg-color;margin: 100px auto;border: 10px solid black;border-radius: 40px;display: flex;flex-flow: column;align-items: center;justify-content: space-around;//游戏舞台.stage {width: 304px;height: 304px;border: 2px solid black;position: relative;//蛇的样式#snake{&>div{width: 10px;height: 10px;background-color: black;border: 1px solid @bg-color;//绝对定位position: absolute;}}#food{position: absolute;left: 40px;top: 100px;background-color: rebeccapurple;display: flex;flex-flow: row wrap;justify-content: space-between;align-content: space-between;&>div{width: 4px;height: 4px;background-color: black;transform: rotate(45deg);}}}//记分牌.score-panel {width: 300px;display: flex;justify-content: space-between;}
}
准备3:创建4个类:食物类-Food、记分牌等级类-ScorePanel、蛇类-Snake、操控类-GameControl
//食物类Food
class Food{//定义一个属性表示食物所对应的元素element : HTMLElement;constructor(){//获取页面中的food元素并将其赋值给elementthis.element = document.getElementById('food') ! ;}//定义一个获取食物X轴坐标的方法get X (){return this.element.offsetLeft}//定义一个获取食物Y轴坐标的方法get Y (){return this.element.offsetTop}//修改食物的位置change(){//生成一个随机的位置let top = Math.round(Math.random()*29)*10let left = Math.round(Math.random()*29)*10//食物的位置最小的0,最大是290//蛇移动一次就是一格,一格的大小就是10,所以就要求食物的this.element.style.left = left + 'px'this.element.style.top = top + 'px'}
}
export default Food
//记分牌的类
class ScorePanel {score = 0;level = 1 //分数和等级所在的元素,在构造函数中进行初始化scoreEle:HTMLElementlevelEle:HTMLElement//设置一个变量限制等级maxLevel :number//设置一个变量表示多少分时升级upScore : numberconstructor(maxLevel:number = 10,upScore:number = 10){this.scoreEle = document.getElementById('score')!this.levelEle = document.getElementById('level')!this.maxLevel = maxLevelthis.upScore = upScore}//设置一个加分的方法addScore(){//使分数自增this.scoreEle.innerHTML = ++this.score + ''if(this.score % this.upScore ===0){this.levelUp()}}//提升等级方法levelUp(){if(this.level < this.maxLevel){this.levelEle.innerHTML = ++this.level + ''}}
}
export default ScorePanel
//蛇类-Snake
class Snake {//表示蛇头的元素head : HTMLElement//蛇的身体bodies: HTMLCollection//获取蛇的容器element :HTMLElement constructor(){this.element = document.getElementById('snake')!this.head = document.querySelector('#snake >div') as HTMLElementthis.bodies = this.element.getElementsByTagName('div')}//获取蛇(蛇头)的坐标get X (){return this.head.offsetLeft}get Y(){return this.head.offsetTop}//设置蛇头的坐标set X(value:number){//如果新值和旧值相同,则直接返回不再修改if(this.X ===value){return}//X的值的合法范围在0-290之间if(value <0 || value >290){//进入判断说明蛇撞墙了throw new Error('蛇撞墙了')}//修改X时,是在修改水平坐标,蛇在左右移动,蛇向左移动时,不能向右移动,反之亦然if(this.bodies[1]&&(this.bodies[1] as HTMLElement).offsetLeft ===value){//如果发送了掉头,让蛇向反方向继续移动if(value >this.X){//如果新值value大于旧值X,则说明蛇在向右走,此时发送掉头,应该使蛇继续向左走value = this.X - 10;}else{//向左走value = this.X + 10}}//移动身体this.moveBody()this.head.style.left = value + 'px'this.checkHeadBody()}set Y(value:number){if(this.Y ===value){return}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大于旧值Y,则说明蛇在向下走,此时发送掉头,应该使蛇继续向上走value = this.Y - 10;}else{//向左走value = this.Y + 10}}this.moveBody()this.head.style.top = value + 'px'//检查有没有撞到自己this.checkHeadBody()}//蛇增加身体的方法addBody(){//向element中添加一个divthis.element.insertAdjacentHTML("beforeend","<div></div>")}//添加一个蛇身体移动的方法moveBody(){//将后边的身体设置为前边身体的位置//遍历获取所有的身体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';}}//检查蛇头是否撞到身体上checkHeadBody(){//获取所有身体,检查是否和蛇头的坐标发生重叠for(let i = 1 ;i<this.bodies.length;i++){let bd = this.bodies[i] as HTMLElementif(this.X ===bd.offsetLeft && this.Y === bd.offsetTop){//进入判断说明蛇头撞到了身体,游戏结束throw new Error('撞到了自己!!!')}}}
}
export default Snake
//控制类
import Food from './Food'
import ScorePanel from './ScorePanel'
import Snake from './Snake'
//游戏控制器,控制其他的所有类
class GameControl{//定义一个属性//蛇snake :Snake//食物food:Food//记分牌scorePanel:ScorePanel//创建一个属性来存储蛇的移动方向(也就是按键的方向)direction :string = ''//创建一个属性用来记录游戏是否结束isLive = trueconstructor(){this.snake = new Snake()this.food = new Food()this.scorePanel = new ScorePanel()this.init()}//游戏的初始化方法,调用后游戏即开始init(){//绑定键盘按键按下的事件document.addEventListener('keydown',this.keydownHandler.bind(this))//调用run方法,使蛇移动this.run()}//创建一个键盘按下的响应函数keydownHandler(event:KeyboardEvent){//需要检查event.key的值是否合法(用户是否按了正确的按键)//修改direction属性this.direction = event.key}//创建一个控制蛇移动的方法run (){/** 根据方向(this.direction)来使蛇的位置改变* 向上 top减少* 向下top增加* 向左 left减少* 向右left 增加 *///获取蛇现在的坐标let X = this.snake.Xlet Y = this.snake.Y//根据按键方向switch(this.direction){case "ArrowUp":case 'Up'://向上移动top减少Y -=10;break;case 'ArrowDown':case 'Down'://向下移动top增加Y+=10;break;case 'ArrowLeft':case 'Left'://向左移动left 减少X-=10;break;case 'ArrowRight':case 'Right'://向右移动left 增加X+=10;break;}//检查蛇是否吃到食物this.checkEat(X,Y)//修改蛇的X、Y方向try {this.snake.X = X;this.snake.Y = Y ; } catch (e:any) {//进入到catch,说明出现了异常,游戏结束,弹出一个提示信息alert(e.message + 'GAME OVER')//将isLive设置为falsethis.isLive = false;}//开启一个定时调用this.isLive && setTimeout(this.run.bind(this),300 -(this.scorePanel.level-1)*30)}//定义一个方法,用来检查蛇吃到食物checkEat(X:number,Y:number){if (X===this.food.X && Y===this.food.Y) {//食物对的位置要进行重置this.food.change()//分数增加this.scorePanel.addScore()//蛇要增加一节this.snake.addBody()} }
}
export default GameControl
准备4:创建index.ts文件,执行游戏
import './style/index.less'
import GameControl from './moduls/GameControl'new GameControl()