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>