typescript学习记录-练习项目-贪食蛇

8,621次阅读
没有评论

共计 6573 个字符,预计需要花费 17 分钟才能阅读完成。

参考文章:https://www.bilibili.com/video/BV1Xy4y1v7S2?p=22

项目搭建

将之前的 package.json,tsconfig.json,webpack.config.js 复制到新项目中,同时新建 src 跟 test 目录。
然后修改 package.json 中的 name,修改成现在的项目名称。
然后使用 npm i 自动下载项目所需的包,或者直接使用图形化命令下载也行。
typescript 学习记录 - 练习项目 - 贪食蛇

typescript 学习记录 - 练习项目 - 贪食蛇
搭建完后测试一下是否正常。在 src 下新建 main.ts 与 main.html,随意在 main.ts 中写一些代码。
写完后使用 npm run build 来构建一下。正常完成后会构建一个 dict 文件夹。

配置 less

使用 npm i -D less less-loader css-loader style-loader 下载。然后修改 webpack.config.js 文件。
typescript 学习记录 - 练习项目 - 贪食蛇
添加的配置信息

            { 
                test: /.less$/, 
                use:[   
                    "style-loader",
                    "css-loader",
                    "less-loader"
                ],
                exclude: /node-modules/ 
            }

测试

创建一个 less 文件,随意写些内容,然后再 ts 中引入。
typescript 学习记录 - 练习项目 - 贪食蛇
typescript 学习记录 - 练习项目 - 贪食蛇
typescript 学习记录 - 练习项目 - 贪食蛇
使用 npm run build 后页面变成绿色了,这代表 less 的代码执行成功。

css 的兼容性处理

跟 ts 一样,css 也要考虑兼容性问题。使用 npm i -D postcss postcss-loader postcss-preset-env 下载相关构建。然后修改 webpack.config.js 文件。

                    {  
                        loader: "postcss-loader",
                        options: {  
                            postcssOptions:{
                                plugins:[  
                                    [
                                        "postcss-preset-env",  
                                        {  
                                            browsers: "last 2 versions" 
                                        }
                                    ]
                                ]
                            }
                        }
                    },

最终 webpack.config.js 中的 less 配置信息如下:

            { 
                test: /.less$/, 
                use:[   
                    "style-loader",
                    "css-loader",
                    {  
                        loader: "postcss-loader",
                        options: {  
                            postcssOptions:{
                                plugins:[  
                                    [
                                        "postcss-preset-env",  
                                        {  
                                            browsers: "last 2 versions" 
                                        }
                                    ]
                                ]
                            }
                        }
                    },
                    "less-loader"
                ],
                exclude: /node-modules/ 
            }

项目界面

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">0span>
        div>
        div>
            level:span id="level">1span>
        div>

    div>

div>

body>
html>

设置样式

body{
  background-color: #1E90FF;
  display: flex;
  font:bold 20px "Courier New"; // 统一设置字体
}

// 设置变量
@bg-color: #b7d4a8;

// 清除默认样式
*{
  margin: 0;
  padding: 0;
  // 改变盒子模型的计算方式, 方便自动计算内容器大小
  box-sizing: border-box;
}

// 设置主窗口的样式
#main{
  width: 360px;
  height: 420px;
  background-color: @bg-color; // 设置主容器背景颜色
  margin: 100px auto; // 位置下移 100 像素,水平自动居中
  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: black;
        border: 1px solid @bg-color;
        position: absolute; // 开启绝对定位
      }
      }
    }

    // 设置食物的样式
    #food{
      width: 10px;
      height: 10px;
      border: 1px solid @bg-color;
      position: absolute; // 开启绝对定位
      display: flex;  // 开启弹性盒模型
      flex-flow: row wrap;  // row 设置横轴为主轴, wrap 表示会自动换行
      justify-content: space-between;  // 设置主轴和侧轴的空白空间分配到元素之间
      align-content: space-between;

      //left: 40px;
      //top: 100px;

      &>div{
        width: 4px;
        height: 4px;
        background-color: black;
        transform: rotate(45deg); // 设置旋转 45 度
      }
    }

  // 游戏记分牌
  #score-panel{
    width: 300px;
    display: flex;
    justify-content: space-between;  // 设置主轴对齐方式
  }

}

运行逻辑

完成 FOOD 类

新建一个 Food.ts 来存放该类


class Food{
    
    element: HTMLElement
    stage

    constructor() {
        
        this.element = document.getElementById("food")! 
        this.stage = document.getElementById("stage")!
        
    }

    
    get X(){
        return this.element.offsetLeft
    }

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

    
    change(){
        
        
        

        const high = (Math.floor(this.stage.offsetHeight/10)*10 - 10)/10
        const width= (Math.floor(this.stage.offsetWidth/10)*10 - 10)/10

        let x = Math.round(Math.random() * width) * 10
        let y = Math.round(Math.random() * high) * 10

        this.element.style.left = x + "px"
        this.element.style.top =  y + "px"
    }
}


export default Food

完成 ScorePanel 类

新建一个 ScorePanel.ts 来存放该类



class ScorePanel{
    
    #score:number = 0 
    level:number=1
    
    #score_ele:HTMLElement
    level_ele:HTMLElement

    
    max_level:number
    
    up_score:number

