1.前要
今天刷掘金,无意间看到了中秋创意投稿大赛,正所谓掘金王者霸业,岂可无我,随设计了一款简单的小游戏,以庆中秋,没想到这点小东西居然也花了我3个小时,太久没写代码了,一些基本的东西都有点生疏了啊,岂可修!多的不说了,祝大家中秋快乐,好运常来。

2.设计思路
本想着设计成金山打字风格的打字游戏,后来一想都过节了,玩个游戏还得敲键盘属实是有点不应该,我是个懒人就设计成用鼠标控制接月饼的游戏吧。接果子那就得有2个主要的元素---月饼还有盘子。然后考虑他们有那些动作和状态。
月饼
月饼作为得分道具,会从空中掉落,一直处于下落状态,直至落地或被盘子接触。落地或接触后随即消失,因此我将状态分为了2个状态:可视与不可视;唯二的事件:落地与接触盘子;唯一的动作:下落。如下图所示
-
移动问题的解决
那么我们来想想如何实现下落的效果,毕业后就一直从事与前端,一直写着无聊的业务代码,从没想过一个div还需要下落的事情,这可把我难到了,难道第一步就要放弃了,怎么可能,我不同意,还好大学用QT做过打砖块的游戏,仔细想想用QT定位元素的时候不是和现在的绝对定位相同吗?瞬间茅塞顿开,用setInterval和绝对定位就可以解决移动问题了。
-
如何消失
消失,那我可太熟了,哈哈,v-show,v-if直接上吧,看着上百个月饼,难道我得声明100个变量给它绑定吗?太可怕了。那就直接用数组和v-for来解决这个问题吧,至于怎么判断该消失哪个月饼呢?一开始的想法是按它的left值来判断,神奇的事情来了,当当,同一列的月饼集体消失了,敬请期待中秋大作<<消失的它>>,这可不行那该用什么来解决呢?要是有个唯一标识符就好了,要不我给月饼一个一个标号吧,列一个list?有一个月饼把list中的值pop一个?不行工程量太大了,我太懒肯定做不来,突然想到神奇的Symbol,唯一标识符,试了下果然能行,这样消失的问题也解决了,月饼死亡或触碰盘子的时候,获取其唯一标识符,通过filter从数组中剔除出去即可。
-
如何判断落地和触碰盘子
上文不是说了用setInterval来移动月饼吗?可以再setInterval中通过月饼的left和top以及月饼的width和height判断与盘子的距离以及与地面的距离,满足条件了就大吼一声"Jojo! 这是我最后的波纹了",随即消失即可。
代码如下,正如我所说我是个懒人加上写的急,所以没怎么打注释。
js
<!-- -->
<template>
<div class="cake" :style="{'background-image':imgUrl,transition: 'top 0.1s ease',position:'absolute' ,width: '20px', height: '20px', left: position.left + 'px', top: position.top + 'px'}"></div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick, defineProps, defineEmits, onBeforeUnmount } from 'vue';
import img from '../assets/cake.png'
let props = defineProps(['position','rabbitPosition','isBoom'])
let imgUrl = props.isBoom ? ref("url('/src/assets/boom.png')") : ref("url('/src/assets/cake.png')")
let isGet = false;
let position = reactive(props.position)
let rabbitPosition = reactive(props.rabbitPosition)
const emit = defineEmits(['getPoint','cakeDie','losePoint'])
let interVal = setInterval(() => {
const pageHeight = document.documentElement.scrollHeight;
if(position.top + 40 + rabbitPosition.bottom >= pageHeight && pageHeight - rabbitPosition.bottom >= position.top) {
if(position.left + 20 >= rabbitPosition.left && position.left <= rabbitPosition.left + 200 && !isGet) {
if(!props.isBoom)
emit('getPoint')
else
emit('losePoint')
isGet = true;
}
}
if(position.top > pageHeight - rabbitPosition.bottom + 10) {
emit('cakeDie')
}
position.top += 10;
},100)
onBeforeUnmount(() => {
clearInterval(interVal)
})
// 运动逻辑
</script>
<style scoped>
.cake{
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
</style>
盘子
盘子的话,唯一状态就是可视,唯一动作是平移,有人问事件呢?刚刚说了得分事件我绑定到了月饼上,臭小子上课不听讲是吧,过来! 赏你一个爱的捏捏。因此实现的非常简单,我们只要告诉页面我们这个盘子在哪就好了。代码如下:
js
<!-- -->
<template>
<div class="rabbit" :style="{width: '100px', height: '20px', position:'absolute', left:left + 'px', bottom: '100px'}">
</div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick,defineEmits} from 'vue';
const emit = defineEmits(['rabbitMove'])
let left = ref(0)
document.addEventListener('mousemove', handleMouseMove);
const pageWidth = document.documentElement.scrollWidth;
function handleMouseMove(event) {
const mouseX = event.pageX; // 鼠标在页面中的水平坐标
// 在这里可以使用获取到的坐标进行后续操作
if(mouseX <= pageWidth - 50 && mouseX >= 50) {
left.value = mouseX - 50;
emit('rabbitMove',mouseX - 50);
}
}
</script>
<style scoped>
.rabbit {
background-image: url('../assets/rabbit.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
</style>
场地环境
有了月饼和盘子,还差什么?想想语文老师经常说什么,讲故事要有时间、地点、人物,我们来看看我们现在有什么,时间就是我们玩的时间可以不考虑;人物:玩家、月饼、盘子(不要问我为什么把盘子、月饼和人放在一起考虑,我是不会告诉你我语文没学好的,才不是的,哼!其实是玩游戏要有带入感嘛);那么就差一个了地点,我们需要给月饼和盘子提供一个场地,所有的事件都会在这里发生,月饼的list也存放在这里,得分、丢分也在这里,还需要通过这个'场地'告诉月饼盘子在哪里。当月饼告诉我们碰到盘子的时候,我们还需要在这里计分。(真可怕,得没得分居然是月饼告诉我得,万一它作弊骗我怎么办)
代码如下:
js
<script setup>
import MoonCake from './components/moonCake.vue'
import Rabbit from './components/rabbit.vue'
import {reactive, ref} from 'vue'
let rabbitPosition = {bottom:100, left:0}
let texts = ref([{left:10,top:200},{left:30,top:100}])
let booms = ref([])
let point = ref(0)
let temp1 = setInterval(() => {
const pageWidth = document.documentElement.clientWidth;
console.log(pageWidth)
const min = 1;
const max = 10;
const randomInt = Math.floor(Math.random() * (max - min + 1)) + min;
let left = pageWidth * randomInt / 10;
console.log(left)
left = left * 0.8 | 0
let key = Symbol(left)
texts.value.push({left:left,top:100,key:key})
}, 1500);
let temp2 = setInterval(() => {
const pageWidth = document.documentElement.clientWidth;
console.log(pageWidth)
const min = 1;
const max = 10;
const randomInt = Math.floor(Math.random() * (max - min + 1)) + min;
let left = pageWidth * randomInt / 10;
left = left * 0.8 | 0
let key = Symbol(left)
if((left / 2).toString().indexOf(".") != -1)
booms.value.push({left:left,top:100,key:key})
}, 3000);
texts.value.forEach(item => {
item.key = Symbol(item.left)
})
function rabbitMove(value) {
rabbitPosition.left = value
}
function getPoint(value) {
point.value++;
texts.value = texts.value.filter(item => item.key != value.key)
}
function losePoint(value) {
point.value--;
booms.value = booms.value.filter(item => item.key != value.key)
}
function cakeDie(value) {
texts.value = texts.value.filter(item => item.key != value.key)
}
function boomDie(value) {
booms.value = booms.value.filter(item => item.key != value.key)
}
</script>
<template>
<div class="main" style="witdh: 100%; height: 100%">/
<MoonCake :isBoom="false" @cakeDie="cakeDie(item)" @getPoint="getPoint(item)" :key="item.key" :rabbitPosition="rabbitPosition" v-for="item in texts" :position="item" ></MoonCake>
<MoonCake :isBoom="true" @cakeDie="boomDie(item)" @losePoint="losePoint(item)" :key="item.key" :rabbitPosition="rabbitPosition" v-for="item in booms" :position="item" ></MoonCake>
<Rabbit @rabbitMove="rabbitMove"/>
<div class="score">{{ point }}</div>
</div>
</template>
<style scoped>
.score{
position:absolute;
right: 50px;
top: 50px;
color: red;
font-size: 30px;
font-weight: 400;
}
.main{
background-image: url('./assets/background.jpeg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
#app {
width: 100%;
height: 100%;
}
html, body {
height: 100%;
}
</style>
3.游玩效果展示
偷偷修改分数为1145分,孰能挡我,嘿嘿嘿,偷偷告诉你们我还加了炸弹哦。

4.需要改进的地方
- 炸弹速度需要进行调整以增加难度。
- 得分、丢分特效
- 盘子太丑了
- 关卡设置
5.总结
这个游戏可以说是我毕业以后做的最有趣得东西之一了,年复一年、日复一日得重复着业务代码,感觉代码之魂也沉寂了呢,这个小游戏又让我想起来了当初对一切都好奇的自己,回想当年天天一柱擎天的日子,现在却干什么都提不起劲了,唉。你们不要想太多了,我说的是写代码的热情啊,混蛋!
希望这份简单的小游戏,能让你也在忙碌之余,也能想起当初那个什么都没有,但对一切都充满期待的自己吧,忘记了过去的话,也是会迷失自我的吧。