vue a failed dom game production

Originally planned to use vue to make a simple airplane game

However, in practice, it is found that the dom performance seems to be a bit poor, and the lag is obvious when there are too many elements.

Let's take a look at canvas later

PS:

It seems that the computed properties and methods are confused, and the position of the element is calculated using the function. . . This method might be less efficient.

 

Project structure

Aids, bullets, planes and balls

 

Introduce these classes on the main page, and then call the methods in the class to control the position of the element and refresh the view

The class includes basic data and operation methods

Note that when setting the interval, this points to the problem, the closure! ! ! ! ! !

If you assign a member function directly, the function will point to windows when it is executed.

import {REFRESH_INTERVAL, HEIGHT, Width} from "./config";

class Ball {
  // 位置,大小,类型,速度,方向是度数,向下为0,顺时针增加
  constructor(x = 0, y = 0, size = 20, type = 0, direction = 0, speed = 10,) {
    this.x = x
    this.y = y
    this.size = size
    this.type = type
    this.speed = speed
    this.direction = direction
    // 闭包问题,下面这种写法是有问题的,在执行move时,this的指向已经变化为windows
    // 所以变化不会生效
    // setInterval(
    //   this.move, 100
    // )

    this.start()

  }

  move() {
    this.x -= this.speed * Math.sin(this.direction * Math.PI / 180)
    this.y -= this.speed * Math.cos(this.direction * Math.PI / 180)
  }


  start() {
    this.inv = setInterval(
      () => this.move(), REFRESH_INTERVAL
    )
  }

  stop() {
    clearInterval(this.inv)
  }

  isDestory() {
    return this.x >= Width || this.x <= 0 || this.y <= 0 || this.y >= HEIGHT
  }
}


export default Ball

 

Globally add key events, the listener added on the div is invalid, it can only be added on the body

      document.body.onkeydown = this.keyDown

 

Use watch to observe data in depth, refresh the view when data changes, and check for collisions and out-of-bounds

Note that the method of binding objects uses strings, or you can use ordinary functions or arrow functions. Note that this does not point to the instance object of vue, so I stepped on the pit here. . . .

    data() {
      return {
        balls: [],
        bullets: [],
        plane: new Plane(0, 0, 2),
        clientHeight: 0,
        clientWidth: 0,
      }
    },
    watch: {
      plane: {
        handler: 'refresh',
        deep: true,
      },
      balls: {
        handler: 'refresh',
        deep: true,
      },
      bullets: {
        handler: 'refresh',
        deep: true,
      }
    },

 

Get the triggered key name through code and trigger the keyboard event

      keyDown(e) {
        console.log(e.code)
        if (e.code == 'ArrowUp') {
          this.move(0)
        } else if (e.code == 'ArrowRight') {
          this.move(1)
        } else if (e.code == 'ArrowDown') {
          this.move(2)
        } else if (e.code == 'ArrowLeft') {
          this.move(3)
        } else if (e.code == 'Space') {
          this.shoot()
        } else if (e.code == 'Enter') {
          this.addBallRandom()
        }
      },

 

 

The main page, the control panel is also a problem. . .

The button is a bit ugly, you should try the mobile button of the glory of the king. . . I don't know if I can find a similar library

<template>
  <div class="main" ref="main">

    <div class="bullet" v-for="b in bullets" :style="getStyle(b)"></div>
    <div class="ball" v-for="b in balls" :style="getBallStyle(b)"></div>
    <!--<div class="ball" :style="getBallStyle(ball)"></div>-->
    <img src="../assets/plane.svg" ref="plane" class="plane" :style="getStyle(plane)">

    <!--<div class="control">-->
    <!--<div class="btn-group">-->
    <!--<button @click="move(0)">U</button>-->
    <!--<button @click="move(1)">R</button>-->
    <!--<button @click="move(2)">D</button>-->
    <!--<button @click="move(3)">L</button>-->
    <!--</div>-->

    <!--<div class="action">-->
    <!--<button @click="shoot">shoot</button>-->
    <!--<button @click="addBallRandom">add</button>-->
    <!--</div>-->
    <!--</div>-->
  </div>
