【Tank】10.0 遊戲入口、遊戲開始和結束

重構遊戲入口

src/main.ts

import config from './config'
import './style.scss'

const app = document.querySelector<HTMLDivElement>("#app")
// @ts-ignore
app.style.width = config.canvas.width + 'px'
// @ts-ignore
app.style.height = config.canvas.height + 'px'

src/app.ts

import {promises} from "./service/image";
import canvasStraw from "./canvas/Straw";
import canvasWallBrick from "./canvas/WallBrick";
import canvasWallSteel from "./canvas/WallSteel";
import canvasTank from "./canvas/Tank";
import canvasWater from "./canvas/Water";
import bullet from "./canvas/Bullet";
import boss from "./canvas/Boss";
import player from "./canvas/Player";

export default {
    async gameStart() {
        // console.log(promises)
        //先加載各種貼圖
        await Promise.all(promises)
        // console.log(image.get('straw'))
        // 調用render方法渲染
        canvasStraw.render() // 畫布渲染:草地
        canvasWallBrick.render() // 畫布渲染:磚牆
        canvasWallSteel.render() // 畫布渲染:鋼牆
        canvasTank.render() // 畫布渲染:敵方坦克
        canvasWater.render() // 畫布渲染:水域
        bullet.render() // 畫布渲染:子彈
        boss.render() // 畫布渲染:boss,老巢
        player.render() // 畫布渲染:玩家坦克
    }
}

遊戲開始

src/style.scss

body {
  background-color: #000;
  //視圖的寬度
  width: 100vw;
  //視圖的高度
  height: 100vh;
  display: flex;
  /*主軸*/
  justify-content: center;
  /*交叉軸*/
  align-items: center;
  //div畫布默認就是居中
  #app {
    background-color: #000;
    position: relative;
    border: solid 12px #333;
    //默認值。如果你設置一個元素的寬爲 100px,
    // 那麼這個元素的內容區會有 100px 寬,
    // 並且任何邊框和內邊距的寬度都會被增加到最後繪製出來的元素寬度中
    box-sizing: content-box;

    //背景圖片
    background-image: url("/src/static/images/cover/cover.png");
    background-size: cover;
    background-position: center;
    cursor: pointer;

    canvas {
      position: absolute;
    }

  }
}

src/main.ts

import app from './app'

app.gameBootstrap()

src/app.ts

import {promises} from "./service/image";
import canvasStraw from "./canvas/Straw";
import canvasWallBrick from "./canvas/WallBrick";
import canvasWallSteel from "./canvas/WallSteel";
import canvasTank from "./canvas/Tank";
import canvasWater from "./canvas/Water";
import bullet from "./canvas/Bullet";
import boss from "./canvas/Boss";
import player from "./canvas/Player";
import config from "./config";
import './style.scss'

const app = document.querySelector<HTMLDivElement>("#app")
// @ts-ignore
app.style.width = config.canvas.width + 'px'
// @ts-ignore
app.style.height = config.canvas.height + 'px'


export default {
    // 遊戲當前狀態
    gameStatus: 'init',//'start' | 'gameover' | 'init' =
    // 遊戲初始化
    gameBootstrap() {
        // 遊戲開始點擊事件
        // @ts-ignore
        app.addEventListener('click', this.gameStart.bind(this))
    },
    // 遊戲開始
    async gameStart() {
        if (this.gameStatus != 'start') {
            this.gameStatus = 'start'
            //背景圖片關閉
            // @ts-ignore
            app.style.backgroundImage='none';

            // console.log(promises)
            //先加載各種貼圖
            await Promise.all(promises)
            // console.log(image.get('straw'))
            // 調用render方法渲染
            canvasStraw.render() // 畫布渲染:草地
            canvasWallBrick.render() // 畫布渲染:磚牆
            canvasWallSteel.render() // 畫布渲染:鋼牆
            canvasTank.render() // 畫布渲染:敵方坦克
            canvasWater.render() // 畫布渲染:水域
            bullet.render() // 畫布渲染:子彈
            boss.render() // 畫布渲染:boss,老巢
            player.render() // 畫布渲染:玩家坦克
        }
    },

    // 遊戲失敗
    gameOver() {

    }
}

遊戲結束

src/app.ts

