基于typescript下的贪吃蛇练习项目(内含源码)

Posted 是小橙鸭丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于typescript下的贪吃蛇练习项目(内含源码)相关的知识,希望对你有一定的参考价值。

各位铁汁们,这个是我练习typescript时根据资料做的一个练习项目(用babel配置了浏览器的兼容,以及用postcss兼容各个浏览器的css3(IE))

这个贪吃蛇的规则:最高等级是十级,每吃三个食物升一级,(最高等级和吃几个食物升级可以自己自定义的)贪吃蛇移动速度加快,撞到墙撞到自己身体都会显示游戏GAME OVER! 以及贪吃蛇不可以自己掉头移动等

在这里插入图片描述

首先先将我们的项目进行一些简单配置(这里就不详细描述了),小伙伴们要是不理解可以去之前写过的webpack打包ts的配置可以看看,(依然送上我们的截图和代码)

1.package.json
在这里插入图片描述

{
  "name": "part5",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \\"Error: no test specified\\" && exit 1",
    "build": "webpack",
    "start": "webpack serve --open chrome.exe"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.14.3",
    "@babel/preset-env": "^7.14.2",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^4.0.0-alpha.0",
    "core-js": "^3.12.1",
    "css-loader": "^5.2.5",
    "html-webpack-plugin": "^5.3.1",
    "less": "^4.1.1",
    "less-loader": "^9.0.0",
    "postcss": "^8.3.0",
    "postcss-loader": "^5.3.0",
    "postcss-preset-env": "^6.7.0",
    "style-loader": "^2.0.0",
    "ts-loader": "^8.3.0",
    "typescript": "^4.2.4",
    "webpack": "^5.37.1",
    "webpack-cli": "^4.7.0",
    "webpack-dev-server": "^3.11.2"
  }
}

2.tsconfig.json
在这里插入图片描述

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

3.webpack.config.js(直接上代码)
webpack配置css3兼容浏览器的
npm i -D postcss postcss-loader postcss-preset-env

// 引入一个包
const path = require('path')
// 引入html插件
let HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入clean插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

// webpack中的所有的配置信息都应该写在module.exports中
module.exports = {
	
	//指定入口文件 通常放在src 源码目录
	entry: "./src/index.ts",
	
	// 指定打包文件所在目录
	output:{
		//指定打包文件的目录
		path:path.resolve(__dirname, 'dist'),
		// 打包后的文件
		filename: "bundle.js",
		
		//告诉webpack不要使用箭头函数以及const 我要兼容IE
		environment:{
			arrowFunction:false,
			const:false

		}
	},
	
	// 指定webpack打包时要使用的模块
	module:{
		//指定要加载的规则
		rules:[
			{
				// test指定的是规则生效的文件//去匹配所有以ts结尾的文件
				test:/\\.ts$/,
				// 要使用对应的loader //从后往前执行 先执行ts-loader
				use: [
					//配置babel
					{
						//指定加载器
						loader:"babel-loader",
						//设置babel
						options:{
							// 设置预定义的环境
							presets:[
								[
									//指定环境的插件
									"@babel/preset-env",
									//配置信息
									{
										//要兼容的目标浏览器
										targets:{
											//"chrome":"88" //兼容到chrome88版本最新的  语法都能识别
											"chrome":"58",
											"ie":"11" //要兼容ie 11  不支持const语法的 
										},
										//指定corejs的版本
										"corejs": "3", //比如我们用了promise ie11是没有promise语法的 此时corejs就起作用了
										//使用core js的方式 "usage"表示按需加载
										"useBuiltIns":"usage"
									}
								]
							]
						}
					},
					//'babel-loader' 配置选项少的时候直接用就行了 多的就像上面那样配置
					'ts-loader'
				],
				// 要排除的文件
				exclude:/node_modules/	
			},

			//设置less文件的处理
			{
				test:/\\.less$/,
				use:[
					"style-loader",
					"css-loader",
					// 引入postcss
					{
						loader:"postcss-loader",
						options:{
							postcssOptions:{
								plugins:[
									[
										"postcss-preset-env",
										{
											browsers:'last 2 version' //兼容两个最新的浏览器
										}
									]
								]
							}
						}
					},
					"less-loader"
				]
			}
		]
	},
	//配置webpack插件
	plugins:[
		new CleanWebpackPlugin(),
		new HtmlWebpackPlugin({
			//title:"这是一个自定义的title"
			template:"./src/index.html" //引入你自己定义的模板
		}),
	],
	
	// 用来设置引用模块
	resolve:{
		extensions: ['.ts', '.js']
	}
}

