亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

利用TypeScript編寫(xiě)貪吃蛇游戲

 更新時(shí)間:2022年09月23日 16:25:59   作者:陽(yáng)樹(shù)陽(yáng)樹(shù)  
這篇文章主要為大家詳細(xì)介紹了如何利用TypeScript編寫(xiě)貪吃蛇游戲,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的可以了解一下

先來(lái)康康效果圖

我接下來(lái)將講解相關(guān)配置和代碼,源碼鏈接放在最底下了,在GitHub上。

Explanation

1. tsconfig.json配置

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

此處是對(duì)編譯選項(xiàng)進(jìn)行配置

  • target: 我們將TS轉(zhuǎn)譯成JS的版本。
  • module: 模塊化的版本。
  • strict: 所有相關(guān)的嚴(yán)格模式是否開(kāi)啟。
  • noEmitOnError: 當(dāng)出現(xiàn)錯(cuò)誤時(shí)是否停止編譯。

2. HTML & CSS 布局相關(guān)

先來(lái)看看我們整體的布局

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body scroll="no">
    <!-- 創(chuàng)建游戲的主容器 -->
    <div id="main">
        <!-- 設(shè)置游戲舞臺(tái) -->
        <div id="stage">
            <!-- 設(shè)置蛇 -->
            <div id="snack">
                <!-- snack內(nèi)部的div表示蛇的各部分 -->
                <div></div>
            </div>
            <div id="food">
                <!-- 設(shè)置食物的樣式 -->
                <div></div>
                <div></div>
                <div></div>
                <div></div>
            </div>
        </div>
        <!-- 設(shè)置游戲的積分牌 -->
        <div id="scoreStage">
            <div>SCORE:<span id="score">0</span></div>
            <div>LEVEL:<span id="level">1</span></div>
        </div>
    </div>
</body>
</html>

CSS代碼

// 設(shè)置變量
@bgColor: #b7d4a8;

// 清除默認(rèn)樣式清除默認(rèn)樣式
* {
    margin: 0;
    padding: 0;
    // 怪異模式
    box-sizing: border-box;
}

body {
    font: bold 20px "Courier";
    width: 100%;
    height: 100%;
    overflow: hidden;
}

// 設(shè)置主窗口的樣式
#main {
    width: 360px;
    height: 420px;
    background-color: @bgColor;
    margin: 100px auto;
    border: 10px solid #000;
    border-radius: 40px;
    display: flex;
    flex-direction: column;
    align-items: center;
    // 主軸對(duì)齊方式
    justify-content: space-around;
    
}

#stage {
    width: 304px;
    height: 304px;
    border: 2px solid black;
    position: relative;
}

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

#snack {
    &>div {
        width: 10px;
        height: 10px;
        background-color: #000;
        border: 1px solid @bgColor;
        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: #000;
        transform: rotate(45deg);
        // border: 1px solid @bgColor;
    }
}

body {
    scroll-behavior: unset;
}

這里面比較有意思是可以通過(guò)@xxx來(lái)設(shè)置CSS變量

比如這里的:@bgColor: #b7d4a8;

3. TS核心邏輯

TS的核心在于Class,所以我們需要定義出非常多的類(lèi)來(lái)對(duì)這個(gè)貪吃蛇小游戲進(jìn)行分析。

我們先來(lái)看看這個(gè)貪吃蛇小游戲有幾個(gè)主要的部分。

  • 食物
  • 分?jǐn)?shù)版
  • 游戲操控

食物

食物有幾個(gè)核心邏輯

食物這個(gè)類(lèi)。

首先,我們需要獲取其中的橫縱坐標(biāo)??梢栽O(shè)置get來(lái)獲取X與Y。

其次,當(dāng)蛇蛇碰到食物的時(shí)候,這個(gè)食物的位置會(huì)改變,可以設(shè)置一個(gè)change()方法。

class Food {
    // 屬性 & 方法
    // 定義食物所對(duì)應(yīng)的元素
    element: HTMLElement;

    constructor() {
        // 加一個(gè) “!”表示這玩意不會(huì)為空
        this.element = document.getElementById('food')!;
    }
    // 方法
    // 1. 獲取食物的x坐標(biāo)的方法
    get X() {
        return this.element.offsetLeft;
    }
    // 2. 獲取食物的y坐標(biāo)的方法
    get Y() {
        return this.element.offsetTop;
    }
    
    // 修改食物位置的方法
    change() {
        // 使用random,生成隨機(jī)位置
        // 蛇移動(dòng)一次就是一格,大小為10
        const left = Math.round(Math.random()*29)*10;
        const top = Math.round(Math.random()*29)*10;
        this.element.style.left = left + 'px';
        this.element.style.top = top + 'px';
    }

}

export default Food

蛇的話,話頭就很多了。

首先,我們需要獲取到蛇頭的橫縱坐標(biāo),還要能夠給橫縱坐標(biāo)賦值。

其次,我們需要有方法增加蛇的身子addBody

同時(shí)還需要增加身子移動(dòng)的方式moveBody

當(dāng)然還需要增加檢測(cè)機(jī)制,舌頭不能與身子重疊

