TypeScript 学习笔记 第三部分 贪吃蛇游戏

尚硅谷TypeScript教程(李立超老师TS新课)

1. 创建开发环境

  1. 创建工程,使用学习笔记的第二部分
  2. 安装css部分
javascript 复制代码
npm i -D less less-loader css-loader style-loader
  1. 对css部分处理,能够运行在低版本浏览器
javascript 复制代码
npm i -D postcss postcss-loader postcss-preset-env
  1. 修改webpack.config.json文件
javascript 复制代码
// 设置less文件的处理
            {
                test: /\.less$/,
                use: [
                    "style-loader",
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions:{
                                plugins: [
                                    [
                                        "postcss-preset-env",
                                        {
                                            browsers:"last 2 versions"
                                        }
                                    ]
                                ]
                            }
                        }
                    },
                    "less-loader"
                ]
            }

2. 工程目录结构

3. 代码部分

  • index.js
html 复制代码
import './style/index.less'

import GameControl from "./modules/GameControl";

new GameControl()
  • index.html
javascript 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>贪吃蛇</title>
</head>
<body>
<div id="main" >

    <div id="stage">
        <div id="snake">
            <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>
  • /style/index.less
css 复制代码
// 设置变量
@bg-color:#b7d4a8;

*{
  margin: 0;
  padding: 0;
  caret-color: transparent;
  // 改变盒子模型的计算方式
  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;
      border:1px solid @bg-color;
      // 开启绝对定位
      position: absolute;
      display: flex;
      flex-wrap: wrap;
      align-content: space-between;
      justify-content: space-between;
      &>div{
        width: 4px;
        height: 4px;
        background-color: black;
        transform: rotate(45deg);
      }
    }
  }
  #score-panel{
    width: 300px;
    display: flex;
    justify-content: space-between;
  }
}
  • /modules/snake.ts
javascript 复制代码
export default class Snake{
    // 蛇头
    head:HTMLElement
    // 蛇身体,包括蛇头
    bodies:HTMLCollection
    // 蛇的父容器
    element:HTMLElement
    constructor() {
        this.element = document.getElementById("snake")!
        this.bodies = this.element.children
        this.head = this.element.firstElementChild as HTMLElement
    }
    // 蛇头的坐标
    get X(){
        return this.head.offsetLeft
    }
    get Y(){
        return this.head.offsetTop
    }

    // 设置蛇头的坐标
    set X(value){

        //  当蛇的第二节与蛇头的x轴坐标重合,说明发生了水平方向的掉头
        if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {
                    if(value > this.X){
                    value =  this.X - 10
                }else{
                    value  =  this.X + 10
                }

        }
        this.moveBody()
        // 设置蛇头坐标
        this.head.style.left = value + 'px'
    }
    set Y(value){
        // 当蛇的第二节与蛇头的Y轴坐标重合,发生了垂直方向的掉头
        if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value){

            if(value > this.Y){
                value =  this.Y - 10
            }else{
                value  =  this.Y + 10
            }
        }
        this.moveBody()
        this.head.style.top = value + 'px'
    }

    // 蛇增加身体
    addBody(){
        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';


        }
    }

    checkHandBody():boolean{
        for (let i = 1; i < this.bodies.length; i++) {
            if (this.X === (this.bodies[i] as HTMLElement).offsetLeft &&
                this.Y === (this.bodies[i] as HTMLElement).offsetTop){
                return false
            }
        }
        return true
    }
}
  • /modules/ScorePanel.ts
