TypeScript实战 – 贪吃蛇(1)
目的
使用typescript以及面向对象的方式编写简易版贪吃蛇的项目,实现:蛇自主移动,吃到食物检测并增加身体长度,食物随机生成,计分板,撞墙检测,键盘事件检测等基本功能。
界面
分析
总体来说,整个游戏中所含的类可以分为四个:蛇(Snake),食物(Food),计分板(Scoreboard),游戏控制器(GameController)。
- Snake:获取与设置蛇身体部分的位置,增加长度,处理撞墙,检测是否吃到自己,禁止掉头
- Food:获取与设置食物位置,随机改变位置
- Scoreboard:获取积分和等级,增加积分,增加等级
- GameController:控制游戏开始和结束,吃到食物检测,蛇移动,调用其他各对象的方法完成游戏的逻辑。
还需要一个HTML作为界面展示,具体typescript简单项目搭建,webpack配置可以看我另外一个文章讲了如何从0开始搭建简单的webpack编译ts的项目
HTML整体结构与样式不多赘述
注意的地方就是:因为每次吃到食物后,增加一段,增加的是蛇容器里面的子元素,所以我们对于蛇的样式需要加在蛇的子元素上,而蛇容器不必加样式和定位
HTML:
<html><head><title>贪吃蛇</title></head><body><div class="snake_pos20000tion"><div class="snake_body"><div class="snake_body_screen"><!-- 设置蛇 --><div class="snake"><div></div></div><!-- 设置食物 --><div class="food"></div></div><div class="snake_body_score_level"><div class="snake_body_item">level:<span class="level">0</span></div><div class="snake_body_item">grade:<span class="score">0</span></div></div></div></div></body></html>
CSS(将主要用到的列了出来):
/* 定位总游戏盒子 */.snake_postion{position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);}/* 游戏盒子 */.snake_body{width: 324px;height: 400px;background: #1e272e;border-radius: 10px;box-sizing: border-box;display: flex;flex-direction: column;overflow: hidden;padding: 10px;box-shadow: 0 0 15px #1e272e;}/* 屏幕 */.snake_body_screen{background: #485460;height: 304px;box-sizing: border-box;border-radius: 6px;border: 2px solid #808e9b;position: relative;}/* 记分板 */.snake_body_score_level{border-radius: 6px;text-align: center;font-size: 20px;color: #fff;line-height: 50px;display: flex;}/* 记分板各项 */.snake_body_item{width: 50%;font-size: 14px;text-shadow: 0 0 10px #222f3e;}/* 蛇身体 */.snake > div{width: 10px;height: 10px;background: #222f3e;border: 1px solid #808e9b;position: absolute;border-radius: 2px;}/* 蛇头 */.snake > div:nth-child(1){width: 10px;height: 10px;background: #3c6382;border: 1px solid #808e9b;position: absolute;border-radius: 2px;left: 20px;top: 20px;}/* 食物 */.food{width: 10px;height: 10px;background: #222f3e;border: 1px solid #808e9b;position: absolute;left: 120px;top: 100px;border-radius: 2px;}
各类的具体分析与实现
我们最好将各类分成各个模块,这样好维护。
因为从分析来看,食物类和计分板类是最简单的两个类,所以先将它们实现
Food类
食物类包含了获取和设置食物位置,随机改变食物位置
-
实现步骤
- 定义存放食物元素的变量
- 使用
document.querySelector
获取食物元素
- 使用
offsetTop
和
offsetLeft
获取食物的位置
get
和
set
设置食物的位置
- 因为我们屏幕的有效长宽是290px,每次移动10px,所以我们只需要取0-29的随机数,最后再乘以10就行了。使用
Math.random
和
Math.round
实现随机取数
- 赋值给食物元素的样式
-
具体代码
// 定义食物类class Food{// 定义存储元素的变量private element: HTMLElement;constructor() {// 拿到元素this.element = document.querySelector(".food")!;}get X() {// 得到left值return this.element.offsetLeft;}get Y() {// 得到top值return this.element.offsetTop;}// 吃到一个事物改变位置change() {// 这里left和top需要分别拿,不然就只能走对角线了let left = Math.round(Math.random() * 29) * 10;let top = Math.round(Math.random() * 29) * 10;// 赋值给food实现随机改变位置this.element.style.left = left + 'px';this.element.style.top = top + 'px';}}export default Food;
测试代码
项目入口文件
index.ts
增加如下测试代码
import Food from './modules/Food';const testFood = new Food();testFood.change();
然后刷新多次页面查看食物随机改变的效果
Scoreboard类
包含了获取积分和等级,增加积分,增加等级的功能
-
实现步骤
- 我们需要定义四个变量,存放等级
level
和积分
score
,等级显示元素和积分显示元素
- 如果还想自定义升级的规则和最大等级可以定义两个变量去控制
- 升级和积分增加都是先将现在的
level
和
score
自增后往各自的元素的
innerHTML
添加即可
-
具体代码
// 定义记分牌class LevelScore{// 等级private level: number = 1;// 积分private score: number = 0;// 控制升级规则(每吃多少个升一级)private levelItem: number;// 最大等级private maxLevel: number;// 显示等级的元素private levelElement: HTMLElement;// 显示积分的元素private scoreElement: HTMLElement;// 给出初始值定义初始规则:吃10个食物升一级,最高等级10及constructor(leveItem: number = 10, maxLevel: number = 10) {this.levelItem = leveItem;this.maxLevel = maxLevel;this.levelElement = document.querySelector(".level")!;this.scoreElement = document.querySelector(".score")!;}// 增加积分addScore() {this.scoreElement.innerHTML = ++this.score + '';// 如果当前的积分和升级规则规定的个数取余为0,说明达到规定个数升级if (this.score % this.levelItem === 0) {this.levelUp();}}levelUp() {if (this.level < this.maxLevel) {this.levelElement.innerHTML = ++this.level + '';}}getlevel() {return this.level;}getScore() {return this.score;}}export default LevelScore;
测试代码
项目入口文件
index.ts
增加如下测试代码
import LevelScore from './modules/LevelScore'const testLevelScore = new LevelScore();setInterval(()=>{testLevelScore.addScore();},1000)
查看页面对于我们增加分数和增加等级规则的测试