【Tank】7.0 子彈模型和畫布、子彈運動、磚牆打掉

幕布和模型的初始化

src/canvas/Bullet.ts

/**
 * 畫布
 * 子彈
 */
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelBullet from "../model/Bullet";

/**
 * 畫布是單例模式
 * 在一個圖層,所以只需要new一個實例即可。
 */
export default new (class extends AbstractCanvas implements ICanvas {
        render(): void {
            // super:調用父類的方法
            super.createModels()
            // 調用渲染模型,防止每次重新渲染時,又生成新的模型實例
            super.renderModels();
        }

        // 抽象方法,返回模型
        model(): ConstructorModel {
            return ModelBullet;
        }

        // 抽象方法:返回模型數量,子彈數量的創建由坦克決定,這裏默認給0
        num(): number {
            return 0;
        }
    }
)('bullet')

src/model/Bullet.ts

/**
 * 模型
 * 子彈
 */
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
import config from "../config";
import bullet from "../canvas/Bullet";

export default class ModelStraw extends AbstractModel implements IModel {
    name: string = 'bullet';

    // 畫布實例
    canvas: ICanvas = bullet;

    // 繼承父類抽象方法:渲染貼圖
    // 一些初始化自定義的動作、行爲,都在這裏進行
    render(): void {
        super.draw()
    }

    // 獲取貼圖
    getImage(): HTMLImageElement {
        return image.get(this.name as keyof typeof config.images)!;
    }

}

如上,畫布是單例模式,在一個圖層,所以只需要new一個實例即可。都修改爲export default new (xxxxx)(參數)的模式。
比如src/canvas/Tank.ts

/**
 * 畫布
 * 坦克
 */
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelTank from "../model/Tank";
import config from "../config";
import position from "../service/position";

/**
 * 畫布是單例模式
 * 在一個圖層,所以只需要new一個實例即可。
 */
export default new (class extends AbstractCanvas implements ICanvas {
.....
})('tank')

src/main.ts

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

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


const bootstrap = async () => {
    // console.log(promises)
    //先加載各種貼圖
    await Promise.all(promises)
    // console.log(image.get('straw'))
    // 調用render方法渲染
    canvasStraw.render() // 畫布渲染:草地
    canvasWallBrick.render() // 畫布渲染:磚牆
    canvasWater.render() // 畫布渲染:水域
    canvasWallSteel.render() // 畫布渲染:鋼牆
    canvasTank.render() // 畫布渲染:敵方坦克
    bullet.render() // 畫布渲染:子彈
}

void bootstrap()

src/config.ts

// 草地
import imgUrlStraw from './static/images/straw/straw.png'
// 磚牆
import imgUrlWallBrick from './static/images/wall/wall.gif'
// 水域
import imgUrlWater from './static/images/water/water.gif'
// 鋼牆
import imgUrlWallSteel from './static/images/wall/steels.gif'
// 坦克
import imgUrlTankTop from './static/images/tank/top.gif'
import imgUrlTankRight from './static/images/tank/right.gif'
import imgUrlTankLeft from './static/images/tank/left.gif'
import imgUrlTankBottom from './static/images/tank/bottom.gif'
// 子彈
import imgUrlBullet from './static/images/bullet/bullet.jpg'

export default {
    // 畫布
    canvas: {
        width: 900,
        height: 600,
    },
    // 模型
    model: {
        common: {
            width: 30,
            height: 30,
        },
        // 草地
        // straw: {
        //     width: 30,
        //     height: 30,
        // }
    },
    //草地
    straw: {
        num: 100,
    },
    // 磚牆
    wallBrick: {
        num: 100,
    },
    // 鋼牆
    wallSteel: {
        num: 30,
    },
    // 水域
    water: {
        num: 40
    },
    // 敵方坦克
    tank: {
        num: 40,// 坦克數量
        speed: 0.5 // 坦克速度,越小越快
    },
    // 圖片
    images: {
        // 草地
        straw: imgUrlStraw,
        // 磚牆
        wallBrick: imgUrlWallBrick,
        // 鋼牆
        wallSteel: imgUrlWallSteel,
        // 水域
        water: imgUrlWater,
        // 敵方坦克
        tankTop: imgUrlTankTop,
        tankRight: imgUrlTankRight,
        tankBottom: imgUrlTankBottom,
        tankLeft: imgUrlTankLeft,
        // 子彈
        bullet: imgUrlBullet
    }
}

