美文网首页让前端飞
【Tank】5.0 水域模型、钢墙模型、坦克模型

【Tank】5.0 水域模型、钢墙模型、坦克模型

作者: bobokaka | 来源:发表于2022-05-13 15:21 被阅读0次

    水域模型、钢墙模型

    同砖墙模型的创建。
    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'
    
    export default {
        // 画布
        canvas: {
            width: 900,
            height: 600,
        },
        // 模型
        model: {
            // 草地
            straw: {
                width: 30,
                height: 30,
            }
        },
        //草地
        straw: {
            num: 100,
        },
        // 砖墙
        wallBrick: {
            num: 100,
        },
        // 钢墙
        wallSteel:{
            num: 50,
        },
        // 水域
        water:{
            num:40
        },
        // 图片
        images: {
            // 草地
            straw: imgUrlStraw,
            wallBrick: imgUrlWallBrick,
            wallSteel:imgUrlWallSteel,
            water:imgUrlWater,
            tank: imgUrlTankTop
        }
    }
    

    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 {promises} from "./service/image";
    
    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()
    }
    
    void bootstrap()
    

    src/canvas/WallBrick.ts

    /**
     * 画布
     * 墙
     */
    import config from "../config";
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelWall from '../model/WallBrick'
    
    class WallBrick extends AbstractCanvas {
        // 构造函数,初始时run一次
        constructor() {
            super();
            // super:调用父类的方法
            super.createModels(config.wallBrick.num, ModelWall)
        }
    
        render(): void {
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    }
    
    
    // 墙在一个图层,所以只需要new一个实例即可。
    export default new WallBrick()
    

    src/canvas/WallSteel.ts

    /**
     * 画布
     * 墙
     */
    import config from "../config";
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelWallSteel from '../model/WallSteel'
    
    class WallSteel extends AbstractCanvas {
        // 构造函数,初始时run一次
        constructor() {
            super();
            // super:调用父类的方法
            super.createModels(config.wallSteel.num, ModelWallSteel)
        }
    
        render(): void {
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    }
    
    
    // 墙在一个图层,所以只需要new一个实例即可。
    export default new WallSteel()
    
    

    src/canvas/Water.ts

    /**
     * 画布
     * 水域
     */
    import config from "../config";
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelWater from '../model/Water'
    
    class Water extends AbstractCanvas {
        // 构造函数,初始时run一次
        constructor() {
            super();
            // super:调用父类的方法
            super.createModels(config.water.num, ModelWater)
        }
    
        render(): void {
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    }
    
    
    // 墙在一个图层,所以只需要new一个实例即可。
    export default new Water()
    
    

    src/model/WallBrick.ts

    /**
     * 模型
     * 草地
     */
    import AbstractModel from "./abstract/AbstractModel";
    import {image} from "../service/image";
    
    export default class ModelWall extends AbstractModel implements IModel {
        // 继承父类抽象方法:渲染贴图
        // 一些初始化自定义的动作、行为,都在这里进行
        render(): void {
            super.draw(image.get("wallBrick")!)
        }
    }
    
    

    src/model/WallSteel.ts

    /**
     * 模型
     * 草地
     */
    import AbstractModel from "./abstract/AbstractModel";
    import {image} from "../service/image";
    
    export default class ModelWallSteel extends AbstractModel implements IModel {
        // 继承父类抽象方法:渲染贴图
        // 一些初始化自定义的动作、行为,都在这里进行
        render(): void {
            super.draw(image.get("wallSteel")!)
        }
    }
    
    

    src/model/Water.ts

    /**
     * 模型
     * 水域
     */
    import AbstractModel from "./abstract/AbstractModel";
    import {image} from "../service/image";
    
    export default class ModelWater extends AbstractModel implements IModel {
        // 继承父类抽象方法:渲染贴图
        // 一些初始化自定义的动作、行为,都在这里进行
        render(): void {
            super.draw(image.get("water")!)
        }
    }
    
    

    效果如下:


    image.png

    重构游戏元素生成逻辑

    主要将模型数量和模型实例抽象出来,以便初始化。
    src/canvas/abstract/AbstractCanvas.ts

    import config from "../../config";
    import position from "../../service/position";
    
    /**
     * 抽象类
     */
    export default abstract class AbstractCanvas {
    
        // 元素实例:模型
        protected models: IModel[] = []
    
        //构造函数渲染
        constructor(
            protected app = document.querySelector('#app') as HTMLDivElement,
            // @ts-ignore
            protected el = document.createElement<HTMLCanvasElement>('canvas')!,
            // @ts-ignore
            protected canvas = el.getContext('2d')!
        ) {
            this.createCanvas()
        }
    
        // 抽象方法:渲染贴图
        abstract render(): void
    
        // 抽象方法,返回模型
        abstract model(): ConstructorModel
    
        // 抽象方法:返回模型数量
        abstract num(): number
    
        // 初始化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.app.insertAdjacentElement('afterbegin', this.el)
        }
    
        // 绘制模型,生成模型实例,只负责创建实例
        // protected:子类可以调用,外部不能调用
        //num: 渲染多少个数量
        //model: 模型
        protected createModels() {
            position.getPositionCollection(this.num()).forEach((position) => {
                const model = this.model()
                const instance = new model(this.canvas, position.x, position.y)
                this.models.push(instance)
                // this.canvas.drawImage(
                //     image.get('straw')!,
                //     position.x,
                //     position.y,
                //     config.model.straw.width,
                //     config.model.straw.height
                // );
            })
            // Array(num).fill('').forEach(() => {
            //     const position = this.position()
            //     this.canvas.drawImage(
            //         image.get('straw')!,
            //         position.x,
            //         position.y,
            //         config.model.straw.width,
            //         config.model.straw.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.straw.width, config.model.straw.height);
            // }
        }
    
        // 画布渲染模型(将模型渲染到画布上)
        protected renderModels() {
            this.models.forEach(model => model.render())
        }
    
    }
    

    src/canvas/Straw.ts

    /**
     * 画布
     * 草地
     */
    import config from "../config";
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelStraw from "../model/Straw";
    
    class Straw extends AbstractCanvas implements ICanvas{
        render(): void {
            // super:调用父类的方法
            super.createModels()
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    
        // 抽象方法,返回模型
        model(): ConstructorModel {
            return ModelStraw;
        }
    
        // 抽象方法:返回模型数量
        num(): number {
            return config.straw.num;
        }
    }
    
    
    // 草地在一个图层,所以只需要new一个实例即可。
    export default new Straw()
    

    src/canvas/WallBrick.ts

    /**
     * 画布
     * 墙
     */
    import config from "../config";
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelWall from '../model/WallBrick'
    
    class WallBrick extends AbstractCanvas implements ICanvas {
        render(): void {
            // super:调用父类的方法
            super.createModels()
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    
        // 抽象方法,返回模型
        model(): ConstructorModel {
            return ModelWall;
        }
    
        // 抽象方法:返回模型数量
        num(): number {
            return config.wallBrick.num
        }
    }
    
    
    // 墙在一个图层,所以只需要new一个实例即可。
    export default new WallBrick()
    

    src/canvas/WallSteel.ts

    /**
     * 画布
     * 墙
     */
    import config from "../config";
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelWallSteel from '../model/WallSteel'
    
    class WallSteel extends AbstractCanvas implements ICanvas {
        render(): void {
            // super:调用父类的方法
            super.createModels()
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    
        // 抽象方法,返回模型
        model(): ConstructorModel {
            return ModelWallSteel;
        }
    
        // 抽象方法:返回模型数量
        num(): number {
            return config.straw.num
        }
    
    }
    
    
    // 墙在一个图层,所以只需要new一个实例即可。
    export default new WallSteel()
    

    src/canvas/Water.ts

    /**
     * 画布
     * 水域
     */
    import config from "../config";
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelWater from '../model/Water'
    
    class Water extends AbstractCanvas implements ICanvas{
        // 抽象方法:渲染贴图
        render(): void {
            // super:调用父类的方法
            super.createModels()
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    
        // 抽象方法,返回模型
        model(): ConstructorModel {
            return ModelWater;
        }
    
        // 抽象方法:返回模型数量
        num(): number {
            return config.water.num
        }
    }
    
    
    // 墙在一个图层,所以只需要new一个实例即可。
    export default new Water()
    

    TS支持提醒优化,src/vite-env.d.ts

    /// <reference types="vite/client" />
    /**
     * 全局声明
     */
    
    /**
     * 模型对象
     */
    interface ConstructorModel {
        new(canvas: CanvasRenderingContext2D,
            x: number,
            y: number): any
    }
    
    /**
     * 模型实现的函数、方法
     */
    interface IModel {
        // 抽象方法:渲染贴图
        render(): void
    
    }
    /**
     * 画布实现的函数、方法
     */
    interface ICanvas {
        // 抽象方法:渲染贴图
        render(): void
    
        // 抽象方法,返回模型
        model(): ConstructorModel
    
        // 抽象方法:返回模型数量
        num(): number
    }
    
    

    运行结果如下:


    image.png

    绘制敌方坦克

    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'
    
    export default {
        // 画布
        canvas: {
            width: 900,
            height: 600,
        },
        // 模型
        model: {
            // 草地
            straw: {
                width: 30,
                height: 30,
            }
        },
        //草地
        straw: {
            num: 100,
        },
        // 砖墙
        wallBrick: {
            num: 100,
        },
        // 钢墙
        wallSteel:{
            num: 40,
        },
        // 水域
        water:{
            num:40
        },
        // 水域
        tank:{
            num:40
        },
        // 图片
        images: {
            // 草地
            straw: imgUrlStraw,
            wallBrick: imgUrlWallBrick,
            wallSteel:imgUrlWallSteel,
            water:imgUrlWater,
            tank: imgUrlTankTop
        }
    }
    
    

    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";
    
    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()
    }
    
    void bootstrap()
    
    

    src/model/Tank.ts

    /**
     * 模型
     * 草地
     */
    import AbstractModel from "./abstract/AbstractModel";
    import {image} from "../service/image";
    
    export default class ModelTank extends AbstractModel implements IModel {
        // 继承父类抽象方法:渲染贴图
        // 一些初始化自定义的动作、行为,都在这里进行
        render(): void {
            super.draw(image.get("tank")!)
        }
    }
    
    

    src/canvas/Tank.ts

    /**
     * 画布
     * 坦克
     */
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelTank from "../model/Tank";
    import config from "../config";
    
    class Tank extends AbstractCanvas implements ICanvas {
        render(): void {
            // super:调用父类的方法
            super.createModels()
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    
        // 抽象方法,返回模型
        model(): ConstructorModel {
            return ModelTank;
        }
    
        // 抽象方法:返回模型数量
        num(): number {
            return config.tank.num
        }
    }
    
    // 坦克在一个图层,所以只需要new一个实例即可。
    export default new Tank()
    

    效果如下:


    image.png

    坦克渲染在最顶层,且方向随机

    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'
    
    export default {
        // 画布
        canvas: {
            width: 900,
            height: 600,
        },
        // 模型
        model: {
            // 草地
            straw: {
                width: 30,
                height: 30,
            }
        },
        //草地
        straw: {
            num: 100,
        },
        // 砖墙
        wallBrick: {
            num: 100,
        },
        // 钢墙
        wallSteel:{
            num: 30,
        },
        // 水域
        water:{
            num:40
        },
        // 水域
        tank:{
            num:40
        },
        // 图片
        images: {
            // 草地
            straw: imgUrlStraw,
            wallBrick: imgUrlWallBrick,
            wallSteel:imgUrlWallSteel,
            water:imgUrlWater,
            tankTop: imgUrlTankTop,
            tankRight: imgUrlTankRight,
            tankBottom: imgUrlTankBottom,
            tankLeft: imgUrlTankLeft,
        }
    }
    
    

    src/canvas/Tank.ts

    /**
     * 画布
     * 坦克
     */
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelTank from "../model/Tank";
    import config from "../config";
    import position from "../service/position";
    
    class Tank extends AbstractCanvas implements ICanvas {
        render(): void {
            // super:调用父类的方法
            this.createModels()
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    
        // 抽象方法,返回模型
        model(): ConstructorModel {
            return ModelTank;
        }
    
        // 抽象方法:返回模型数量
        num(): number {
            return config.tank.num
        }
    
        // 重写父类方法
        // 绘制模型,生成模型实例,只负责创建实例
        createModels() {
            for (let i = 0; i < this.num(); i++) {
                const pos = position.position()
                const model = this.model()
                //Y轴永远从0开始
                const instance = new model(this.canvas, pos.x, 0)
                this.models.push(instance)
            }
        }
    }
    
    // 坦克在一个图层,所以只需要new一个实例即可。
    export default new Tank()
    

    src/model/Tank.ts

    /**
     * 画布
     * 坦克
     */
    import AbstractCanvas from "./abstract/AbstractCanvas";
    import ModelTank from "../model/Tank";
    import config from "../config";
    import position from "../service/position";
    
    class Tank extends AbstractCanvas implements ICanvas {
        render(): void {
            // super:调用父类的方法
            this.createModels()
            // 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
            super.renderModels();
        }
    
        // 抽象方法,返回模型
        model(): ConstructorModel {
            return ModelTank;
        }
    
        // 抽象方法:返回模型数量
        num(): number {
            return config.tank.num
        }
    
        // 重写父类方法
        // 绘制模型,生成模型实例,只负责创建实例
        createModels() {
            for (let i = 0; i < this.num(); i++) {
                const pos = position.position()
                const model = this.model()
                //Y轴永远从0开始
                const instance = new model(this.canvas, pos.x, 0)
                this.models.push(instance)
            }
        }
    }
    
    // 坦克在一个图层,所以只需要new一个实例即可。
    export default new Tank()
    

    src/enum/enumPosition.ts

    /**
     * 枚举类
     *
     */
    
    // 方向
    export enum EnumDirection {
        top = 'top',
        right = 'right',
        bottom = 'bottom',
        left = 'left'
    }
    
    image.png

    Lodash——JS工具库

    在src/model/Tank.ts中,如下代码可以优化如下:

    ......
        // 随机取用其中一个图片
        randomImage(): HTMLImageElement {
            let img: HTMLImageElement;
            switch (this.direction) {
                case EnumDirection.top:
                    img = image.get('tankTop')!
                    break;
                case EnumDirection.right:
                    img = image.get('tankRight')!
                    break;
                case EnumDirection.bottom:
                    img = image.get('tankBottom')!
                    break;
                case EnumDirection.left:
                    img = image.get('tankLeft')!
                    break;
                default:
                    img = image.get('tankTop')!
                    break;
            }
            return img
        }
    ......
    

    安装lodash

     npm install -D  lodash
     npm install -S  @type/lodash
    

    package.json

    {
      "name": "tank",
      "private": true,
      "version": "0.0.0",
      "scripts": {
        "dev": "vite",
        "build": "tsc && vite build",
        "preview": "vite preview"
      },
      "dependencies": {
        "lodash": "^4.17.21"
      },
      "devDependencies": {
        "@types/lodash": "^4.14.182",
        "sass": "^1.51.0",
        "typescript": "^4.5.4",
        "vite": "^2.9.7"
      }
    }
    

    src/model/abstract/AbstractModel.ts

    import config from "../../config";
    
    /**
     * 抽象类
     */
    export default abstract class AbstractModel {
        //构造函数渲染
        constructor(
            protected canvas: CanvasRenderingContext2D,
            protected x: number,
            protected y: number
        ) {
        }
    
        // 抽象属性:模型名称
        abstract name: string
    
        // 抽象方法:渲染贴图
        abstract render(): void
    
        // 渲染函数
        protected draw(img: HTMLImageElement) {
            this.canvas.drawImage(
                img,
                this.x,
                this.y,
                config.model.straw.width,
                config.model.straw.height
            )
        }
    }
    

    src/model/Tank.ts

    /**
     * 模型
     * 草地
     */
    import AbstractModel from "./abstract/AbstractModel";
    import {image} from "../service/image";
    import {EnumDirection} from "../enum/enumPosition";
    
    import {upperFirst} from 'lodash'
    import config from "../config";
    
    export default class ModelTank extends AbstractModel implements IModel {
    
        name: string = 'tank';
        // 方向
        protected direction: EnumDirection = EnumDirection.top
        // 继承父类抽象方法:渲染贴图
        // 一些初始化自定义的动作、行为,都在这里进行
        render(): void {
            this.randomDirection();
            super.draw(this.randomImage())
        }
    
        randomDirection() {
            //  随机取一个
            const index = Math.floor((Math.random() * 4))
            this.direction = Object.keys(EnumDirection)[index] as EnumDirection
        }
    
        // 随机取用其中一个图片
        randomImage(): HTMLImageElement {
            return image.get(`${this.name}${upperFirst(this.direction)}` as keyof typeof config.images)!
            // let img: HTMLImageElement;
            // switch (this.direction) {
            //     case EnumDirection.top:
            //         img = image.get('tankTop')!
            //         break;
            //     case EnumDirection.right:
            //         img = image.get('tankRight')!
            //         break;
            //     case EnumDirection.bottom:
            //         img = image.get('tankBottom')!
            //         break;
            //     case EnumDirection.left:
            //         img = image.get('tankLeft')!
            //         break;
            //     default:
            //         img = image.get('tankTop')!
            //         break;
            // }
            // return img
        }
    
    }
    

    src/model/Straw.ts、src/model/WallBrick.ts、src/model/WallSteel.ts、src/model/Water.ts修改类似,这里不重复贴代码。

    /**
     * 模型
     * 草地
     */
    import AbstractModel from "./abstract/AbstractModel";
    import {image} from "../service/image";
    import config from "../config";
    
    export default class ModelStraw extends AbstractModel implements IModel {
        name: string = 'straw';// 这里的名字换成对应的贴图名称即可
        // 继承父类抽象方法:渲染贴图
        // 一些初始化自定义的动作、行为,都在这里进行
        render(): void {
            super.draw(image.get(this.name as keyof typeof config.images)!)
        }
    
    }
    

    效果不变:


    image.png

    相关文章

      网友评论

        本文标题:【Tank】5.0 水域模型、钢墙模型、坦克模型

        本文链接:https://www.haomeiwen.com/subject/iobcurtx.html