    constructor(max_level:number = 10, up_score:number = 10) {
        this.#score_ele = document.getElementById("score")!
        this.level_ele = document.getElementById("level")!
        this.max_level = max_level
        this.up_score = up_score
    }

    
    addScore(){
        
        this.#score_ele.innerHTML = ++this.#score + ''
        
        if(this.#score % this.up_score === 0){
            this.levelUp()
        }
    }

    
    levelUp(){
        if (this.level  this.max_level){
            this.level_ele.innerHTML = ++this.level + ''
        }

    }
}


const sp = new ScorePanel()
for (let i=0;i10;i++){
    sp.addScore()
}

export default ScorePanel

完成 Snake 类

蛇的相关属性方法

class Snake{
    
    head:HTMLElement
    
    bodis:HTMLCollectionBase
    
    snake:HTMLElement
    
    stage:HTMLElement

    stage_width:number
    stage_high:number

    is_reback_way:boolean = false
    constructor() {
        this.snake = document.getElementById("snake")!
        this.head = document.querySelector('#snake > div') as HTMLElement 
        
        
        this.bodis = this.snake.getElementsByTagName("div")

        this.stage = document.getElementById("stage")!

        this.stage_width= (Math.floor(this.stage.offsetWidth/10)*10 - 10) 
        this.stage_high = (Math.floor(this.stage.offsetHeight/10)*10 - 10)  
    }

    
    get X(){
        return this.head.offsetLeft
    }

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

    
    set X(value){
        if(this.X === value){  
            return
        }

        if (value  0 || value > this.stage_width){
            
            throw new Error('蛇撞墙了')
        }




        
        
        
        
        
        
        
        
        
        
        
        

        this.moveBody()
        this.head.style.left = value + "px"
        
        this.checkHeadBody()
    }

    set Y(value){
        if(this.Y === value){  
            return
        }

        if (value  0 || value > this.stage_high){
            
            throw new Error('蛇撞墙了')
        }

        this.moveBody()
        this.head.style.top = value + "px"
        
        this.checkHeadBody()
    }



    
    addBody(){
        
        this.snake.insertAdjacentHTML("beforeend","
"
) } moveBody(){ for (let i=this.bodis.length-1; i > 0; i--){ let x = (this.bodis[i - 1] as HTMLElement).offsetLeft; let y = (this.bodis[i - 1] as HTMLElement).offsetTop; (this.bodis[i] as HTMLElement).style.left = x + 'px'; (this.bodis[i] as HTMLElement).style.top = y + 'px'; } } checkHeadBody(){ for( let i = 1; i this.bodis.length; i++){ let tmp = this.bodis[i] as HTMLElement if ( this.X === tmp.offsetLeft && this.Y === tmp.offsetTop){ throw new Error('蛇撞自己') } } } } export default Snake

GameContro 键盘控制器


import Snake from "./Snake";
import Food from "./Food";
import ScorePanel from "./ScorePanel";


class GameContro{
    
    snake:Snake
    food:Food
    score_panele:ScorePanel

    
    direction: string = ""

    
    is_live = true

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

        this.init()
    }

    
    init(){
        
        document.addEventListener("keydown", this.keyDownHandler.bind(this)) 
        
        this.run()
    }

    
    keyDownHandler(event:KeyboardEvent){
        let up_key = ["ArrowUp", "Up", "w", "W"]
        let down_key = ["ArrowDown", "Down", "s", "S"]
        let right_key = ["ArrowRight", "Right", "d", "D"]
        let left_key = ["ArrowLeft", "Left", "a", "A"]
        if(up_key.indexOf(event.key) >= 0){  
            if(this.direction === "Down" && this.snake.bodis.length !== 1){}else{ 
                this.direction = "Up"
            }
        }
        if(down_key.indexOf(event.key) >= 0){
            if(this.direction === "Up" && this.snake.bodis.length !== 1){}else{
                this.direction = "Down"
            }
        }
        if(right_key.indexOf(event.key) >= 0){
            if(this.direction === "Left" && this.snake.bodis.length !== 1){}else{
                this.direction = "Right"
            }
        }
        if(left_key.indexOf(event.key) >= 0){
            if(this.direction === "Right" && this.snake.bodis.length !== 1){}else{
                this.direction = "Left"
            }
        }
    }

    
    checkEat(X:number, Y:number){
        if((X === this.food.X) && (Y === this.food.Y)){
            console.log('吃到食物')
            
            this.food.change()
            
            this.score_panele.addScore()
            
            this.snake.addBody()
        }


    }

    
    run(){
        
        let x = this.snake.X
        let y = this.snake.Y

        
        switch (this.direction){
            case "ArrowUp":
            case "Up":  
                y -= 10
                break
            case "Down":
                y += 10
                break
            case "Right":
                x += 10
                break
            case "Left":
                x -= 10
                break
        }

        
        this.checkEat(x, y)

        try{
            
            this.snake.X = x
            this.snake.Y = y
        }catch(e:any){
            alert(e.message + 'Game Over')
            
            this.is_live = false
        }



        
        this.is_live && setTimeout(this.run.bind(this), 300 - (this.score_panele.level - 1) * 30) 

    }


}

export default GameContro;

main.ts


import './style/main.less'



import GameContro from "./mod/GameContro";

new GameContro()

最终效果

在这里插入图片描述

原文地址: typescript 学习记录 - 练习项目 - 贪食蛇

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-10-14发表,共计6573字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)