如下圖,不僅效果一致,而且能夠在html中區分每一個畫布是什麼。


子彈創建邏輯分析

  1. 子彈是由坦克發出來的
    2.子彈不能一直不停地發,需要有停頓,且一個一個發出,不然滿屏幕都是子彈,遊戲沒法玩下去。

進入子彈的畫布,自定義子彈渲染。
src/canvas/Bullet.ts

/**
 * 畫布
 * 子彈
 */
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelBullet from "../model/Bullet";
import tank from "./Tank";

/**
 * 畫布是單例模式
 * 在一個圖層,所以只需要new一個實例即可。
 */
export default new (class extends AbstractCanvas implements ICanvas {
        render(): void {
            // super:調用父類的方法
            // super.createModels()
            // 調用渲染模型,防止每次重新渲染時,又生成新的模型實例
            // super.renderModels();

            //bind:綁定this
            setTimeout(this.createBullet.bind(this), 100)
        }

        //自定義子彈的渲染函數:子彈創建
        createBullet() {
            // 子彈的創建需要根據坦克的畫布中的坦克模型數量來決定需要發送多少顆子彈
            tank.models.forEach(tItem => {
                // 查詢是否存在同樣名稱的坦克的子彈,如果有,表示該坦克已經發射子彈了
                const isExists = this.models.some(model => model.tank == tItem)
                //如果子彈不存在,放置子彈
                if (!isExists) {
                    this.models.push(new ModelBullet(tItem))
                }
            })
            console.log(this.models)
        }


        // 抽象方法,返回模型
        model(): ConstructorModelBullet {
            return ModelBullet;
        }

        // 抽象方法:返回模型數量,子彈數量的創建由坦克決定,這裏默認給0
        num(): number {
            return 0;
        }
    }
)('bullet')

src/canvas/abstract/AbstractCanvas.ts

......
    // 繪製模型,生成模型實例,只負責創建實例
    // protected:子類可以調用,外部不能調用
    //num: 渲染多少個數量
    //model: 模型
    protected createModels() {
        position.getPositionCollection(this.num()).forEach((position) => {
            const model = this.model() as ConstructorModel
            const instance = new model(position.x, position.y)
            this.models.push(instance)
        })
    }
......

src/model/Bullet.ts

/**
 * 模型
 * 子彈
 */
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
import config from "../config";
import bullet from "../canvas/Bullet";

export default class ModelStraw extends AbstractModel implements IModel {
    name: string = 'bullet';
    // 畫布實例
    canvas: ICanvas = bullet;

    //tank: 屬性:每一個子彈都有屬於自己的坦克
    constructor(public tank: IModel) {
        // 子彈是坦克發的,所以子彈的座標取的是坦克的x、y座標爲基準的,在坦克中間生成
        super(
            tank.x + Math.floor(config.model.common.width / 2),
            tank.y + Math.floor(config.model.common.height / 2))
    }

    // 繼承父類抽象方法:渲染貼圖

    // 一些初始化自定義的動作、行爲,都在這裏進行
    render(): void {
        super.draw()
    }

    // 獲取貼圖
    getImage(): HTMLImageElement {
        return image.get(this.name as keyof typeof config.images)!;
    }
}

src/vite-env.d.ts

/// <reference types="vite/client" />
/**
 * 全局聲明
 */

/**
 * 模型對象
 */
interface ConstructorModel {
    new(x: number,
        y: number): any
}

/**
 * 子彈構造器類型聲明
 */
interface ConstructorModelBullet {
    new(tank: IModel): any
}

/**
 * 模型實現的函數、方法
 */
interface IModel {
    // 抽象屬性:模型名稱
    name: string
    // 座標,x軸
    x: number
    // 座標,y軸
    y: number
    // 寬度,碰撞判斷需要跨模型調用,所以爲public
    width: number;
    // 高度,碰撞判斷需要跨模型調用,所以爲public
    height: number;

    // 子彈畫布活模型用,子彈所歸屬的坦克模型實例
    tank?: IModel

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

    // 抽象方法:獲取貼圖
    getImage(): HTMLImageElement
}

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

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

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

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

}

子彈模型已經全部生成,剛好是地方坦克的數量——40


子彈的方向

子彈的方向就是坦克當下的方向
src/model/abstract/AbstractModel.ts