import {promises} from "./service/image";
import canvasStraw from "./canvas/Straw";
import canvasWallBrick from "./canvas/WallBrick";
import canvasWallSteel from "./canvas/WallSteel";
import canvasTank from "./canvas/Tank";
import tank from "./canvas/Tank";
import canvasWater from "./canvas/Water";
import bullet from "./canvas/Bullet";
import boss from "./canvas/Boss";
import player from "./canvas/Player";
import config from "./config";
import './style.scss'

const app = document.querySelector<HTMLDivElement>("#app")
// @ts-ignore
app.style.width = config.canvas.width + 'px'
// @ts-ignore
app.style.height = config.canvas.height + 'px'


export default {
    // 遊戲當前狀態
    gameStatus: 'init',//'gameStart' | 'gameFail' |'gameWin'| 'init'
    gameInterval: 0,//定時器
    // 遊戲初始化
    gameBootstrap() {
        // 遊戲開始點擊事件
        // @ts-ignore
        app.addEventListener('click', async () => {
            await this.gameStart()
            // 遊戲結束監聽
            this.gameInterval = setInterval(() => {
                //玩家勝利
                if (tank.models.length <= 0) this.gameStatus = 'gameWin'
                //玩家失敗
                if (player.models.length <= 0 || boss.models.length <= 0) this.gameStatus = 'gameFail'
                if (this.gameStatus == 'gameWin' || this.gameStatus == 'gameFail') {
                    this.gameOver()
                }
            }, 100)
        })
    },
    // 遊戲開始
    async gameStart() {
        if (this.gameStatus != 'gameStart') {
            this.gameStatus = 'gameStart'
            //背景圖片關閉
            // @ts-ignore
            app.style.backgroundImage = 'none';

            // console.log(promises)
            //先加載各種貼圖
            await Promise.all(promises)
            // console.log(image.get('straw'))
            // 調用render方法渲染
            canvasStraw.render() // 畫布渲染:草地
            canvasWallBrick.render() // 畫布渲染:磚牆
            canvasWallSteel.render() // 畫布渲染:鋼牆
            canvasTank.render() // 畫布渲染:敵方坦克
            canvasWater.render() // 畫布渲染:水域
            bullet.render() // 畫布渲染:子彈
            boss.render() // 畫布渲染:boss,老巢
            player.render() // 畫布渲染:玩家坦克
        }
    },

    // 遊戲結束
    gameOver() {
        //關閉遊戲運行的定時器
        clearInterval(this.gameInterval)
    }
}

這裏遊戲雖然結束了,但是應該地圖的坦克和子彈停止。

遊戲結束 運動停止

src/vite-env.d.ts

......
/**
 * 畫布實現的函數、方法
 */
interface ICanvas {
    // 抽象屬性:畫布實例
    ctx: CanvasRenderingContext2D

    // 抽象方法:渲染貼圖
    render(): void

    // 抽象方法,返回模型
    model(): ConstructorModel | ConstructorModelBullet

    // 抽象方法:返回模型數量
    num(): number

    // 畫布移除模型
    removeModel(model: IModel): void

    // 畫布渲染模型(將模型渲染到畫布上)
    renderModels(): void

    //停止,停止移動
    stop():void
}
......

src/app.ts

......
    // 遊戲結束
    gameOver() {
        //關閉遊戲運行的定時器
        clearInterval(this.gameInterval)
        // 敵方坦克停止
        tank.stop()
        // 子彈停止
        bullet.stop()

    }
......

src/model/Player.ts

/**
 * 模型
 * 敵方坦克
 */
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";

import {upperFirst} from 'lodash'
import config from "../config";
import player from "../canvas/Player";
import {EnumDirection} from "../enum/enumPosition";
import utils from "../utils";
import water from "../canvas/Water";
import wallBrick from "../canvas/WallBrick";
import wallSteel from "../canvas/WallSteel";
import bullet from "../canvas/Bullet";
import boss from "../canvas/Boss";
import tank from "../canvas/Tank";
import app from "../app";

export default class ModelTank extends AbstractModel implements IModel {
    name: string = 'player';

    // 畫布實例
    canvas: ICanvas = player;
    // 事件是否綁定
    isBindEvent: boolean = false

