【TypeScript】第五部分 小项目练习(贪吃蛇)

【TypeScript】第五部分 小项目练习(贪吃蛇)



5. 小项目练习(贪吃蛇)

5.1 准备工作

package.json

我把该文件放在下面,有需要的直接创建package.json将下面的代码复制进去,然后 npm i 下载相关的依赖

{
  "name": "webpack-ts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.17.9",
    "@babel/preset-env": "^7.16.11",
    "babel-loader": "^8.2.4",
    "clean-webpack-plugin": "^4.0.0",
    "core-js": "^3.21.1",
    "html-webpack-plugin": "^5.5.0",
    "postcss": "^8.4.12",
    "postcss-loader": "^6.2.1",
    "postcss-preset-env": "^7.4.3",
    "ts-loader": "^9.2.8",
    "typescript": "^4.6.3",
    "webpack": "^5.72.0",
    "webpack-cli": "^4.9.2",
    "webpack-dev-server": "^4.8.1"
  },
  "dependencies": {
    "css-loader": "^6.7.1",
    "less": "^4.1.2",
    "less-loader": "^10.2.0",
    "style-loader": "^3.3.1"
  }
}

webpack.config.js

const {resolve}  = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')

module.exports = {
    entry:'./src/index.ts',
    output:{
        filename:"js/index.js",
        path:resolve(__dirname,'build')
    },
    module:{
        rules:[
            {   //处理ts
                test:/\.ts$/,
                use:[
                    //配置babel
                    {
                        //加载器
                        loader:'babel-loader',
                        options:{
                            //预设环境
                            presets:[
                                [
                                    //指定预设环境
                                    "@babel/preset-env",
                                    {
                                        //兼容目标浏览器
                                        targets:{
                                            "chrome":58,
                                            "ie":11
                                        },
                                        //corejs的版本
                                        "corejs":3,
                                        //配置按需加载
                                        "useBuiltIns":"usage"
                                    }
                                ]
                            ]
                        }
                    },
                    //将ts转为js
                    {loader:'ts-loader'}
                ],
                exclude:/node_modules/
            },
            {
                //处理less
                test:/\.less$/,
                use:[
                    "style-loader",
                    "css-loader",
                    // 处理css的兼容性问题
                    {
                        // 加载器
                        loader:'postcss-loader',
                        options:{
                            postcssOptions:{
                                plugins:[
                                    [
                                        'postcss-preset-env',
                                        {
                                            browsers:'last 2 versions'
                                        }
                                    ]
                                ]
                            }
                        }
                    },
                    // 将less转为css
                    "less-loader"
                ],
                exclude:/node_modules/
            }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:'./src/index.html'
        }),
        new CleanWebpackPlugin()
    ],
    //用来告诉webpack 哪些文件可以导入
    resolve:{
        extensions:['.ts','.css','.js']
    },
    mode:'development'
}

tsconfig.json

{
    "compilerOptions":{
        "strict": true,
        "target": "es2015",
        "module": "es2015",
        "noEmitOnError": true
    }
}

5.2 实现效果

在这里插入图片描述

具体代码实现:

index.ts

import '../style/index.less'
import Control from "../modules/Control"

new Control()

Control.ts

import ScorePanel from "../modules/ScorePanel"
import Food from "../modules/Food"
import Snack from "../modules/Snack"

class Control{
    food:Food
    snack:Snack
    scorepanel:ScorePanel
    direction = ''
    isLive = true
    constructor(){
        this.food = new Food()
        this.snack = new Snack()
        this.scorepanel = new ScorePanel()
        //初始化游戏,调用则游戏开始
        this.init()
    }
    //定义一个初始化函数
    init(){
        /* 
            在这里this会出现问题,因为在这里this是由document调用所以this指向document
            解决办法:
                - 可以使用箭头函数
                - bind()
        */
        // document.addEventListener('keydown',(event:KeyboardEvent)=>{
        //     this.SnackDirection(event)
        // })
        document.addEventListener('keydown',this.SnackDirection.bind(this))
        this.run()
    }

    //键盘按下事件
    SnackDirection(event:KeyboardEvent){
        // 修改direction属性
        this.direction = event.key
    }

    //让蛇跑起来
    run(){
        let x = this.snack.X
        let y = this.snack.Y
        //判断方向
        switch(this.direction){
            case "ArrowUp" : y -= 10; break;
            case "ArrowDown" : y += 10; break;
            case "ArrowLeft" : x -= 10; break;
            case "ArrowRight": x += 10; break;
        }

        this.checkFood(x,y)

       try {
            // 修改蛇的位置
            this.snack.X = x
            this.snack.Y = y
       } catch (error) {
           alert(error)
           this.isLive = false
       }

        this.isLive && setTimeout(()=>{
            this.run()
        }, 100-(this.scorepanel.num2 - 1)*10);
    }