......
/**
 * 抽象類
 */
export default abstract class AbstractModel {
......
    // 方向,子彈的方向取決於坦克的方向,需要同層級調用
    public direction: EnumDirection = EnumDirection.top
......
}
......

src/model/Bullet.ts

......
export default class ModelStraw extends AbstractModel implements IModel {
    name: string = 'bullet';
    // 畫布實例
    canvas: ICanvas = bullet;

    //tank: 屬性:每一個子彈都有屬於自己的坦克
    constructor(public tank: IModel) {
        // 子彈是坦克發的,所以子彈的座標取的是坦克的x、y座標爲基準的,在坦克中間生成
        super(
            tank.x + Math.floor(config.model.common.width / 2),
            tank.y + Math.floor(config.model.common.height / 2))
        //子彈的方向就是坦克當下的方向
        this.direction = tank.direction as EnumDirection
    }
......
}
......

src/vite-env.d.ts

......
/**
 * 模型實現的函數、方法
 */
interface IModel {
    // 抽象屬性:模型名稱
    name: string
    // 座標,x軸
    x: number
    // 座標,y軸
    y: number
    // 寬度,碰撞判斷需要跨模型調用,所以爲public
    width: number;
    // 高度,碰撞判斷需要跨模型調用,所以爲public
    height: number;

    // 子彈畫布活模型用,子彈所歸屬的坦克模型實例
    tank?: IModel
    // 方向,子彈的方向取決於坦克的方向,需要同層級調用
    direction: string

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

    // 抽象方法:獲取貼圖
    getImage(): HTMLImageElement
}
......

子彈移動

如下圖,白色方塊就是子彈,這裏存在2個問題,一個是子彈還沒移動,第二個是子彈特別大。


首先是子彈畫布的創建。

/**
 * 畫布
 * 子彈
 */
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelBullet from "../model/Bullet";
import tank from "./Tank";
import config from "../config";

/**
 * 畫布是單例模式
 * 在一個圖層,所以只需要new一個實例即可。
 */
export default new (class extends AbstractCanvas implements ICanvas {
        render(): void {
            // super:調用父類的方法
            // super.createModels()
            // 調用渲染模型,防止每次重新渲染時,又生成新的模型實例
            // super.renderModels();

            //bind:綁定this
            setInterval(()=>{
                // this.createBullet.bind(this)
                //自定義子彈的渲染函數:子彈創建
                this.createModelsBullet()
                // 調用渲染模型,防止每次重新渲染時,又生成新的模型實例
                super.renderModels();
            }, config.bullet.speed)
        }

        //自定義子彈的渲染函數:子彈創建
        createModelsBullet() {
            // 子彈的創建需要根據坦克的畫布中的坦克模型數量來決定需要發送多少顆子彈
            tank.models.forEach(tItem => {
                // 查詢是否存在同樣名稱的坦克的子彈,如果有,表示該坦克已經發射子彈了
                const isExists = this.models.some(model => model.tank === tItem)
                //如果子彈不存在,放置子彈
                if (!isExists) {
                    this.models.push(new ModelBullet(tItem))
                }
            })
            // console.log(this.models)
        }


        // 抽象方法,返回模型
        model(): ConstructorModelBullet {
            return ModelBullet;
        }

        // 抽象方法:返回模型數量,子彈數量的創建由坦克決定,這裏默認給0
        num(): number {
            return 0;
        }
    }
)('bullet')

子彈模型和運動控制。
src/model/Bullet.ts

/**
 * 模型
 * 子彈
 */
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
import config from "../config";
import bullet from "../canvas/Bullet";
import {EnumDirection} from "../enum/enumPosition";
import utils from "../utils";
import wallBrick from "../canvas/WallBrick";
import wallSteel from "../canvas/WallSteel";

export default class ModelStraw extends AbstractModel implements IModel {
    name: string = 'bullet';
    // 畫布實例
    canvas: ICanvas = bullet;

    //tank: 屬性:每一個子彈都有屬於自己的坦克
    constructor(public tank: IModel) {
        // 子彈是坦克發的,所以子彈的座標取的是坦克的x、y座標爲基準的,在坦克中間生成
        super(
            tank.x + Math.floor(config.model.common.width / 2),
            tank.y + Math.floor(config.model.common.height / 2))
        //子彈的方向就是坦克當下的方向
        this.direction = tank.direction as EnumDirection
    }