這個(gè)比較復(fù)雜,我們分而析之

1.元素設(shè)置與constructor

// 表示蛇的元素
head: HTMLElement;
// 蛇的身體,包括蛇頭
bodies: HTMLCollection;
// 獲取蛇的容器
element: HTMLElement;
constructor() {
   // 斷言一下 | 找到蛇頭
  this.head = document.querySelector('#snack > div') as HTMLElement;
  this.element = document.getElementById('snack')!
  this.bodies = this.element.getElementsByTagName('div');
}

這里面的!是用來(lái)確定存在id為snack這個(gè)元素的。

2.增加身子

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

3.移動(dòng)身子

    moveBody() {
        /**
         * 將后邊身體設(shè)置為前邊身體的位置
         * 第四節(jié) = 第三節(jié)的位置
         * 第三節(jié) = 第二節(jié)的位置
         * 第二節(jié) = 第一節(jié)的位置
        */
        // 調(diào)節(jié)每個(gè)位置
        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;
            // 將這個(gè)值設(shè)置到當(dāng)前身體
            (this.bodies[i] as HTMLElement).style.left = X + 'px';
            (this.bodies[i] as HTMLElement).style.top = Y + 'px';
        }
    }

4.檢測(cè)機(jī)制

    checkHeadBody() {
        // 獲取所有的身體,檢查其是否和蛇頭的坐標(biāo)發(fā)生重疊
        for(let i=1;i<this.bodies.length;i++) {
            const bd = (this.bodies[i] as HTMLElement)
            if(this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
                // 說(shuō)明出現(xiàn)了碰撞
                throw Error('撞到自己了~~~')
            }
        }
    }

5.完整代碼

class Snack {
    // 表示蛇的元素
    head: HTMLElement;
    // 蛇的身體,包括蛇頭
    bodies: HTMLCollection;
    // 獲取蛇的容器
    element: HTMLElement;
    constructor() {
        // 斷言一下 | 找到蛇頭
        this.head = document.querySelector('#snack > div') as HTMLElement;
        this.element = document.getElementById('snack')!
        this.bodies = this.element.getElementsByTagName('div');
    }

    // 獲取蛇的坐標(biāo)
    get X() {
        return this.head.offsetLeft
    }
    get Y() {
        return this.head.offsetTop
    }

    // 設(shè)置蛇的坐標(biāo)
    set X(value) {
        // 新值和舊值相同,直接返回,無(wú)需修改。
        if(this.X === value) return
        if(value < 0 || value > 290) {
            throw new Error('您撞墻了')
        }
        // 蛇在往左走,不能往右走
        if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {
            // 讓蛇向反方向繼續(xù)移動(dòng)
            if(value > this.X) {
                // 如果新值大于舊值X,說(shuō)明蛇在向右走,此時(shí)發(fā)生掉頭,應(yīng)該使蛇繼續(xù)向左走
                value = this.X - 10
            } else {
                value = this.X + 10
            }
        }

        this.moveBody()
        this.head.style.left = value + 'px'
        this.checkHeadBody()
    }
    set Y(value) {
        if(this.Y === value) return
        if(value < 0 || value > 290) {
            throw new Error('您撞墻了')
        }
        if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
            // 讓蛇向反方向繼續(xù)移動(dòng)
            if(value > this.Y) {
                // 如果新值大于舊值X,說(shuō)明蛇在向右走,此時(shí)發(fā)生掉頭,應(yīng)該使蛇繼續(xù)向左走
                value = this.Y - 10
            } else {
                // 不是 += 是等于
                value = this.Y + 10
            }
        }
        this.moveBody()
        this.head.style.top = value + 'px'
        this.checkHeadBody()
    }

    // 蛇增加一截
    addBody() {
        this.element.insertAdjacentHTML("beforeend", "<div></div>")
    }
    //  添加一個(gè)蛇身體移動(dòng)的方法
    moveBody() {
        /**
         * 將后邊身體設(shè)置為前邊身體的位置
         * 第四節(jié) = 第三節(jié)的位置
         * 第三節(jié) = 第二節(jié)的位置
         * 第二節(jié) = 第一節(jié)的位置
        */
        // 調(diào)節(jié)每個(gè)位置
        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;
            // 將這個(gè)值設(shè)置到當(dāng)前身體
            (this.bodies[i] as HTMLElement).style.left = X + 'px';
            (this.bodies[i] as HTMLElement).style.top = Y + 'px';
        }
    }

    checkHeadBody() {
        // 獲取所有的身體,檢查其是否和蛇頭的坐標(biāo)發(fā)生重疊
        for(let i=1;i<this.bodies.length;i++) {
            const bd = (this.bodies[i] as HTMLElement)
            if(this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
                // 說(shuō)明出現(xiàn)了碰撞
                throw Error('撞到自己了~~~')
            }
        }
    }
}

export default Snack

得分面板

這個(gè)就比較簡(jiǎn)單了,主要是得分增加的方法與等級(jí)提升的方法。

class scorePanel {
    // score和level用來(lái)記錄分?jǐn)?shù)和等級(jí)
    score: number = 0;
    level: number = 1;
    scoreSpan: HTMLElement;
    levelSpan: HTMLElement;