</template>

<script>

  import Ball from '../util/Ball'
  import Bullet from '../util/Bullet'
  import Plane from '../util/Plane'
  import _ from 'lodash'
  import {BALL_COLORS, REFRESH_INTERVAL, HEIGHT, Width} from "../util/config";

  export default {
    name: "game-card",
    data() {
      return {
        balls: [],
        bullets: [],
        plane: new Plane(0, 0, 2),
        ball: {},
      }
    },
    watch: {
      plane: {
        handler: 'refresh',
        deep: true,
      },
      balls: {
        handler: 'refresh',
        deep: true,
      },
      bullets: {
        handler: 'refresh',
        deep: true,
      }
    },
    computed: {
      planeCss() {
        let css = `left:${this.plane.x}px;bottom:${this.plane.y}px`
        console.log(css)
        return css
      }
    },
    methods: {
      getBallStyle(ball) {
        // 灰色,击中得分,并消失
        // 白色,击中分裂黑灰色和白色
        // 蓝色,击中分裂,随机数目和颜色
        // 绿色,全部白色消失
        // 黄色,所有白色变为蓝色
        // 橙色,暂停2s
        // 青色,全体上升
        // 黑色,gg
        let color = BALL_COLORS[ball.type]
        let css = `left:${ball.x}px;bottom:${ball.y}px;width:${ball.size}px;height:${ball.size}px;background:${color}`
        console.log(css)
        return css
      },
      // 返回对象的位置的css字符串
      getStyle(obj) {
        let css = `left:${obj.x}px;bottom:${obj.y}px`
        console.log(css)
        return css
      },
      refresh(newVal, oldVal) {
        console.log('refresh')
      },

      keyDown(e) {
        console.log(e.code)
        if (e.code == 'ArrowUp') {
          this.move(0)
        } else if (e.code == 'ArrowRight') {
          this.move(1)
        } else if (e.code == 'ArrowDown') {
          this.move(2)
        } else if (e.code == 'ArrowLeft') {
          this.move(3)
        } else if (e.code == 'Space') {
          this.shoot()
        } else if (e.code == 'Enter') {
          this.addBallRandom()
        }
      },
      move(direction) {
        this.plane.move(direction)
        console.log(this.$refs.plane.style.left);
      },
      shoot() {
        let b = new Bullet(this.plane.x + 32, this.plane.y + 64)
        this.bullets.push(b)
      },
      addBallRandom() {
        let x = Math.floor(Width * Math.random())
        let y = Math.floor((HEIGHT - 400) * Math.random() + 200)
        let size = Math.min(Math.random() * 100, 30)
        let type = Math.floor(Math.random() * BALL_COLORS.length)
        let direction = (Math.random() - 0.5) * 180
        let b = new Ball(x, y, size, type, direction)
        this.balls.push(b)
      },

      // 检查越界和碰撞函数
      check() {

      }
    },
    mounted() {
      document.body.onkeydown = this.keyDown
      setInterval(
        () => {
          this.check()
        },
        REFRESH_INTERVAL
      )


    }
  }
</script>

<style scoped>
  .main {
    width: 100vw;
    height: 100vh;
    position: relative;

  }

  .plane {
    position: absolute;
    left: 0;
    bottom: 0;
  }

  .bullet {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: black;
    position: absolute;
    left: 0;
    bottom: 0;
  }

  .ball {
    width: 10px;
    height: 10px;
    background: deepskyblue;
    position: absolute;
    border-radius: 50%;
  }

  .control {
    display: flex;
    position: fixed;
    width: 100%;
    bottom: 0;
    justify-content: space-around;
  }
</style>

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325329841&siteId=291194637