利用TypeScript编写贪吃蛇游戏

这篇文章主要为大家详细介绍了如何利用TypeScript编写贪吃蛇游戏,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的可以了解一下

先来康康效果图

我接下来将讲解相关配置和代码,源码链接放在最底下了,在GitHub上。

Explanation

1. tsconfig.json配置

{ "compilerOptions": { "target": "ES2015", "module": "ES2015", "strict": true, "noEmitOnError": true } } 

此处是对编译选项进行配置

  • target: 我们将TS转译成JS的版本。
  • module: 模块化的版本。
  • strict: 所有相关的严格模式是否开启。
  • noEmitOnError: 当出现错误时是否停止编译。

2. HTML & CSS 布局相关

先来看看我们整体的布局

  Document 
SCORE:0
LEVEL:1

CSS代码

// 设置变量 @bgColor: #b7d4a8; // 清除默认样式清除默认样式 * { margin: 0; padding: 0; // 怪异模式 box-sizing: border-box; } body { font: bold 20px "Courier"; width: 100%; height: 100%; overflow: hidden; } // 设置主窗口的样式 #main { width: 360px; height: 420px; background-color: @bgColor; margin: 100px auto; border: 10px solid #000; border-radius: 40px; display: flex; flex-direction: column; align-items: center; // 主轴对齐方式 justify-content: space-around; } #stage { width: 304px; height: 304px; border: 2px solid black; position: relative; } #scoreStage { width: 300px; display: flex; justify-content: space-between; } #snack { &>div { width: 10px; height: 10px; background-color: #000; border: 1px solid @bgColor; position: absolute; } } // 食物 #food { width: 10px; height: 10px; position: absolute; left: 40px; top: 100px; display: flex; flex-flow: row wrap; justify-content: space-between; align-content: space-between; &>div { width: 4px; height: 4px; background-color: #000; transform: rotate(45deg); // border: 1px solid @bgColor; } } body { scroll-behavior: unset; } 

这里面比较有意思是可以通过@xxx来设置CSS变量

比如这里的:@bgColor: #b7d4a8;

3. TS核心逻辑

TS的核心在于Class,所以我们需要定义出非常多的类来对这个贪吃蛇小游戏进行分析。

我们先来看看这个贪吃蛇小游戏有几个主要的部分。

  • 食物
  • 分数版
  • 游戏操控

食物

食物有几个核心逻辑

食物这个类。

首先,我们需要获取其中的横纵坐标。可以设置get来获取X与Y。

其次,当蛇蛇碰到食物的时候,这个食物的位置会改变,可以设置一个change()方法。

