TypeScript实现一个贪吃蛇小游戏

游戏效果

文件目录

准备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()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/633517.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序开发实战案例五 | 小程序如何嵌入H5页面

在接入小程序过程中会遇到需要将 H5 页面集成到小程序中情况&#xff0c;今天我们就来聊一聊怎么把 H5 页面塞到小程序中。 本篇文章将会从下面这几个方面来介绍&#xff1a; 小程序承载页面的前期准备小程序如何承载 H5小程序和 H5 页面如何通讯小程序和 H5 页面的相互跳转 小…

安全加速SCDN是什么

安全加速SCDN&#xff08;Secure Content Delivery Network&#xff0c;SCDN&#xff09; 是集分布式DDoS防护、CC防护、WAF防护、BOT行为分析为一体的安全加速解决方案。已使用内容分发网络&#xff08;CDN&#xff09;或全站加速网络&#xff08;ECDN&#xff09;的用户&…

【JavaEE】_网络通信原理

目录 1. 网络发展史 2. 网络通信基础 1.1 IP地址 1.2 端口号 1.3 协议 1.3.1 概念 1.3.2 五元组 1.4 协议分层 1.4.1 协议分层的优点 1.4.2 协议分层的分类 1.4.3网络设备所在分层 1.4.4 两台主机通过TCP/IP协议通讯过程 1.5 封装与分用 1.5.1 封装 1.5.2 分用…

Docker 容器连接

Docker 容器连接 前面我们实现了通过网络端口来访问运行在 docker 容器内的服务。 容器中可以运行一些网络应用&#xff0c;要让外部也可以访问这些应用&#xff0c;可以通过 -P 或 -p 参数来指定端口映射。 下面我们来实现通过端口连接到一个 docker 容器。 网络端口映射 …

算法练习-A+B/财务管理/实现四舍五入/牛牛的菱形字符(题目链接+题解打卡)

难度参考 难度&#xff1a;简单 分类&#xff1a;熟悉OJ与IDE的操作 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。以下内容均为个人笔记&#xff0c;旨在督促自己认真学习。 题目 A B1. A B - AcWing题库财务管理1004:财…

VsCode + CMake构建项目 C/C++连接Mysql数据库 | 数据库增删改查C++封装 | 信息管理系统通用代码 ---- 课程笔记

这个是B站Up主&#xff1a;程序员程子青的视频 C封装Mysql增删改查操作_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1m24y1a79o/?p6&spm_id_frompageDriver&vd_sourcea934d7fc6f47698a29dac90a922ba5a3安装mysql:mysql 下载和安装和修改MYSQL8.0 数据库存储…

【现代密码学】笔记9-10.3-- 公钥(非对称加密)、混合加密理论《introduction to modern cryphtography》

【现代密码学】笔记9-10.3-- 公钥&#xff08;非对称加密&#xff09;、混合加密理论《introduction to modern cryphtography》 写在最前面8.1 公钥加密理论随机预言机模型&#xff08;Random Oracle Model&#xff0c;ROM&#xff09; 写在最前面 主要在 哈工大密码学课程 张…

深入vue响应式原理

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项&#xff0c;Vue 将遍历此对象所有的 property&#xff0c;并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。 这些 getter/setter 对用户来说是不可见的&#xff0c;但是在内部它们让 Vue …

Docker 47 个常见故障的原因和解决方法

本文针对Docker容器部署、维护过程中&#xff0c;产生的问题和故障&#xff0c;做出有针对性的说明和解决方案&#xff0c;希望可以帮助到大家去快速定位和解决类似问题故障。 Docker是一种相对使用较简单的容器&#xff0c;我们可以通过以下几种方式获取信息&#xff1a; 1、…

简单理解自动驾驶-看这篇够了!

本文主要介绍自动驾驶技术的整体框架&#xff0c;旨在从宏观理解自动驾驶技术。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;自动驾驶技术 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a…

第6章 现代通信技术

