美文网首页js css html
【Tank】10.0 游戏入口、游戏开始和结束

【Tank】10.0 游戏入口、游戏开始和结束

作者: bobokaka | 来源:发表于2022-05-16 10:27 被阅读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() // 画布渲染:玩家坦克
    }
}
image.png

游戏开始

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() {

    }
}
image.png

游戏结束

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() {
    }
......

失败或者胜利时,游戏画面禁止。


image.png

相关文章

网友评论

    本文标题:【Tank】10.0 游戏入口、游戏开始和结束

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