javascript 复制代码
export default class ScorePanel{
    score = 0
    level = 1
    scoreElement : HTMLElement
    levelElement : HTMLElement
    maxLevel : number
    upScore : number
    constructor(maxLevel = 10,upScore = 10) {
        this.scoreElement = document.getElementById('score')!
        this.levelElement = document.getElementById('level')!
        this.maxLevel = maxLevel // 最大等级
        this.upScore = upScore // 多少分升一级
    }
    // 增加积分,每吃一个食物增加1分,在此处设置等级的增加
    addScore(){
        this.score ++
        this.scoreElement.innerText = this.score.toString()
        if (this.score % this.upScore === 0){
            this.addLevel()
        }
    }
    // 增加等级
    addLevel(){
        if ( ++this.level <= this.maxLevel) {
            this.levelElement.innerText = this.level.toString()
        }
    }
}

// const a = new ScorePanel()
// for (let i = 0; i < 200; i++) {
//     a.addScore()
// }
  • /modules/Gamecontrol.ts
javascript 复制代码
import Snake from "./snake";
import Food from "./Food";
import ScorePanel from "./ScorePanel";

export default class GameControl {
    snake: Snake
    food: Food
    scorePanel: ScorePanel
    dircter = '' // 保存按键的值

    constructor() {
        this.snake = new Snake()
        this.food = new Food()
        this.scorePanel = new ScorePanel()
        this.init()
    }
    // 初始化操作,监听按键事件
    init() {
        // 第二个参数如果不写bind(this),那调用的函数中的this就是document,而不是类的实例化对象
        document.addEventListener("keydown", this.keydownHandler.bind(this))
        this.run()
    }
    // 按键的回调函数
    keydownHandler(event: KeyboardEvent) {
        this.dircter = event.key
    }

    run() {
        // 蛇头的原坐标
        let x = this.snake.X
        let y = this.snake.Y
        // 按下按键后,4个方向中某一个方向的坐标需要修改
        switch (this.dircter) {
            case 'ArrowUp':
            case 'Up':
                y = y - 10
                break;
            case 'ArrowDown':
            case 'Down':
                y = y + 10
                break
            case 'ArrowLeft':
            case 'Left':
                x = x - 10
                break
            case 'ArrowRight':
            case 'Right':
                x = x + 10
                break
        }

        // 蛇头下一步是不是撞墙了
        if (x < 0 || x > 290 || y < 0 || y > 290) {
            alert("蛇撞墙了")
            return
            // 检测蛇头是否撞到自己
        } else if (!this.snake.checkHandBody()) {
            alert('你咬到自己尾巴了')
            return
        } else {
            this.eachFood()
            // 同一时间只可能有一个轴的位置发生改变
            if (this.snake.Y === y) {
                this.snake.X = x
            }
            else if (this.snake.X === x) {
                this.snake.Y = y
            }
            // 开启定时器,随着等级的升高,蛇的移动速度越来越快
            let time = this.scorePanel.level * 50
            setTimeout(this.run.bind(this), 250 - time)
        }


    }

    // 检测蛇是否吃到食物,吃到食物后:新增食物,增加积分,增加蛇的身体
    eachFood() {
        if (this.snake.X === this.food.X && this.snake.Y === this.food.Y) {
            this.food.change()
            this.scorePanel.addScore()
            this.snake.addBody()

        }
    }

}
  • /modules/Food.ts
javascript 复制代码
class Food{
    element:HTMLElement
    constructor() {
        this.element = document.getElementById('food')!
        this.change()
    }
    // 获取食物坐标
    get X(){
        return this.element.offsetLeft
    }
    get Y(){
        return this.element.offsetTop
    }
    // 设置食物坐标
    set X(value ){
        this.element.style.left = value + 'px'
    }
    set Y(value){
        this.element.style.top = value + 'px'
    }
    // 随机在地图上新增食物
    change(){
        let top = Math.round(Math.random()*29) *10
        let left = Math.round(Math.random()*29) *10
        this.X = left
        this.Y = top
    }
}

export default Food

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

4. 配置文件

  • package.json