    // 繼承父類抽象方法:渲染貼圖

    // 一些初始化自定義的動作、行爲,都在這裏進行
    render(): void {
        // super.draw()
        // ********************* 座標更新 *********************
        let x = this.x;
        let y = this.y;
        switch (this.direction) {
            case EnumDirection.top:
                y--
                break;
            case EnumDirection.right:
                x++
                break;
            case EnumDirection.bottom:
                y++
                break;
            case EnumDirection.left:
                x--
                break;
        }

        // // ********************* 座標更新 *********************
        let touchNotBlowModel = utils.modelTouch(x, y, [
                ...wallSteel.models,// 鋼牆
            ],
            config.model.bullet.width,
            config.model.bullet.height
        )
        let touchBlowModel = utils.modelTouch(x, y, [
                ...wallBrick.models,// 磚牆
            ],
            config.model.bullet.width,
            config.model.bullet.height
        )
        //
        if (touchNotBlowModel || utils.isCanvasTouch(x, y, config.model.bullet.width, config.model.bullet.height)) {
            // 移除模型
            this.destroy()
        } else if (touchBlowModel) {
            // 當前子彈模型消失
            this.destroy()
            // 碰撞的模型消失
            touchBlowModel.destroy();
        } else {
            this.x = x;
            this.y = y;
        }

        // 畫布重繪, 渲染坦克模型,在這裏調用減少重繪次數
        this.draw()
    }

    // 函數:渲染模型
    draw(): void {
        this.canvas.ctx.drawImage(
            this.getImage(),
            this.x,
            this.y,
            config.model.bullet.width,
            config.model.bullet.height
        )
    }

    // 獲取貼圖
    getImage(): HTMLImageElement {
        return image.get(this.name as keyof typeof config.images)!;
    }
}

這裏涉及到碰撞檢測的方法,和地方坦克的碰撞檢測方法類似,可以抽象成工具類。
src/utils.ts

import config from "./config";
import water from "./canvas/Water";
import wallBrick from "./canvas/WallBrick";
import wallSteel from "./canvas/WallSteel";

export default {
    /**
     * 判斷是否與畫布觸碰
     * @param x  當前模型的X軸
     * @param y  當前模型的Y軸
     * @param width  當前模型的寬
     * @param height 當前模型的高
     */
    isCanvasTouch(
        x: number,
        y: number,
        width: number = config.model.common.width,
        height: number = config.model.common.height
    ): boolean {
        // ********************* 座標邊界判斷 *********************
        //x最大的座標邊界
        let maxX = config.canvas.width - width;
        //x最大的座標邊界
        let maxY = config.canvas.height - height;

        return x < 0 || x > maxX || y < 0 || y > maxY;
        // ********************* 座標邊界判斷 *********************
    },

    /**
     * 判斷是否與模型觸碰
     * @param x  當前模型的X軸
     * @param y  當前模型的Y軸
     * @param width  當前模型的寬
     * @param height 當前模型的高
     * @param models 需要判斷碰撞的其他模型集合
     */
    modelTouch(
        x: number,
        y: number,
        models: IModel[] = [
            ...water.models,// 水域
            ...wallBrick.models,// 磚牆
            ...wallSteel.models,// 鋼牆
        ],
        width: number = config.model.common.width,
        height: number = config.model.common.height,
    ): IModel | undefined {
        // ********************* 其他模型碰撞判斷 *********************
        return models.find(model => {
            let leftX = model.x - width // 物體模型 左側邊碰撞判斷
            let rightX = model.x + model.width// 物體模型 右側邊碰撞判斷
            let topY = model.y - height// 物體模型 上側邊碰撞判斷
            let bottomY = model.y + model.height// 物體模型 下側邊碰撞判斷
            const state = x <= leftX || x >= rightX || y <= topY || y >= bottomY
            return !state
        })
        // ********************* 其他模型碰撞判斷 *********************
    }
}

src/main.ts

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

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


const bootstrap = async () => {
    // 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() // 畫布渲染:子彈
}

void bootstrap()

對應的src/canvas/abstract/AbstractCanvas.ts

import config from "../../config";
import position from "../../service/position";

/**
 * 抽象類
 */
export default abstract class AbstractCanvas {

    // 元素實例:模型,元素碰撞需要取用,所以爲public
    public models: IModel[] = []