4.我们在src目录下创建index.html和index.ts写上我们的基础页面,以及创建css目录下的index.less(这里引入less也已经在webpack配置好了, 以及我们引入postcss来兼容浏览器中的css3,兼容ie等,设置了兼容浏览器最新的两个版本)

index.html

<!DOCTYPE html>
<html land="zh">
<head>
    <meta charset="UTF-8">
    <title>贪吃蛇</title>
</head>
<body>

    <!--创建游戏的主容器-->
    <div id="main">
        <!--设置游戏的舞台-->
        <div id="stage">
            <!--设置蛇-->
            <div id="snake">
                <!--snake内部的div 表示🐍的各个部位-->
                <div>

                </div>
            </div>

            <!--设置食物-->
            <div id="food">
                <div></div>
                <div></div>
                <div></div>
                <div></div>
            </div>
        </div>

        <!--设置游戏的记分牌-->
        <div id="score-panel">
            <div>
                SCORE:<span id="score">0</span>
            </div>
            <div>
                level:<span id="level">1</span>
            </div>

        </div>

    </div>
    
</body>

</html>

index.less

//设置变量
@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: #000;
                border:1px solid @bg-color; //间隔缝隙
                //开启绝对定位
                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: red;
                transform: rotate(45deg);
            }
        }

    }

    #score-panel{
        width: 300px;
        display: flex;
        justify-content: space-between;
    }
}

现在基本的页面样式都搭好了。
重点编写我们的ts内容,我们要将各个功能分成一个个类实现

主要的index.ts

//引入样式
import './css/index.less'

//引入各个类
import Food from './moduls/Food';
import ScorePanel from './moduls/ScorePanel';


const food = new Food()
food.change()
console.log(food.X, food.Y);

const scorePanel = new ScorePanel();
for(let i=0;i<210 ;i++){
    scorePanel.addScore()
}

定义食物类Food.ts

//定义食物类Food

class Food{
    // 定义一个属性表示食物所对应的元素
    element:HTMLElement;

    constructor(){
        // 获取页面中的food元素并将其复制给element
        this.element = document.getElementById('food')!;//!表示这东西不会为空
    }

    //定义一个获取食物X轴坐标的方法
    get X(){
        return this.element.offsetLeft;
    }

    //定义一个获取食物Y轴坐标的方法
    get Y(){
        return this.element.offsetTop;
    }

    //修改食物的位置
    change(){
        //生成一个随机的位置
        //食物的位置 最小是0 最大是290(300-10 食物本身是10)
        // 蛇移动一次就说一格,一格的大小就是10 所以就要求食物的坐标必须是整10

        let top = Math.round(Math.random() * 29) * 10; //里面那个是取0-29的随机数 后面×10 
        let left = Math.round(Math.random() * 29) * 10;

        this.element.style.left = left + 'px';
        this.element.style.top = top + 'px';
    }


}


//测试代码
// const food = new Food()
// food.change()
// console.log(food.X, food.Y);

export default Food;

效果如下图所示
在这里插入图片描述

也能获取到食物的位置
在这里插入图片描述

定义表示记分牌的类ScorePanel.ts(默认最高等级是10级,每一级升级分数需要10分(可以根据自己的传入的值而改变))

//定义表示记分牌的类
class ScorePanel{
    //score 和level 用来记录分数和等级
    score = 0;
    level = 1;

    // 分数和等级所在的元素,在构造函数中进行初始化
    scoreEle:HTMLElement;
    levelEle:HTMLElement;

    //设置一个变量限制等级
    maxLevel:number;
    //设置一个变量表示升级所需要的分数
    upScore:number

    constructor(maxLevel:number = 10, upScore:number = 10){
        this.scoreEle = document.getElementById('score')!;
        this.levelEle = document.getElementById('level')!;
        this.maxLevel = maxLevel
        this.upScore = upScore
    }

    //设置一个加分的方法
    addScore(){
        this.score++;
        this.scoreEle.innerHTML = this.score + ''; //把分数赋给页面
        //判断分数是多少 能够整除10的时候就升一级
        if(this.score % this.upScore === 0){
            this.levelUp();
        }
    }
    //设置一个升级的方法
    levelUp(){
        //小于我的最大等级在给你升级
        if(this.level < this.maxLevel){
            this.levelEle.innerHTML = ++this.level + '';
        }
        
    }

}