json 复制代码
{
  "name": "part2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack serve --open"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.23.3",
    "@babel/preset-env": "^7.23.3",
    "babel-loader": "^9.1.3",
    "clean-webpack-plugin": "^4.0.0",
    "core-js": "^3.33.3",
    "css-loader": "^6.8.1",
    "html-webpack-plugin": "^5.5.3",
    "less": "^4.2.0",
    "less-loader": "^11.1.3",
    "postcss": "^8.4.31",
    "postcss-loader": "^7.3.3",
    "postcss-preset-env": "^9.3.0",
    "style-loader": "^3.3.3",
    "ts-loader": "^9.5.1",
    "typescript": "^5.3.2",
    "webpack": "^5.89.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  }
}
  • tsconfig.json
javascript 复制代码
{
  "compilerOptions": {
    "module": "ES6",
    "target": "ES6",
    "strict": true,
    "noEmitOnError": true
  },
}
  • webpack.config.js
javascript 复制代码
// 引入一个包
const path = require('path')
const HTMlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// webpack中的所有的配置信息都应该写在moudle.exports中
module.exports = {
    // 当前为开发模式
    mode: 'development',
    // 指定入口文件
    entry: "./src/index.ts",

    // 打包文件的设置项
    output: {
        // 打包后文件的目录
        path: path.resolve(__dirname,'dist'),
        //  打包后文件的名字
        filename: "bundle.js",
        // 打包后文件不使用箭头函数,不使用const
        environment: {
            arrowFunction: false,
            const:false
        },

    },

    // 指定webpack打包时要使用的模块
    module: {
        // 指定要加载的规则
        rules: [
            {
                // test指定的是规则生效的文件
                test: /\.ts$/,
                // 要使用的loader
                use: [
                    // 将新版本的js转换为旧版本的js,提高代码的兼容性
                    {
                        // 指定加载器
                        loader:'babel-loader',
                        // 设置上面这个加载器的配置项
                        options: {
                            // 设置预定义的环境(代码要在那些浏览器中运行)
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        // 要兼容的目标浏览器
                                        targets:{
                                            // 'chrome':'88',
                                            'ie':'10'
                                        },
                                        // 指定corejs的版本
                                        'corejs':'3',
                                        // 使用corejs的方式:usage 按需加载
                                        'useBuiltIns':'usage'
                                    }
                                ]
                            ]
                        }
                    },
                    'ts-loader', // 将ts转换为js
                ],
                // 要排除的文件
                exclude: /node_moudles/
            },
            // 设置less文件的处理
            {
                test: /\.less$/,
                use: [
                    "style-loader",
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions:{
                                plugins: [
                                    [
                                        "postcss-preset-env",
                                        {
                                            browsers:"last 2 versions"
                                        }
                                    ]
                                ]
                            }
                        }
                    },
                    "less-loader"
                ]
            }
        ]
    },
    // 插件
    plugins: [
        // 自动生成html文件,并且引入相关资源
        new HTMlWebpackPlugin({
            // 自动生成的网页以路径中的网页为模板
            template: "./src/index.html"
        }),
        // 自动清除上次编译后的文件
        new CleanWebpackPlugin(),
    ],

    // 设置那些文件可以作为模块可以被引用
    resolve: {
        extensions: ['.ts','.js']
    }
}
相关推荐
昨晚我输给了一辆AE8616 小时前
为什么现在不推荐使用 React.FC 了?
前端·react.js·typescript
Wect1 天前
LeetCode 130. 被围绕的区域:两种解法详解(BFS/DFS)
前端·算法·typescript
Dilettante2581 天前
这一招让 Node 后端服务启动速度提升 75%!
typescript·node.js
jonjia2 天前
模块、脚本与声明文件
typescript
jonjia2 天前
配置 TypeScript
typescript
jonjia2 天前
TypeScript 工具函数开发
typescript
jonjia2 天前
注解与断言
typescript
jonjia2 天前
IDE 超能力
typescript
jonjia2 天前
对象类型
typescript
jonjia2 天前
快速搭建 TypeScript 开发环境
typescript