食物类Fod
// 定义
class Food {// 定义一个属性表示食物所对应的元素element:HTMLElement;constructor(){//加个!表示不能为空,非空断言操作符 //获取页面中的food元素并将其赋值给element this.element=document.getElementById('food')!;}// 定义一个获取食物x轴坐标的方法get X(){return this.element.offsetLeft} //定义一个获取食物Y轴坐标的方法get Y(){return this.element.offsetTop}//改变食物的位置change(){//生成一个随机的位置//食物的位置最小是0,最大时290//蛇移动一次就是一格,一格的大小就是10,所以就要求食物的大小必须是整10// Math.random()*290 0-290 不包括0,也不包括290; Math.round(Math.random()*290) 四舍五入包括0也包括290,0到290的整数 不行//包括0,包括29let top=Math.round(Math.random()*29)*10let left=Math.round(Math.random()*29)*10this.element.style.left=left+'px'this.element.style.top=top+'px'}
}
export default Food;
游戏控制器,,控制其他的所有类
//引入其他的类
import Snake from "./Snake";
import Food from "./Food";
import ScorePanel from "./ScorePanel";
//游戏控制器,控制其他的所有类
class GameControl {snake: Snake;food: Food;scorePanel: ScorePanel;//创建一个属性来存储蛇的移动方向(也就是按键的方向),默认值是空串direction: string = '';//创建一个属性用来记录游戏是否结束,false代表游戏结束isLive = true;constructor() {this.snake = new Snake();this.food = new Food();this.scorePanel = new ScorePanel(10,2);this.init();}//游戏的初始化方法,调用后游戏即开始init() {//绑定键盘按键按下的事件document.addEventListener('keydown', this.keydownHandler.bind(this))//调用run方法,使蛇移动this.run();}//创建一个键盘按下的响应函数 // ArrowUp ArrowDown ArrowLeft ArrowRight keydownHandler(event: KeyboardEvent) {//需要检查event.key的值是否合法(用户是否按了正确的按钮,也就是方向键)//修改direction属性this.direction = event.key}//创建一个控制蛇移动的方法run() {//根据方向(this.direction)来使蛇的位置改变 向上 top 减少 向下top 增加 向左left 减少 向右left 增加//获取蛇现在的坐标let X = this.snake.X;let Y = this.snake.Y;// ArrowUp Up ArrowDown Down ArrowLeft Left ArrowRight Right //根据按键的方向修改X值和Y值switch (this.direction) {case "ArrowUp":case "up"://向上移动Y -= 10;break;case "ArrowDown":case "Down"://向下移动 top值增加Y += 10;break;case "ArrowLeft":case "Left"://向左移动 left 值减少X -= 10break;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) {//进入到catch,说明出现了异常,游戏结束,弹出了一个提示信息alert(e.message + 'GAME OVER!')//将isLive设置为falsethis.isLive = false}//开启一个定时调用,每隔300毫秒去调用一下,随着等级的提高速度变慢;当isLive是true的时候开启定时器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;
记分牌的类
//定义表示记分牌的类
class ScorePanel{//score和level用来代表分数和等级score=0;level=1;//分数和等级所在的元素,在构造函数中进行初始化scoreEle:HTMLElement;levelEle:HTMLElement;//设置一个变量限制等级maxLevel:number;//设置一个变量表示多少分时升级upScore:number;//传参100就使用100,不传参就使用默认值是10constructor(maxLevel:number=10, upScore:number=10){//! 代表不可能为空this.scoreEle=document.getElementById('score')!this.levelEle=document.getElementById('level')!this.maxLevel=maxLevel;this.upScore=upScore;}//设置一个加分的方法addScore(){//使分数自增this.score++;this.scoreEle.innerHTML=this.score+''//判断分数是多少if(this.score%this.upScore===0){this.levelUp()}}//提升等级的方法levelUp(){if (this.level< this.maxLevel){this.levelEle.innerHTML=++this.level+''}}
}
// const scorePanel=new ScorePanel()// for(let i=0;i<200;i++){
// scorePanel.addScore()
// }
export default ScorePanel;
蛇
class Snake {//表示蛇头的元素head: HTMLElement;//蛇的身体(包括蛇头)bodies: HTMLCollection;//获取蛇的容器element: HTMLElement;constructor() {this.element = document.getElementById('snake')// querySelector 匹配指定 CSS 选择器的第一个元素。this.head = document.querySelector('#snake>div') as HTMLElement;// document.querySelectorAll() 是 HTML5中引入的新方法,返回文档中匹配的CSS选择器的所有元素节点列表 是静态的this.bodies = this.element!.getElementsByTagName('div')}//获取蛇的坐标(蛇头坐标)get X() {return this.head.offsetLeft;}//获取蛇的Y轴坐标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) {console.log('水平方向发生了掉头')//如果发生了掉头,让蛇向反方向继续移动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大于旧值X,则说明蛇在向上走,此时发生掉头,应该使蛇继续向下走value = this.Y - 10;} else {value = this.Y + 10}}//移动身体this.moveBody();this.head.style.top = value + 'px'//检查有没有撞到自己this.checkHeadBody();}//蛇增加身体的方法addBody() {this.element.insertAdjacentHTML('beforeend', '<div></div>')}//添加一个蛇身体移动的方法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;console.log(this.bodies[i]);//将值设置到当前身体上(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 HTMLElement;if(this.X===bd.offsetLeft&&this.Y===bd.offsetTop){//进入判断说明蛇头撞到了身体,游戏结束throw new Error('撞到自己了!')}}}}
export default Snake;
样式
@bg-color:#b7d4a8;
// 清除默认的样式
*{margin:0;padding:0;//改变盒子模型的计算方式box-sizing: border-box;
}
body{font:bold 20px "Courier";//禁用滚动条overflow-x: hidden;overflow-y: hidden;
}
//设置主窗口的样式
#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: #000; border: 1px solid @bg-color;position: absolute;} }&>#food{width:10px;height:10px;position: absolute;left:40px;top:100px;//开启弹性盒display:flex;//设置横轴为主轴,wrap表示会自动换行flex-flow:row wrap;//设置主轴和侧轴的空白空间分配到元素之间justify-content: space-between;align-content: space-around;div{width:4px;height:4px;background-color: black;//使四个div 旋转45度transform: rotate(45deg);}}
}
//记分牌
#score-panel{width:300px;display:flex;//设置株洲的对齐方式justify-content: space-between;
}
html
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>贪吃蛇</title></head><body><!-- 创建游戏的主窗口 --><div id="main"><!-- 设置游戏的舞台 --><div id="stage"><!-- 设置蛇 --><div id="snake"><!-- snake内部的div表示蛇的各部分 --><div></div></div><!-- 设置食物 --><div id="food"><!-- 添加4个小div来设置食物的样式 --><div></div><div></div><div></div><div></div></div></div><!-- 设置游戏的积分牌 --><div id="score-panel"><div>SCORE:<span id="score">0</span></div><div>level:<span id="level">1</span></div></div></div>
</body></html>
index.ts
//引入样式
import './style/index.less'
import GameControl from './modules/GameControl'
const gc=new GameControl();
尚硅谷TypeScript教程(李立超老师TS新课) https://www.bilibili.com/video/BV1Xy4y1v7S2?p=1&vd_source=c2279a533bc837e2d5b9ca3570c0a841