class Food { // 属性 & 方法 // 定义食物所对应的元素 element: HTMLElement; constructor() { // 加一个 “!”表示这玩意不会为空 this.element = document.getElementById('food')!; } // 方法 // 1. 获取食物的x坐标的方法 get X() { return this.element.offsetLeft; } // 2. 获取食物的y坐标的方法 get Y() { return this.element.offsetTop; } // 修改食物位置的方法 change() { // 使用random,生成随机位置 // 蛇移动一次就是一格,大小为10 const left = Math.round(Math.random()*29)*10; const top = Math.round(Math.random()*29)*10; this.element.style.left = left + 'px'; this.element.style.top = top + 'px'; } } export default Food 

蛇的话,话头就很多了。

首先,我们需要获取到蛇头的横纵坐标,还要能够给横纵坐标赋值。

其次,我们需要有方法增加蛇的身子addBody

同时还需要增加身子移动的方式moveBody

当然还需要增加检测机制,舌头不能与身子重叠

这个比较复杂,我们分而析之

1.元素设置与constructor

// 表示蛇的元素 head: HTMLElement; // 蛇的身体,包括蛇头 bodies: HTMLCollection; // 获取蛇的容器 element: HTMLElement; constructor() { // 断言一下 | 找到蛇头 this.head = document.querySelector('#snack > div') as HTMLElement; this.element = document.getElementById('snack')! this.bodies = this.element.getElementsByTagName('div'); } 

这里面的!是用来确定存在id为snack这个元素的。

2.增加身子

addBody() { this.element.insertAdjacentHTML("beforeend", "
") }

3.移动身子

    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'; } }

4.检测机制

    checkHeadBody() { // 获取所有的身体,检查其是否和蛇头的坐标发生重叠 for(let i=1;i

5.完整代码

class Snack { // 表示蛇的元素 head: HTMLElement; // 蛇的身体,包括蛇头 bodies: HTMLCollection; // 获取蛇的容器 element: HTMLElement; constructor() { // 断言一下 | 找到蛇头 this.head = document.querySelector('#snack > div') as HTMLElement; this.element = document.getElementById('snack')! this.bodies = this.element.getElementsByTagName('div'); } // 获取蛇的坐标 get X() { return this.head.offsetLeft } get Y() { return this.head.offsetTop } // 设置蛇的坐标 set X(value) { // 新值和旧值相同,直接返回,无需修改。 if(this.X === value) return if(value <0 || value> 290) { throw new Error('您撞墙了') } // 蛇在往左走,不能往右走 if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) { // 让蛇向反方向继续移动 if(value > this.X) { // 如果新值大于旧值X,说明蛇在向右走,此时发生掉头,应该使蛇继续向左走 value = this.X - 10 } else { value = this.X + 10 } } this.moveBody() this.head.style.left = value + 'px' this.checkHeadBody() } set Y(value) { if(this.Y === value) return if(value <0 || value> 290) { throw new Error('您撞墙了') } if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) { // 让蛇向反方向继续移动 if(value > this.Y) { // 如果新值大于旧值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", "
") } // 添加一个蛇身体移动的方法 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

得分面板

这个就比较简单了,主要是得分增加的方法与等级提升的方法。

class scorePanel { // score和level用来记录分数和等级 score: number = 0; level: number = 1; scoreSpan: HTMLElement; levelSpan: HTMLElement; // 设置等级 maxLevel: number; // 设置一个变量表示多少分升级 upScore: number; // 给两个需要修改的元素赋值 constructor(maxLevel: number = 10, upScore: number = 2) { this.scoreSpan = document.getElementById('score')!; this.levelSpan = document.getElementById('level')!; this.maxLevel = maxLevel this.upScore = upScore } // method // 设置加分的方法 addScore() { // 分数自增 this.score += 1 this.scoreSpan.innerHTML = this.score + ''; // 判断一下分数是多少 if(this.score % this.upScore === 0) { this.levelUp() } } // 提升等级的方法 levelUp() { if(this.level 

控制面板

这个逻辑的核心之一是整合,之二是监控键盘keydown事件

关于整合:我们会把其中的蛇,面板,食物都整合到这个类中,所谓一个启动游戏的开关。

    snack: Snack; food: Food; scorePanel: scorePanel; arrowDirection: string = ''; // 创建一个属性用来记录游戏是否结束 isLeave: boolean = true constructor() { this.snack = new Snack(); this.food = new Food(); this.scorePanel = new scorePanel(); this.init(); } 

关于监控键盘事件

这里的核心逻辑就是监控,看是上下左右中的哪一个,然后对应的改变蛇蛇的方向。

其中蛇的移动需要不断的调用run这个函数,所以我们使用isLeave作为开关,用递归来多次调用run这个函数。

import Snack from './snack'; import Food from "./Food"; import scorePanel from './scorePanel'; class GameControl { snack: Snack; food: Food; scorePanel: scorePanel; arrowDirection: string = ''; // 创建一个属性用来记录游戏是否结束 isLeave: boolean = true constructor() { this.snack = new Snack(); this.food = new Food(); this.scorePanel = new scorePanel(); this.init(); } // 游戏的初始化方法 init() { // 绑定键盘按下的时间 // const _this = this // 如果不改变这个this,则会绑定到document上面 // document.addEventListener('keydown', _this.keyDownHandler) document.addEventListener('keydown', this.keyDownHandler.bind(this)); // 调用run方法 this.run(); } // 创建一个键盘按下的响应函数 /** *  ArrowRight Right ArrowLeft Left ArrowDown Down ArrowUp Up */ keyDownHandler(event: KeyboardEvent) { this.arrowDirection = event.key // this.run(); } // 创建一个控制蛇移动的方法 run() { // 根据方向(this.direction)来使蛇的位置改变 // 向上 top - // 向下 top + // 向左 left - // 向右 left + let X = this.snack.X; let Y = this.snack.Y; // 根据按键方向修改值 switch (this.arrowDirection) { case "ArrowUp": case "Up": // 向上移动 Y -= 10 break; case "ArrowDown": case "Down": Y += 10 break; case "ArrowLeft": case "Left": X -= 10 break case "ArrowRight": case "Right": X += 10 break } try { this.snack.X = X; this.snack.Y = Y; } catch(e: any) { alert(e.message) this.isLeave = false } this.checkEat(X, Y) // 开启定时调用 // 这是递归调用 this.isLeave && 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.snack.addBody() } } } export default GameControl 

项目源码链接

Github

以上就是利用TypeScript编写贪吃蛇游戏的详细内容,更多关于TypeScript贪吃蛇游戏的资料请关注0133技术站其它相关文章!

以上就是利用TypeScript编写贪吃蛇游戏的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » JavaScript 教程