【中秋特文】Vue3实现接月饼小游戏

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.总结

这个游戏可以说是我毕业以后做的最有趣得东西之一了,年复一年、日复一日得重复着业务代码,感觉代码之魂也沉寂了呢,这个小游戏又让我想起来了当初对一切都好奇的自己,回想当年天天一柱擎天的日子,现在却干什么都提不起劲了,唉。你们不要想太多了,我说的是写代码的热情啊,混蛋!

希望这份简单的小游戏,能让你也在忙碌之余,也能想起当初那个什么都没有,但对一切都充满期待的自己吧,忘记了过去的话,也是会迷失自我的吧。

相关推荐
yyywoaini~6 分钟前
wordcount程序
前端·javascript·ajax
Yvonne爱编码17 分钟前
CSS- 4.2 相对定位(position: relative)
前端·css·状态模式·html5·hbuilder
白小白灬27 分钟前
Vue主题色切换实现方案(CSS 变量 + 类名切换)
前端·css·vue.js
江沉晚呤时32 分钟前
.NET Core 中 Swagger 配置详解:常用配置与实战技巧
前端·.netcore
waterHBO1 小时前
chrome 浏览器插件 myTools, 日常小工具。
前端·chrome
哎呦你好1 小时前
HTML 颜色全解析:从命名规则到 RGBA/HSL 值,附透明度设置与场景应用指南
前端·css·html
多云的夏天1 小时前
前端:VUE-(0)-环境搭建和helloworld
前端·javascript·vue.js
Dontla1 小时前
BootCDN介绍(Bootstrap主导的前端开源项目免费CDN加速服务)
前端·开源·bootstrap
90后小陈老师2 小时前
WebXR教学 07 项目5 贪吃蛇小游戏
前端·数码相机
一口一个橘子2 小时前
[ctfshow web入门] web118
前端·web安全·网络安全