文章目录 6.1 图像与多媒体通信6.1.1 图像通信6.1.2 多媒体通信技术1、多媒体通信概念2、多媒体通信的组成3、多媒体通信的业务分类4、实用化的多媒体通信系统类型5、多媒体通信应用系统&#xff08;1&#xff09;多媒体会议电视系统&#xff08;2&#xff09;IPTV 6.2 移动通信…

【机器学习300问】12、为什么要进行特征归一化?

当线性回归模型的特征量变多之后&#xff0c;会出现不同的特征量&#xff0c;然而对于那些同是数值型的特征量为什么要做归一化处理呢&#xff1f; 一、为了消除数据特征之间的量纲影响 使得不同指标之间具有可比性。例如&#xff0c;分析一个人的身高和体重对健康的影响&…

每日一题——LeetCode1252.奇数值单元格的数目

进阶&#xff1a;你可以设计一个时间复杂度为 O(n m indices.length) 且仅用 O(n m) 额外空间的算法来解决此问题吗&#xff1f; 方法一 直接模拟&#xff1a; 创建一个n x m的矩阵&#xff0c;初始化所有元素为0&#xff0c;对于indices中的每一对[ri,ci]&#xff0c;将矩…

多色女童家居服,柔软细腻超舒适

柔软细腻到不想脱下来的 优可丝面料家居服来啦 精挑细选的可爱印花图案 让宝贝能够更快乐的进入梦乡 长度也是刚刚好合适 春夏交替的季节&#xff0c;建议多入几件换着穿

【新书推荐】Web3.0应用开发实战(从Web 2.0到Web 3.0)

第一部分 Flask简介 第1章 安装 1.1 创建应用目录 1.2 虚拟环境 1.2.1 创建虚拟环境 1.2.2 使用虚拟环境 1.3 使用pip安装Python包 1.4 使用pipregs输出包 1.5 使用requirements.txt 1.6 使用pipenv管理包 第2章 应用的基本结构 2.1 网页显示过程 2.2 初始化 2.3 路由和视图函数…

【C语言基础考研向】06运算符与表达式

文章目录 1.运算符分类 2.算术运算符及表达式 3.关系运算符与关系表达式 4.c语言运算级优先级表 课后习题自测 1.运算符分类 语言提供了13种类型的运算符,如下所示. (1)算术运算符( - * / %) . (2)关系运算符(>< >< l) . (3)逻辑运算符(l && ll) . (4)位…

一文了解【完全合作关系】下的【多智能体强化学习】

处于完全合作关系的多智能体的利益一致&#xff0c;获得的奖励相同&#xff0c;有共同的目标。比如多个工业机器人协同装配汽车&#xff0c;他们的目标是相同的&#xff0c;都希望把汽车装好。 在多智能体系统中&#xff0c;一个智能体未必能观测到全局状态 S。设第 i 号智能体…

c语言:用一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。

题目 用一个宏&#xff0c;可以将一个整数的二进制位的奇数位和偶数位交换。 如&#xff1a;01&#xff0c;是1&#xff0c;交换完是10&#xff0c;是2. 思路 1.分别取出奇数位上的数字和偶数位上的数字 举个例子&#xff1a;1001 0110 1001 0110 奇…

javaScript设计模式-工厂

它的好处是消除对象间的耦合度&#xff0c;在派生子类时提供了更大的灵活性。但盲目的把普通的构造函数扔在一边&#xff0c;并不值得提倡。如果要采一不可能另外换用一个类&#xff0c;或都不需要在运行期间在一系列可互换的类中进行选择&#xff0c;就不应该使用。这样在后期…

基于android的违章处理APP 前后端服务 -毕业设计

基于android的违章处理APP 该项目是基于android版本的违章处理APP&#xff0c;系统包含前端android服务和后端web服务&#xff0c;内容和技术都是目前比较流行的架构。 技术介绍 前端android端&#xff1a; jdk17 gradle8.0 android studio 采用2023版本 后端web端&#xff…