食物类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,包括29
let top=Math.round(Math.random()*29)*10
let left=Math.round(Math.random()*29)*10
this.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 -= 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) {
//进入到catch,说明出现了异常,游戏结束,弹出了一个提示信息
alert(e.message + 'GAME OVER!')
//将isLive设置为false
this.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,不传参就使用默认值是10
constructor(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