// //测试代码
// const scorePanel = new ScorePanel();
// for(let i=0;i<210 ;i++){
//     scorePanel.addScore()
// }

export default ScorePanel

在这里插入图片描述

初步定义一个蛇的类Snake.ts

class Snake{
    // 表示蛇头的元素
    head:HTMLElement;
    // 表示蛇的身体的元素(包括蛇头)
    bodies:HTMLCollection; //会实时更新的
    // 获取蛇的容器
    element:HTMLElement;


    constructor(){
        this.element = document.getElementById('snake')!
        this.head = document.querySelector('#snake > div')! as HTMLElement;//只取一个 所以表示蛇头
        this.bodies =  this.element.getElementsByTagName('div'); //获取snake里面所有的div
    }

    //获取蛇X轴的坐标(蛇头坐标)
    get X(){
        return this.head.offsetLeft;
    }
    // 获取蛇的Y轴坐标(蛇头坐标)
    get Y(){
        return this.head.offsetTop;
    }

    //设置蛇头的坐标
    set X(value:number){
        this.head.style.left = value + 'px';
    }

    set Y(value:number){
        this.head.style.top = value + 'px';
    }


    //设置蛇增加身体的方法
    addBody(){
        //向element添加身体(添加div)
        this.element.insertAdjacentHTML("beforeend","<div></div>")
    }


}

接下来就是定义我们最重要的一个类GameControl.ts

1.首先是要定义键盘事件

注意: keydownHandler函数里面的this指向问题,下面提供了两种方法 ,一是使用bind(this)
二是使用了箭头函数,大家可以了解一下

//引入其他的类
import Snake from './Snake'
import Food from './Food'
import ScorePanel from './ScorePanel'


// 游戏控制器, 控制其他的所有类
class GameControl{
    //定义三个属性
    //蛇
    snake:Snake;
    //食物
    food:Food;
    //记分牌
    scorePanel:ScorePanel;

    //创建属性来存储蛇的移动方向(也就是我们按键的方向)
    direction:string = '';


    constructor() {
        this.snake = new Snake();
        this.food = new Food();
        this.scorePanel = new ScorePanel();   

        this.init()
    }

    //游戏的初始化方法,调用后游戏即开始
    init(){
        // 绑定键盘按键按下的事件
        //document.addEventListener('keydown', this.keydownHandler.bind(this)) //第一种解决方案-----调用bind(this) 创建一个新函数然后把(this)绑定成this.keydownHandler.bind(this)这个函数的this,所以(this)表示当前的对象 就不会有问题了
        document.addEventListener('keydown', this.keydownHandler) 

    }
    /**
     * 
     * ArrowUp  上   Up(IE)
     * ArrowDown 下  Down(IE)
     * ArrowLeft 左  Left(IE)
     * ArrowRight 右 Right(IE)
     */

    //创建一个键盘按下的响应函数
    // keydownHandler(event:KeyboardEvent){
       
    //     // 修改direction属性
    //     this.direction = event.key;

    // }

    //创建一个键盘按下的响应函数 //第二种解决方法改为箭头函数 解决this指向问题
    keydownHandler = (event:KeyboardEvent) =>{
        // 需要检查event.key的值是否合法(用户是否按了正确的按键)
        
        // 修改direction属性
        this.direction = event.key;
    }


}

export default GameControl

我们在index.ts进行测试

//引入样式
import './css/index.less'

//引入各个类
import GameControl from './moduls/GameControl'
const gameControl = new GameControl()

setInterval(()=>{
    console.log(gameControl.direction);
},1000)

结果如图所示

在这里插入图片描述

2.让蛇移动起来重点看run的方法

//引入其他的类
import Snake from './Snake'
import Food from './Food'
import ScorePanel from 以上是关于基于typescript下的贪吃蛇练习项目(内含源码)的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript教程# 16:ts + webpack + less实现贪吃蛇小游戏

基于C#开发的简易贪吃蛇

贪吃蛇JS实现(超详细,万字解析版)

用 typescript 做一个贪吃蛇小游戏

小项目特供 贪吃蛇游戏(基于C语言)

Python制作当年第一款手机游戏-贪吃蛇游戏(练习)