    //構造函數渲染
    constructor(
        protected name: string,
        protected app = document.querySelector('#app') as HTMLDivElement,
        // @ts-ignore
        protected el = document.createElement<HTMLCanvasElement>('canvas')!,
        // @ts-ignore
        public ctx = el.getContext('2d')!
    ) {
        this.createCanvas()
    }

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

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

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

    // 畫布移除模型
    public removeModel(model:IModel): void {
        this.models = this.models.filter(mItem => mItem !=model)
    }

    // 繪製模型,生成模型實例,只負責創建實例
    // protected:子類可以調用,外部不能調用
    //num: 渲染多少個數量
    // 初始化canvas,創建畫布
    protected createCanvas() {
        // 元素的寬高就是全局canvas得到寬高
        // @ts-ignore
        this.el.width = config.canvas.width
        // @ts-ignore
        this.el.height = config.canvas.height

        // 測試畫布
        // 定義填充顏色
        // this.canvas.fillStyle = '#16a085'
        // 繪製矩形
        // this.canvas.fillRect(0, 0, config.canvas.width, config.canvas.height)

        // 最終元素要放到我們的app的div中
        // @ts-ignore
        this.el.setAttribute('name', this.name)
        // @ts-ignore
        this.app.insertAdjacentElement('afterbegin', this.el)
        // @ts-ignore
        // this.app.appendChild(this.el)
    }

    //model: 模型
    protected createModels() {
        position.getPositionCollection(this.num()).forEach((position) => {
            const model = this.model() as ConstructorModel
            const instance = new model(position.x, position.y)
            this.models.push(instance)
            // this.canvas.drawImage(
            //     image.get('straw')!,
            //     position.x,
            //     position.y,
            //     config.model.common.width,
            //     config.model.common.height
            // );
        })
        // Array(num).fill('').forEach(() => {
        //     const position = this.position()
        //     this.canvas.drawImage(
        //         image.get('straw')!,
        //         position.x,
        //         position.y,
        //         config.model.common.width,
        //         config.model.common.height
        //     );
        // })

        // const img = document.createElement('img')
        // img.src = imgUrl;
        // //圖片是異步加載,所以需要將圖片加載完畢後,才進行渲染繪製
        // img.onload = () => {
        //     const position = this.position()
        //     this.canvas.drawImage(img, position.x, position.y, config.model.common.width, config.model.common.height);
        // }
    }

    // 畫布渲染模型(將模型渲染到畫布上)。public:模型需要主動渲染畫布時需要調用,所以public
    public renderModels() {
        // 畫布清理
        this.ctx.clearRect(0, 0, config.canvas.width, config.canvas.height)
        this.models.forEach(model => model.render())
    }
}

src/model/abstract/AbstractModel.ts

import config from "../../config";
import {EnumDirection} from "../../enum/enumPosition";

/**
 * 抽象類
 */
export default abstract class AbstractModel {
    // 寬度,碰撞判斷需要跨模型調用,所以爲public
    public width = config.model.common.width;
    // 高度,碰撞判斷需要跨模型調用,所以爲public
    public height = config.model.common.height;
    // 抽象屬性:模型名稱
    abstract name: string
    // 抽象屬性:畫布實例
    abstract canvas: ICanvas
    // 方向,子彈的方向取決於坦克的方向,需要同層級調用
    public direction: EnumDirection = EnumDirection.top

    //構造函數渲染
    // 碰撞判斷需要跨模型調用模型的座標位置,所以爲public
    constructor(
        public x: number,
        public y: number
    ) {
        // 方向隨機生成
        this.randomDirection();
    }

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

    // 抽象方法:獲取貼圖
    abstract getImage(): HTMLImageElement

    // 方向隨機生成
    randomDirection(): void {
        // 隨機取一個
        const index = Math.floor((Math.random() * 4))
        // 存儲方向
        this.direction = Object.keys(EnumDirection)[index] as EnumDirection
    }

    // 函數:卸載(移除)模型
    public destroy(): void {
        // 讓畫布將模型移除掉
        this.canvas.removeModel(this)
        // 重新渲染畫布
        this.canvas.renderModels()
    }

    // 函數:渲染模型
    protected draw(): void {
        this.canvas.ctx.drawImage(
            this.getImage(),
            this.x,
            this.y,
            config.model.common.width,
            config.model.common.height
        )
    }
}