    // 設(shè)置等級(jí)
    maxLevel: number;
    // 設(shè)置一個(gè)變量表示多少分升級(jí)
    upScore: number;
    // 給兩個(gè)需要修改的元素賦值
    constructor(maxLevel: number = 10, upScore: number = 2) {
        this.scoreSpan = document.getElementById('score')!;
        this.levelSpan = document.getElementById('level')!;

        this.maxLevel = maxLevel
        this.upScore = upScore
    }

    // method
    // 設(shè)置加分的方法
    addScore() {
        // 分?jǐn)?shù)自增
        this.score += 1
        this.scoreSpan.innerHTML = this.score + '';
        // 判斷一下分?jǐn)?shù)是多少
        if(this.score % this.upScore === 0) {
            this.levelUp()
        }
    }


    // 提升等級(jí)的方法
    levelUp() {
        if(this.level < this.maxLevel) {
            this.level += 1
            this.levelSpan.innerHTML = this.level + '';
        }
    }
}

export default scorePanel

控制面板

這個(gè)邏輯的核心之一是整合,之二是監(jiān)控鍵盤(pán)keydown事件

關(guān)于整合:我們會(huì)把其中的蛇,面板,食物都整合到這個(gè)類(lèi)中,所謂一個(gè)啟動(dòng)游戲的開(kāi)關(guān)。

    snack: Snack;
    food: Food;
    scorePanel: scorePanel;
    arrowDirection: string = '';
    // 創(chuàng)建一個(gè)屬性用來(lái)記錄游戲是否結(jié)束
    isLeave: boolean = true
    constructor() {
        this.snack = new Snack();
        this.food = new Food();
        this.scorePanel = new scorePanel();
        this.init();
    }

關(guān)于監(jiān)控鍵盤(pán)事件

這里的核心邏輯就是監(jiān)控,看是上下左右中的哪一個(gè),然后對(duì)應(yīng)的改變蛇蛇的方向。

其中蛇的移動(dòng)需要不斷的調(diào)用run這個(gè)函數(shù),所以我們使用isLeave作為開(kāi)關(guān),用遞歸來(lái)多次調(diào)用run這個(gè)函數(shù)。

import Snack from './snack';
import Food from "./Food";
import scorePanel from './scorePanel';

class GameControl {
    snack: Snack;
    food: Food;
    scorePanel: scorePanel;
    arrowDirection: string = '';
    // 創(chuàng)建一個(gè)屬性用來(lái)記錄游戲是否結(jié)束
    isLeave: boolean = true
    constructor() {
        this.snack = new Snack();
        this.food = new Food();
        this.scorePanel = new scorePanel();
        this.init();
    }
    // 游戲的初始化方法
    init() {
        // 綁定鍵盤(pán)按下的時(shí)間
        // const _this = this
        // 如果不改變這個(gè)this,則會(huì)綁定到document上面
        // document.addEventListener('keydown', _this.keyDownHandler)
        document.addEventListener('keydown', this.keyDownHandler.bind(this));
        // 調(diào)用run方法
        this.run();
    }
    // 創(chuàng)建一個(gè)鍵盤(pán)按下的響應(yīng)函數(shù)
    /**
     *  ArrowRight Right
        ArrowLeft Left
        ArrowDown Down
        ArrowUp Up
    */
    keyDownHandler(event: KeyboardEvent) {
        this.arrowDirection = event.key
        // this.run();
    }

    // 創(chuàng)建一個(gè)控制蛇移動(dòng)的方法
    run() {
        // 根據(jù)方向(this.direction)來(lái)使蛇的位置改變
        // 向上 top -
        // 向下 top +
        // 向左 left - 
        // 向右 left +
        let X = this.snack.X;
        let Y = this.snack.Y;
        // 根據(jù)按鍵方向修改值
        switch (this.arrowDirection) {
            case "ArrowUp":
            case "Up":
                // 向上移動(dòng) 
                Y -= 10
                break;
            case "ArrowDown":
            case "Down":
                Y += 10
                break;
            case "ArrowLeft":
            case "Left":
                X -= 10
                break
            case "ArrowRight":
            case "Right":
                X += 10
                break
        }
        try {
            this.snack.X = X;
            this.snack.Y = Y;
        } catch(e: any) {
            alert(e.message)
            this.isLeave = false
        }
        
        this.checkEat(X, Y)
        // 開(kāi)啟定時(shí)調(diào)用
        // 這是遞歸調(diào)用
        this.isLeave && setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30);
    }

    // 定義一個(gè)方法,用來(lái)檢查蛇是否吃到食物
    checkEat(x:number, y:number) {
        if(x === this.food.X && y === this.food.Y) {
            this.food.change() // 食物改變位置
            this.scorePanel.addScore() // 分?jǐn)?shù)增加
            this.snack.addBody()
        }
    }
}

export default GameControl

項(xiàng)目源碼鏈接

Github

以上就是利用TypeScript編寫(xiě)貪吃蛇游戲的詳細(xì)內(nèi)容,更多關(guān)于TypeScript貪吃蛇游戲的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論