- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
昨天咱们已经把贪吃蛇的页面写好了,今天咱们来写 TS 部分 。
TS 我们要用面向对象的形式去编写我们的功能,所以我们要以一个功能去定义一个对象 。
把这个项目分成几个模块,也就是几个对象功能 。
写对象的前提,我们要去定义类,用类去创建对象 。
首先我们直接 class 一个 Food 类,由于我们的食物是一个 div 所以我们的 Food 类里面必须有一个属性来存放我们的元素 。
// 食物 Food 类 class Food{ element:HTMLElement; constructor(){ // 拿到元素 id this .element = document.getElementById( ' food ' )! ; } }
元素有了,现在我们想食物还有哪些功能?
我们的食物被蛇吃掉以后是不是要消失啊,那怎么确定我的蛇吃到食物了呢,看代码 。
// 获取食物 x 轴坐标的方法 get x(){ return this .element.offsetLeft } // 获取食物 y 轴坐标的方法 get y(){ return this .element.offsetTop }
如果我们蛇的坐标和食物的坐标一样了,是不是就证明我们吃到了食物,所以我们在 Food 类里面直接写俩 get 方法 获取 offsetTop/Left 就获取到了 x y 轴坐标 。
坐标有了,但我们现在食物的位置是固定的,那能固定吗?我们的蛇吃完食物,食物的位置是不是要改变,所以我们的食物一定是随机一个位置 。
// 修改食物位置 change(){ // 生成一个随机的位置 食物的位置 最小是 0 最大是 290 // 蛇移动一次是一格,一格大小是 10 所以要求食物的坐标必须是整 10 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' }
因为我们的盒子是 300 我们的食物是 10 所以我们食物的位置最大是290,最小是0 。
而且我们设计蛇移动一次是一格也就是 10 所以我们食物的坐标必须是10的倍数 。
这样我们食物的 Food 类就完成了 。
接下来我们再写一个简单的,我们的计分板,我们的计分板有两个值,一个是积分一个是等级 。
还是同理,首先先获取元素 。
// 计分板 定义一个 ScorePanel 类 class ScorePanel{ // 积分 score = 0 lecel = 0 // 元素 scoreSpan:HTMLElement lecelSpan:HTMLElement constructor(){ this .scoreSpan = document.getElementById('score')! ; this .lecelSpan = document.getElementById('lecel')! ; } }
获取完元素之后,我们的分数和等级是不是要进行一个增加 。
addScore(){ // this.score++ this .scoreSpan.innerHTML = ++ this .score + '' ; // 判断我们多少积分升一级 if ( this .score % 10 === 0 ){ this .lecelup(); } } // 等级提升 lecelup(){ // 判断是否到达最大等级 if ( this .lecel < 10 ){ this .lecelSpan.innerHTML = ++ this .lecel + '' ; } }
蛇的类是我们这里最主要的一个类也是最难写的一个类,所以我们这里先写一个基础 。
还是跟前面的一样,我们先获取元素,但是蛇这里我们会麻烦一点,因为蛇是一个容器,我们要给他分为蛇头和蛇身子 。
// 设置我们的蛇类 class Snake{ // 蛇的容器 element:HTMLElement // 蛇的头部 head:HTMLElement // 蛇的身体(包括蛇头的) HTMLCollection 是一个集合,特点:它是会实时的刷新的 bodies:HTMLCollection constructor(){ // 容器元素 this .element = document.getElementById('snake')! ; // 获取蛇头的元素也就是我们元素里面的第一个 div this .head = document.querySelector('#snake > div')! as HTMLElement; // 获取容器里面的所有 div this .bodies = this .element.getElementsByTagName('div' ) } }
获取完蛇元素之后,我们还需要获取到蛇的位置,也就是蛇头部的位置。蛇身子暂不考虑 。
// 获取我们的蛇的坐标(蛇头) get x(){ return this .head.offsetLeft } get y(){ return this .head.offsetTop } // 设置我们蛇头的坐标 set x(value:number){ this .head.style.left = value + '' } set y(value:number){ this .head.style.top = value + '' }
这样就获取到了,设置蛇的坐标我们现在先写一个简单的,等会再完善 。
现在我们先写一个吃完食物蛇的身子增加的方法,其实这个增加就是向元素里添加 div 。
// 蛇增加身体的一个方法 addBody(){ // 向 element 中添加 div this .element.insertAdjacentHTML('beforeend','<div></div>' ) }
这样一个基础的模型就写好了,但却还有问题,我们的坐标还没有完善,而我们蛇还不能动 。
到现在谁把三个类都放到一个 TS 里面了 ?
看起来不多吗,修改起来太墨迹了,所以我们要把这三个类差分,放到不同的 TS 文件里,然后每一个文件都作为一个模块暴露出去 。
首先再 src 下边创建一个 modules 的文件夹,然后创建三个 TS 文件 。
然后每一个 TS 都作为模块暴露出去,代码为 。
export
default
class类名;
都弄完了以后我们还需要一个 TS 文件来把我们这三个 TS 进行一个整合 。
新建一个 GameControl.ts 它现在就是我们的游戏控制器,控制其他的所有类 。
首先先引入其他的类,然后来定义,方便使用,同时 GameControl 这个 TS 也是当一个模块去暴露出去的,给到我们最终的 index.ts 。
// 引入其他类 import Food from './Food' // 食物类 Food import ScorePanel from './ScorePanel' // 记分类 ScorePanel import Snake from './Snake' // 蛇 Snake // 游戏控制器,控制其他的所有类 class GameControl{ // 定义三个属性 food:Food // 食物 scorepanel:ScorePanel // 记分类 snake:Snake // 蛇 constructor(){ this .food = new Food(); this .scorepanel = new ScorePanel(); this .snake = new Snake(); this .init(); } } export default GameControl;
定义完之后,肯定要有一个游戏的开始,或者一个游戏的初始化方法 。
那第一件事就是让我们的蛇可以动,首先绑定一个键盘事件 。
// 创建一个属性来存储我们的按键,蛇的移动方向 direction:string = '' // 游戏初始化方法,调用之后游戏开始 init(){ // 键盘摁下的事件 document.addEventListener('keydown', this .keydomnHandler.bind( this )) } // ArrowUp // ArrowDown // ArrowLeft // ArrowRight // 创建一个键盘按下的响应 keydomnHandler(event:KeyboardEvent){ // 用户是否合法,用了正确的摁键 // 触发按下 修改值 // console.log(event.key) this .direction = event.key }
绑定写完了,我们现在来让蛇动起来,也就是改蛇的 left 和 top ,向上 top 减少,向下 top 增加,left 同理 。
// 创建一个属性来存储我们的按键,蛇的移动方向 direction:string = '' init(){ // 键盘摁下的事件 document.addEventListener('keydown', this .keydomnHandler.bind( this )); this .run(); } run(){ /* * * 根据方向 this.direction 来让我们的蛇改变 * 向上 top 减少 * 向下 top 增加 * 向左 left 减少 * 向右 left 增加 */ let x = this .snake.x let y = this .snake.y // 计算我们的 x y 值 switch ( this .direction){ 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 ; }; // 修改蛇的 x y 值 this .snake.x = x; this .snake.y = y; setTimeout( this .run.bind( this ), 500 - ( this .scorepanel.lecel -1) * 50 ); }
setTinmeout 重复执行我们的代码,让蛇一直动,500 的数值越大,蛇走的越慢,让它随着我们的等级来走,等级乘 50 就行 。
接下来我们来写游戏失败的方法,贪吃蛇失败无疑就是撞墙或装自己身体 。
而我们再写 class 类的时候要注意,谁的事情,尽量让谁去处理,蛇死了,是蛇自己的事情,所以我们在 Snake 也就是蛇类,去处理 。
我们修改蛇的移动是调用了,Snake 里 set x 和 set y,所以只要给这俩前边加一个限制 。
// 设置我们蛇头的坐标 set X(value:number){ // 新值和旧值相同,不会去改 if ( this .x === value){ return ; } // x 值的合法范围 也就是 0 - 290 之间 if (value < 0 || value > 290 ){ // 进入判断说明我的蛇撞墙了 throw new Error('蛇撞墙了!' ) } this .head.style.left = value + "px" } set Y(value:number){ if ( this .y === value){ return ; } // x 值的合法范围 也就是 0 - 290 之间 if (value < 0 || value > 290 ){ // 进入判断说明我的蛇撞墙了 throw new Error('蛇撞墙了!' ) } this .head.style.top = value + 'px' }
接下来就是如何吃到食物了 。
checkEat(x:number,y:number){ if (x === this .food.x && y === this .food.y){ // 食物改变位置 this .food.change() // 积分版 this .scorepanel.addScore() // 蛇添加一节 this .snake.addBody() } }
我们之前就都定义好了,直接拿来用就行了,剩下的就是我们的身体如何跟头去移动,还有撞自己身体游戏结束的方法就完成了我们的贪吃蛇 。
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' ; } }
this.bodies 就是蛇的身体长度,那我们蛇怎么前进呢,就是后边 div 的位置等于前边 div 的位置 。
这样我们的蛇身子就动起来了,接下来我们写蛇头撞身子游戏结束 。
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("撞自己了" ) } } }
之后我们把这个方法写到 set x、y里面最后一段的位置调用 。
接下来我们固定一下左右,在 set 里面写 。
// 修改 x 时候 是在修改水平坐标,蛇向左移动时候不能向右 反之同理 if ( this .bodies[1] && ( this .bodies[1] as HTMLElement).offsetTop === value){ console.log( "水平掉头了" ) if (value > this .y){ // 新值 大于 x 旧值 掉头 应该继续走 value = this .y - 10 } else { value = this .y + 10 } }
y 轴是同理的 。
这样下来我们整个一个贪吃蛇就完成了 。
以下贴出所有代码 。
Food.ts 。
// 食物 Food 类 class Food{ element:HTMLElement; constructor(){ // 拿到元素 id 直接赋值 this .element = document.getElementById('food')! ; } // 获取食物 x 轴坐标的方法 get x(){ return this .element.offsetLeft } // 获取食物 y 轴坐标的方法 get y(){ return this .element.offsetTop } // 修改食物位置 change(){ // 生成一个随机的位置 食物的位置 最小是 0 最大是 290 // 蛇移动一次是一格,一格大小是 10 所以要求食物的坐标必须是整 10 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;
ScorePanel.ts 。
// 计分板 定义一个 ScorePanel 类 class ScorePanel{ // 用来记录我们的分数和等级 score = 0 lecel = 1 // 记住我们分数和等级 元素,再构造函数中初始化 scoreSpan:HTMLElement lecelSpan:HTMLElement // 设置我们的等级最大值 maxLevel:number // 设置多少积分升一级 upScore:number constructor(maxLevel:number = 10,upScore:number = 10 ){ this .scoreSpan = document.getElementById('score')! ; this .lecelSpan = document.getElementById('lecel')! ; this .maxLevel = maxLevel; this .upScore = upScore; } // 设置一个加分的方法 addScore(){ this .score++ this .scoreSpan.innerHTML = this .score + '' ; // 判断我们多少积分升一级 if ( this .score % this .upScore === 0 ){ this .lecelup(); } } // 等级提升 lecelup(){ // 判断是否到达最大等级 if ( this .lecel < this .maxLevel){ this .lecel++ this .lecelSpan.innerHTML = this .lecel + '' ; } } } export default ScorePanel;
。
Snake.ts 。
// 设置我们的蛇类 class Snake{ // 蛇的容器 element:HTMLElement // 蛇的头部 head:HTMLElement // 蛇的身体(包括蛇头的) HTMLCollection 是一个集合,特点:它是会实时的刷新的 bodies:HTMLCollection constructor(){ // 容器元素 this .element = document.getElementById('snake')! ; // 获取蛇头的元素也就是我们元素里面的第一个 div this .head = document.querySelector('#snake > div')! as HTMLElement; // 获取容器里面的所有 div this .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){ console.log( "水平掉头了" ) 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:number){ if ( this .y === value){ return ; } // x 值的合法范围 也就是 0 - 290 之间 if (value < 0 || value > 290 ){ // 进入判断说明我的蛇撞墙了 throw new Error('蛇撞墙了!' ) } // 修改 x 时候 是在修改水平坐标,蛇向左移动时候不能向右 反之同理 if ( this .bodies[1] && ( this .bodies[1] as HTMLElement).offsetTop === value){ console.log( "水平掉头了" ) 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(){ // 向 element 中添加 div this .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 HTMLElement if ( this .x === bd.offsetLeft && this .y === bd.offsetTop){ // 进入判断说明撞了,游戏结束 throw new Error("撞自己了" ) } } } } export default Snake;
GameControl.ts 。
// 引入其他类 import Food from './Food' // 食物类 Food import ScorePanel from './ScorePanel' // 记分类 ScorePanel import Snake from './Snake' // 蛇 Snake // 游戏控制器,控制其他的所有类 class GameControl{ // 定义三个属性 food:Food // 食物 scorepanel:ScorePanel // 记分类 snake:Snake // 蛇 // 创建一个属性来存储我们的按键,蛇的移动方向 direction:string = '' // 创建一个属性来记录我们游戏是否结束 isLive = true constructor(){ this .food = new Food(); this .scorepanel = new ScorePanel(); this .snake = new Snake(); this .init(); } // 游戏初始化方法,调用之后游戏开始 init(){ // 键盘摁下的事件 console.log("执行" ) document.addEventListener( 'keydown', this .keydomnHandler.bind( this )); this .run(); } // ArrowUp // ArrowDown // ArrowLeft // ArrowRight // 创建一个键盘按下的响应 keydomnHandler(event:KeyboardEvent){ // 用户是否合法,用了正确的摁键 // 触发按下 修改值 console.log(event.key,1111 ) this .direction = event.key } // 创建蛇移动的方法 run(){ /* * * 根据方向 this.direction 来让我们的蛇改变 * 向上 top 减少 * 向下 top 增加 * 向左 left 减少 * 向右 left 增加 */ let x = this .snake.x let y = this .snake.y // 计算我们的 x y 值 switch ( this .direction){ 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 ; }; // 检查是否吃到了 this .checkEat(x,y) // 修改蛇的 x y 值 try { this .snake.X = x; this .snake.Y = y; } catch { // 捕获异常,游戏结束 alert('GAME OVER!' ) this .isLive = false } // isLive 开关 ture 的话重复执行 this .isLive && setTimeout( this .run.bind( this ), 500 - ( this .scorepanel.lecel -1) * 50 ); } // 定义一个方法,用来检查是否吃到食物 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;
index.ts 。
import "./style/index.less" import GameControl from './modules/GameControl' new GameControl();
这里是六扇有伊人,我们有缘再见 。
。
最后此篇关于自学TypeScript第五天,手把手项目搭建TS篇的文章就讲到这里了,如果你想了解更多关于自学TypeScript第五天,手把手项目搭建TS篇的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
为了构建 CentOS 6.5 OSM 切片服务器,我正在寻找一些文档和/或教程。 我试过this one正如我在我的 previous post 中所说的那样但它适用于 Ubuntu 14.04,而
我正在寻找可用于集成任何源代码控制管理系统的通用 git 桥(如 git-svn、git-p4、git-tfs)模板。 如果没有这样的模板,至少有一些关于如何在 git 端集成基本操作的说明(对于其他
1、前言 redis在我们企业级开发中是很常见的,但是单个redis不能保证我们的稳定使用,所以我们要建立一个集群。 redis有两种高可用的方案: High availabilit
简介 前提条件: 确保本机已经安装 VS Code。 确保本机已安装 SSH client, 并且确保远程主机已安装 SSH server。 VSCode 已经安装了插件 C/
为什么要用ELK ELK实际上是三个工具,Elastricsearch + Logstash + Kibana,通过ELK,用来收集日志还有进行日志分析,最后通过可视化UI进行展示。一开始业务量比
在日常办公当中,经常会需要一个共享文件夹来存放一些大家共享的资料,为了保证文件数据的安全,最佳的方式是公司内部服务器搭建FTP服务器,然后分配多个用户给相应的人员。今天给大家分享FileZilla搭
最近由于业务需要,开始进行 Flutter 的研究,由于 Flutter 的环境搭建在官网上有些细节不是很清楚,笔者重新整理输出 1. 配置镜像 由于在国内访问 Flutter
目录 1. 安装go软件包 2. 配置系统变量 3. 安装git 4. 设置go代理 5. 下载gin框架 6. 创建项目 7.
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任
上篇文章给大家介绍了使用docker compose安装FastDfs文件服务器的实例详解 今天给大家介绍如何使用 docker compose 搭建 fastDFS文件服务器,内容详情如下所示:
目录 1.创建Maven 2.Maven目录和porm.xml配置 3.配置Tomcat服务器 1.创建Maven
laravel 官方提供 homestead 和 valet 作为本地开发环境,homestead 是一个官方预封装的 vagrant box,也就是一个虚拟机,但是跟 docker 比,它占用体积
这个tutorial显示了 Razor Pages 在 Asp.Net Core 2 中的实现。但是,当我运行 CLI 命令时: dotnet aspnet-codegenerator razorp
我创建了一个单独的类库项目来存储数据库上下文和模型类。在同一解决方案中,我创建了一个 ASP.NET MVC 项目并引用了类库项目,并在项目的 Web.config 文件中包含了数据库上下文的连接字符
关于代码托管,公司是基于Gitlab自建的,它功能全而强大,但是也比较重,我个人偏向于开源、小巧、轻便、实用,所以就排除了Github,在Gogs和Gitea中选者。Gogs在Github有38
目录 1、高可用简介 1.1 高可用整体架构 1.2 基于 QJM 的共享存储系统的数据同步机制分析 1.3 NameNode 主
Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,它已经在该站点运行超过两年半了。Igor 将源代码以类BSD许可证的形式发布。 在高并发连接的情况
对于我们的 ASP.NET Core 项目,我们使用包管理器控制台中的 Scaffold-DbContext 搭建现有数据库。 每次我们做脚手架时,上下文类与所有实体一起生成,它包含调用 option
我正在使用 .net 核心 2.0。我已经安装了以下 nuget 包:1: Microsoft.AspNetCore.All2: Microsoft.EntityFrameworkCore.Tools
我正在使用 NetBeans 及其 RAD 开发功能开发 JEE6 JSF 应用程序。我想使用脚手架来节省更新 Controller 和模型 View 的时间。 OneToMany 关联在 View
我是一名优秀的程序员,十分优秀!