src/canvas/Tank.ts

......
    // 畫布渲染模型(將模型渲染到畫布上)
    public renderModels() {
        // 調用渲染模型,防止每次重新渲染時,又生成新的模型實例
        super.renderModels();

    }
......

src/model/Tank.ts

import {ICanvas, IModel} from "../vite-env";
......
    // 坦克行動
    protected move(): void {
        while (true) {
            // 畫布清空
            // this.canvas.clearRect(this.x, this.y, config.model.common.width, config.model.common.height);
            // ********************* 座標更新 *********************
            let x = this.x;
            let y = this.y;
            switch (this.direction) {
                case EnumDirection.top:
                    y--
                    break;
                case EnumDirection.right:
                    x++
                    break;
                case EnumDirection.bottom:
                    y++
                    break;
                case EnumDirection.left:
                    x--
                    break;
            }
            if (this.isTouch(x, y)) {
                // 隨機獲取方向
                this.randomDirection()
            } else {
                this.x = x;
                this.y = y;
                // 跳出while死循環
                break;
            }
        }
        // ********************* 座標更新 *********************
        // 畫布重繪, 渲染坦克模型,在這裏調用減少重繪次數
        super.draw()
    }

    // 判斷是否觸碰
    protected isTouch(x: number, y: number): boolean {
        // ********************* 座標邊界判斷 *********************
        //x最大的座標邊界
        let maxX = config.canvas.width - this.width;
        //x最大的座標邊界
        let maxY = config.canvas.height - this.height;

        if (x < 0 || x > maxX || y < 0 || y > maxY) {
            return true
        }
        // ********************* 座標邊界判斷 *********************

        // ********************* 其他模型碰撞判斷 *********************
        const models = [
            ...water.models,// 水域
            ...wallBrick.models,// 磚牆
            ...wallSteel.models,// 鋼牆
        ]
        return models.some(model => {
            let leftX = model.x - this.width // 物體模型 左側邊碰撞判斷
            let rightX = model.x + model.width// 物體模型 右側邊碰撞判斷
            let topY = model.y - this.height// 物體模型 上側邊碰撞判斷
            let bottomY = model.y + model.height// 物體模型 下側邊碰撞判斷
            const state = x <= leftX || x >= rightX || y <= topY || y >= bottomY
            return !state
        })
        // ********************* 其他模型碰撞判斷 *********************
    }

......

src/config.ts

// 草地
import imgUrlStraw from './static/images/straw/straw.png'
// 磚牆
import imgUrlWallBrick from './static/images/wall/wall.gif'
// 水域
import imgUrlWater from './static/images/water/water.gif'
// 鋼牆
import imgUrlWallSteel from './static/images/wall/steels.gif'
// 坦克
import imgUrlTankTop from './static/images/tank/top.gif'
import imgUrlTankRight from './static/images/tank/right.gif'
import imgUrlTankLeft from './static/images/tank/left.gif'
import imgUrlTankBottom from './static/images/tank/bottom.gif'
// 子彈
import imgUrlBullet from './static/images/bullet/bullet.jpg'

export default {
    // 畫布
    canvas: {
        width: 900,
        height: 600,
    },
    // 模型
    model: {
        common: {
            width: 30,
            height: 30,
        },
        // 子彈
        bullet: {
            width: 3,
            height: 3,
        }
    },
    //草地
    straw: {
        num: 100,
    },
    // 磚牆
    wallBrick: {
        num: 100,
    },
    // 鋼牆
    wallSteel: {
        num: 30,
    },
    // 水域
    water: {
        num: 40
    },
    // 敵方坦克
    tank: {
        num: 40,// 坦克數量
        speed: 10 // 坦克速度,越小越快
    },
    // 子彈的速度
    bullet: {
        num: 40,// 坦克數量
        speed: 0.1 // 坦克速度,越小越快
    },
    // 圖片
    images: {
        // 草地
        straw: imgUrlStraw,
        // 磚牆
        wallBrick: imgUrlWallBrick,
        // 鋼牆
        wallSteel: imgUrlWallSteel,
        // 水域
        water: imgUrlWater,
        // 敵方坦克
        tankTop: imgUrlTankTop,
        tankRight: imgUrlTankRight,
        tankBottom: imgUrlTankBottom,
        tankLeft: imgUrlTankLeft,
        // 子彈
        bullet: imgUrlBullet
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章