    //检查是否吃到东西
    checkFood(x:number,y:number){
        if(this.food.X === x && this.food.Y === y)
        {
            //进入判断表示吃到了东西
            this.food.foodRandom()
            this.snack.addBody()
            this.scorepanel.addScore
        }
    }



}

export default Control

Food.ts

class Food{
    element:HTMLElement
    constructor(){
        this.element = document.querySelector('#food')!
    }

    // 获取食物当前的位置
    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'
    }

    /* 
        1.食物随机跳变
            分析:
                 - 跳变的范围 0-290
                 - 并且你要满足是10的倍数,因为设定
                    都一步为10格,要不然一直吃不到
    */
   foodRandom(){
        //生成随机数
        let left = Math.round(Math.random()*29)*10
        let top = Math.round(Math.random()*29)*10
        //设置food的位置
        this.X = left
        this.Y = top
   }

}

export default Food

ScorePanel.ts

class ScorePanel{
    score:HTMLElement
    level:HTMLElement
    num1 = 0  //分数初始值
    num2 = 1  //等级初始值
    maxLevel:number  // 设置最大的等级
    setScore:number //设置多少分升一级
    constructor(setScore = 10,maxLevel = 10){
        this.setScore = setScore
        this.maxLevel = maxLevel
        this.score = document.querySelectorAll('#score span')[0] as HTMLLinkElement
        this.level = document.querySelectorAll('#score span')[1] as HTMLLinkElement
    }

    // 增加分数
    addScore(){
        this.score.innerText = ++this.num1 +''
        if(this.num1 % this.setScore === 0)
        {
            this.addLevel()
        }
    }

    // 升级
    addLevel(){
        if(this.num2 < this.maxLevel)
        {
            this.level.innerText = ++this.num2 +''
        }
    }
}

export default ScorePanel

Snack.ts

class Snack{
    head:HTMLElement
    bodies:HTMLCollection
    element:HTMLElement
    constructor(){
        //获取头部
        this.head = document.querySelector('.head')!
        //获取身体
        this.bodies = document.getElementById('snack')!.getElementsByTagName('div')
        //获取snack容器
        this.element = document.querySelector('#snack')!
    }

    //获取头部的位置
    get X ()
    {
        return this.head.offsetLeft
    }

    get Y ()
    {
        return this.head.offsetTop
    }

    //设置头部的位置
    set X(value){
        //当位置未发生改变则不进行赋值
        if(this.X === value)
        {
            return
        }

        //限制蛇移动的范围
        if(value < 0 || value > 290)
        {
            throw 'GAME OVER ! 游戏结束!'
        }

        //解决蛇掉头问题
        if(this.bodies[1] && value === (this.bodies[1] as HTMLElement).offsetLeft)
        {
            //进入判断就表示调头了
            if(value > this.X)
            {
                value -= 20
            }else
            {
                value += 20
            }
        }

        this.moveBody()
        this.head.style.left = value + 'px'
        this.checkHead()
    }   

    set Y(value){
        //当位置未发生改变则不进行赋值
        if(this.Y === value)
        {
            return
        }

        //限制蛇移动的范围
        if(value < 0 || value > 290)
        {
            throw 'GAME OVER ! 游戏结束!'
        }

         //解决蛇掉头问题
         if(this.bodies[1] && value === (this.bodies[1] as HTMLElement).offsetTop)
         {
             //进入判断就表示调头了
             if(value > this.Y)
             {
                 value -= 20
             }else
             {
                 value += 20
             }
         }

        this.moveBody()
        this.head.style.top = value + 'px'
        this.checkHead()
    }   

    addBody(){
        this.element.insertAdjacentHTML('beforeend','<div></div>')
    }

    //添加身体移动的方法
    /* 
        实现的逻辑:
            第四节的位置 --> 第三节的位置
            第三节的位置 --> 第二节的位置
            第二节的位置 --> 第一节的位置
    */
    moveBody(){
        for(let i = this.bodies.length-1; i>0; i--)
        {
            let left = (this.bodies[i-1] as HTMLElement).offsetLeft;
            let top = (this.bodies[i-1] as HTMLElement).offsetTop;
            (this.bodies[i] as HTMLElement).style.left = left + 'px';
            (this.bodies[i] as HTMLElement).style.top = top + 'px';

        }
    }

    //检查是否撞头
    checkHead(){
        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)
            {
                // 如果进入了判断就表示撞到自己了
                throw '撞到自己了!!游戏结束!!'
            }
        }
}

}

export default Snack

总结

以上就是今天要讲的内容,希望对大家有所帮助!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值