- 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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
如何将多个 TS 文件合并为一个具有正确时间戳的 TS 文件?我可以将文件合并为一个,而无需接触数据。这将生成一个可播放的文件,但时间戳不正确,因此不会显示时间线(例如在 VLC 中)。因此,我需要重
有一个视频,其实是一个HLS流(TS文件的序列) 我想取出一个 TS block 并用另一个替换它。 另一个 block 将使用相同的 FFMPEG 编码设置进行编码。 如果你想知道我为什么需要这个:
这段代码取自https://en.cppreference.com/w/cpp/utility/variant/visit using var_t = std::variant; template s
我刚刚开始使用 ts-node。这是一个非常方便的工具。运行时间看起来很清晰。但它不适用于 CLI 解决方案。我无法将参数传递到编译的脚本中。 ts-node --preserve-symlinks
如果您愿意花 2 美分来解决调用另一个文件中定义的函数的问题,我将不胜感激。我阅读了 Typescript 的文档,其中建议设置一个接口(interface)或一个模块,然后引用它,但我收到了与下面相
我在努力 tsc ts/game.ts --out test.js --module amd 在我在 webstorm 中的 typescript 项目上,但是控制台没有显示任何错误,也没有输出 t
我已经意识到有一段时间了,一些 typescript 文件有一个 .d.ts 而其他的只有一个 .ts 扩展名。 它们代表什么?有什么区别? 最佳答案 这些是 declaration files ,或
是否有一种简单的方法可以将std::variant中包含的数据移动到std::variant中? 我想可以通过一个额外的类模板来切换Ts...中的所有类型,但是我想知道是否存在一种更优雅的就地方法。
我是 Angular 的新手,正在查看文档,但我想我也应该在这里问我的问题。 我正在导出一个越来越大的类,因此我想将其分成两个文件,并将 d1、d2、d3 等存储在一个单独的文件中,然后将它们导入到
无法通过指定 ts.t 找到条目(ts 是时间戳类型) 挖oplog,想搞清楚一秒钟有多少操作。 无法通过指定时间戳字段找到条目,其他字段可以。$在蒙戈外壳中: > db.oplog.rs.findO
这是我的 Angular5 项目结构。 tsconfig.app.json 和package.json 都包含这个部分 "include": [ "/src/main.ts",
我在 Angular 10 中有一个项目,遇到了奇怪的 TS (TSLint) 错误。在我使用 $localize 的任何地方的 Visual Studio Code 中,我都收到错误消息 Canno
所以我创建了一个快速服务器,它获取一个 mp3 文件(现在存储在本地,但稍后将从 mongo db 中获取)并使用 ffmpeg 制作 .m3u8 和 .ts 文件。文件成功发送到客户端,在客户端播放
我有一个 MY_FILE.ts像这样的文件: const someFunction = (param: MY_NAMESPACE.PARAM) : boolean => { // DO SOMETHI
我过去常常在运行前编译用 TS 编写的 e2e Protractor 测试,但现在我想出了如何在运行时编译 ts 文件——ts-node 似乎是一个很好的工具。正如许多文章所说,我在 Protract
我不熟悉 mod-rewrite,我找不到任何类似的问题或解决方案。 非常感谢... 模板: {query1}_{query2}.ts ts.php?v={query1}&seg={query2} 例
我有 firestore.service.ts,在一种方法中,我在 firebase 中创建和更新了一个集合……离开该方法后,我更改了集合的“id”,但我需要这个“id”是没有丢失,因为我需要它到下一
DefinitelyTyped 有许多库的类型定义,但当 Javascript 实现与 Typescript 分离时,我常常找不到使用它们的好方法,比如当库通过 a 将自己分配给窗口的属性时 标记,
我正在寻找一种在我发布到 NPM 时忽略项目中所有 .ts 文件的好方法,我可以通过将以下内容添加到我的 .npmignore 文件中来做到这一点: *.ts 但是等等......实际上我想在我的项目
我的 Yarn 工作区结构如下: /project package.json /packages /app package.json webpack.confi
我是一名优秀的程序员,十分优秀!