    // 繼承父類抽象方法:渲染貼圖
    // 一些初始化自定義的動作、行爲,都在這裏進行
    render(): void {
        //初始化時永遠朝上
        // this.direction = EnumDirection.top
        // 初始化模型
        super.draw()
        if (!this.isBindEvent) {
            this.isBindEvent = true
            // 添加鍵盤監聽事件,坦克方向改變
            document.addEventListener('keydown', this.changeDirection.bind(this))
            // 添加鍵盤監聽事件,坦克移動
            document.addEventListener('keydown', this.move.bind(this))
            // 添加鍵盤監聽事件,坦克發射子彈
            document.addEventListener('keydown', this.bulletLaunch.bind(this))
        }
    }

    //遊戲結束判斷
    isGameOver(): boolean {
        if (this.isBindEvent && app.gameStatus != 'gameStart') {
            this.isBindEvent = false
            document.removeEventListener('keydown', this.changeDirection.bind(this), false)
            document.removeEventListener('keydown', this.bulletLaunch.bind(this), false)
            document.removeEventListener('keydown', this.move.bind(this), false)
            return false
        }
        return true
    }

    bulletLaunch(event: KeyboardEvent) {
        if (this.isGameOver()) {
            // console.log(event);
            //發射子彈,空格,回車,小鍵盤迴車
            (event.code === 'Space'
                || event.code === 'Enter'
                || event.code === 'NumpadEnter') && bullet.addPlayerBullet();
        }
    }

    /**
     * 方向改變
     * @param event 鍵盤對象
     */
    changeDirection(event: KeyboardEvent) {
        if (this.isGameOver()) {
            // console.log(event)
            switch (event.code) {
                case 'KeyW':
                case 'ArrowUp':
                    this.direction = EnumDirection.top
                    break;
                case 'KeyD':
                case 'ArrowRight':
                    this.direction = EnumDirection.right
                    break;
                case 'KeyS':
                case 'ArrowDown':
                    this.direction = EnumDirection.bottom
                    break;
                case 'KeyA':
                case 'ArrowLeft':
                    this.direction = EnumDirection.left
                    break;
            }
            // 繪製模型
            this.canvas.renderModels()
        }
    }

    //移動
    move(event: KeyboardEvent) {
        if (this.isGameOver()) {
            // ********************* 座標更新 *********************
            let x = this.x;
            let y = this.y;
            switch (event.code) {
                case 'KeyW':
                case 'ArrowUp':
                    y -= 15
                    break;
                case 'KeyD':
                case 'ArrowRight':
                    x += 15
                    break;
                case 'KeyS':
                case 'ArrowDown':
                    y += 15
                    break;
                case 'KeyA':
                case 'ArrowLeft':
                    x -= 15
                    break;
            }
            if (utils.modelTouch(x, y, [
                ...water.models,// 水域
                ...wallBrick.models,// 磚牆
                ...wallSteel.models,// 鋼牆
                ...boss.models, //boss
                ...tank.models //敵方坦克
            ]) || utils.isCanvasTouch(x, y)) {
                // 隨機獲取方向
            } else {
                this.x = x;
                this.y = y;
                // 繪製模型
                this.canvas.renderModels()
            }
        }
    }

    // 隨機取用其中一個圖片
    getImage(): HTMLImageElement {
        return image.get(`${this.name}${upperFirst(this.direction)}` as keyof typeof config.images)!
    }
}

src/canvas/Tank.ts、src/canvas/Bullet.ts修改類似

......
/**
 * 畫布是單例模式
 * 在一個圖層,所以只需要new一個實例即可。
 */
export default new (class extends AbstractCanvas implements ICanvas {
......
    // 敵方坦克運動定時器
    intervalId: number = 0;

    render(): void {
        // super:調用父類的方法
        this.createModels()
        // 調用渲染模型,防止每次重新渲染時,又生成新的模型實例
        super.renderModels();
        console.log("tank", Number((100 / config.tank.speed).toFixed(3)))
        // 讓坦克畫布實時刷新,每config.tank.speed毫秒擦寫一次,等於速度。
        this.intervalId = setInterval(() => {
            this.renderModels()
        }, Number((100 / config.tank.speed).toFixed(3)))
    }

    //停止,停止移動
    stop() {
        clearInterval(this.intervalId)
    }
......
}

src/canvas/Boss.ts、src/canvas/Player.ts、src/canvas/Straw.ts、src/canvas/WallBrick.ts、src/canvas/WallSteel.ts、src/canvas/Water.ts修改類似

......
    //停止,停止移動
    stop() {
    }
......

失敗或者勝利時,遊戲畫面禁止。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章