vue3版本网页小游戏设计思路 您所在的位置:网站首页 前端编程游戏 vue3版本网页小游戏设计思路

vue3版本网页小游戏设计思路

2023-01-03 03:40| 来源: 网络整理| 查看: 265

vue3版本网页小游戏设计思路

 

1.前言

最近火爆全网的羊了个羊小程序,背景是根据官方介绍,“羊了个羊”是一款闯关消除小游戏,通关率不到0.1%。主要玩法为重叠的各类方块,需要在下方7个栏内完成消除(3个同类消除),其特点就是“极难”,也因此成为热门挑战。我也颇感兴趣,去玩了2把,的确很有乐趣,整理了一下思路,决定搞个vue3版本的网页版本,我看网上有react版本的了,vue3版本还没有,下面分别给出设计思路,实现方式,和玩法

设计思路:

1,先来一张背景图,网上搜一张草地图片

2,最底部设置七个槽位,有三个连续相同的就消除,槽位满了的话,挑战失败

3,中间的图层区域使用重叠的方式,可能是半重叠,可能是全重叠,只有第一层可以移入槽位,全部消除时,表示挑战成功!后续挑战是变化关卡的布局方式(多种排列方式)

4,点击事件的思路(内层不能点击,前置点击如果槽位满了还没有消除完,关卡的消除,消除动作 和 添加爆炸效果,进入下一关,挑战失败)

5,辅助类函数:判断是否过关,消除函数,实现爆炸效果,控制关卡

实现方式:

vue3配合pinia实现数据驱动页面

玩法:

使用关卡模式,从第1关简单到2困难,3关复杂,这里的关卡只是数据的多少变化而已,可以设计出无数关卡,这里前端模拟json数据,使用对象json

效果演示:

在线体验 :

KinHKin https://rondsjinhuajin.github.io/DemoVue/#/

源码地址:

在github欢迎follow和star,感谢可爱的各位看官大佬~❤️

 

2.实现过程 2.1目录

2.2文件介绍

入口文件index.vue,设计背景色

import Header from "./components/Header.vue"; import Main from "./components/Main.vue"; .sheep-wrap { .sheep { display: flex; flex-direction: column; height: 100%; width: 100%; padding-bottom: 20px; } width: 100%; height: calc(100vh - 60px); background: url("../../assets/images/sheep.png") center no-repeat; background-size: cover; }

Header.vue文件,文字动效,配合pinia显示第几关

import { useSheepStore } from "@/stores/sheep"; const store = useSheepStore(); 第{{ store.step + 1 }}关 羊了个羊vue3版本(KinHKin) .flex-center { display: flex; align-items: center; } .sheep-header { padding-top: 2rem; text-align: center; letter-spacing: 0.2rem; font-size: 1.5rem; color: #fff; border-bottom: 1px solid #1d9614; padding-bottom: 1rem; margin-bottom: 2rem; div .l { background-image: -webkit-linear-gradient( left, #1d9614, #fff 25%, #666 50%, #e6d205 75%, #fff ); -webkit-text-fill-color: transparent; -webkit-background-clip: text; -webkit-background-size: 200% 100%; -webkit-animation: maskedAnimation 4s infinite linear; padding-right: 8px; } } @keyframes maskedAnimation { 0% { background-position: 0 0; } 100% { background-position: -100% 0; } }

Main.vue文件是核心文件,作用是引入颜色,控制关卡,设置关卡数据,如何消除,增加爆炸动效,控制交互逻辑等。

import { ref, type Ref } from "vue"; import { ElMessage, ElMessageBox } from "element-plus"; import { useSheepStore } from "@/stores/sheep"; // 关卡数据 import data from "./data.json"; // 颜色 import constants from "./constants"; // pinia 控制关卡 const store = useSheepStore(); // 七个槽位 // const footerList = ref([0, 1, 2, 3, 4, 5, 6]); const footerList: Ref = ref([]); const colors = ref(constants.colors); // 关卡响应式 const totalList: Ref = ref([]); totalList.value = data["list1"]; // 默认第一关 // 控制动画效果结束才能点击 const isNotClick = ref(false); // 点击控制事件 function handleClick( i: number, k: number, onei: { oneSub: string | Array }, onek: number, oneiSub: Array, onekSub: number ) { console.log(i, k, onei, onek, oneiSub, onekSub, "测试"); if (isNotClick.value) { return false; } // 内层不能点击 if (onekSub !== onei.oneSub.length - 1) { return false; } // 前置点击如果槽位满了还没有消除完 fullFun() // 关卡的消除 let tempList = fixFun(k, onekSub, onek, oneiSub) // 消除动作 和 添加爆炸效果 if (footerList.value.length > 2) { isNotClick.value = true const { list, flag } = eliminationFunction(footerList.value) footerList.value = list; if (flag) { footerList.value = addBoomFunction(footerList.value); } setTimeout(() => { const { list, flag } = eliminationFunction(footerList.value) footerList.value = list; isNotClick.value = false }, 1000); // 进入下一关 nextFun(tempList) } // 挑战失败 failFun(tempList) console.log(footerList, tempList, "tempList"); } // full function fullFun() { if (footerList.value.length === 7) { ElMessage.closeAll(); ElMessageBox.alert("挑战失败,点击确定返回!", "Warning", { confirmButtonText: "确定", type: "warning", showClose: false, }).then(() => { location.reload(); }); return false; } } // fix function fixFun(k: number, onekSub: number, onek: number, oneiSub: Array) { const { value } = totalList; let tempList = JSON.parse(JSON.stringify(value)); for (let i = 0; i < tempList.length; i++) { const one = tempList[k].one; for (let j = 0; j < one.length; j++) { const oneSub = one[onek]; for (let k = 0; k < oneSub.oneSub.length; k++) { if (onekSub === k) { const footItem = oneSub.oneSub.splice(onekSub); break; } } } } footerList.value.push(oneiSub); totalList.value = tempList; return tempList } //fail function failFun(tempList: any[]) { setTimeout(() => { if (footerList.value.length > 0 && !jugeList(tempList)) { ElMessage.closeAll(); ElMessageBox.alert("挑战失败,点击确定返回!", "Warning", { confirmButtonText: "确定", type: "warning", showClose: false, }).then(() => { location.reload(); }); return false; } }, 1002) } // next function nextFun(tempList: any[]) { setTimeout(() => { if (!footerList.value.length && !jugeList(tempList)) { // debugger ElMessage.closeAll(); ElMessage.success("恭喜您,挑战成功!进入下一关"); store.step++; const inStep: string = "list" + (store.step + 1); totalList.value = JSON.parse(JSON.stringify(data))[inStep]; footerList.value = []; } }, 1001) } // 判断是否过关 function jugeList(list: any[]) { let temp: any = []; list?.forEach((oeni: { one: any }) => { oeni?.one?.forEach((sub: { oneSub: any }) => { temp = [...temp, ...sub.oneSub]; }); }); return temp.length; } // 消除函数 function eliminationFunction(list: any[]) { let flag: boolean = false; for (let k = 0; k < list.length - 2; k++) { const temp = list; const arr = temp.slice(k, k + 3); console.log(k, arr); if (arr[0] === arr[1] && arr[1] === arr[2] && arr[0] === arr[2]) { list.splice(k + 2); list.splice(k + 1); list.splice(k, 1); flag = true break; } } return { list, flag }; } // 实现爆炸效果 function addBoomFunction(list: any[]) { const temp = JSON.parse(JSON.stringify([...list, ...['boom', 'boom', 'boom']])) return temp; } .flex-center { display: flex; align-items: center; } .el-row { // margin-top: 3rem; height: 28%; } .fz { font-size: 3rem; border: 1px solid #dfe5f9; // box-shadow: 2px 2px 10px #f3f6fe; background: #f3f6fe; border-radius: 5px; } .pic-list { position: relative; width: 100%; height: 100%; &-item { position: absolute; left: 10vw; cursor: pointer; transition: all 0.3s; &:nth-child(1n) { top: calc(var(--i) * 1.5rem); } &.true { box-shadow: 0 -55px 0 0 #dfe5f9 inset; } // &:nth-child(even) { // top: 2rem; // } } } .sheep-main { flex: 1; &-wrap { height: calc(100% - 80px); } } .sheep-footer { height: 80px; width: 100%; // border: 2px solid #298df9; border: 2px solid #778899; background: #010206; .sheep-footer-items { height: 80px; width: calc(100% / 7); margin-left: 8px; display: flex; align-items: center; justify-content: center; .boom-class { font-size: 3rem; animation: myMove 3s ease-in-out infinite; } @keyframes myMove { 0% { opacity: 1; } 100% { opacity: 0; } } // border-right: 1px solid #dfe5f9; } }

 

3.核心逻辑分步骤详解 import { ref, type Ref } from "vue"; import { ElMessage, ElMessageBox } from "element-plus"; import { useSheepStore } from "@/stores/sheep"; // 关卡数据 import data from "./data.json"; // 颜色 import constants from "./constants"; // pinia 控制关卡 const store = useSheepStore();

首先引入data.json数据是渲染中间的页面内容,即是:

中间的就叫卡片区域吧,卡片分为半个遮挡和整个遮挡,在data数据里面配置:

"full": true

默认是半个遮挡,配置了"full": true就表示这块的卡片是全遮挡的效果:

:style="!onei.full ? `--i:${onekSub}` : `--i:0`"

:class="onei.full && onei.oneSub.length > 1 ? 'true' : ''" :key="'i' + onekSub"

css: 使用了var的变量形式,来控制是否需要top下移,&.true来控制是否有下一级的卡片的样式

&:nth-child(1n) { top: calc(var(--i) * 1.5rem); } &.true {         box-shadow: 0 -55px 0 0 #dfe5f9 inset; }

data.json里面的数据oneSub的选值范围是:0-10

这和dom渲染层的息息相关:卡片使用的是简单的icon也可以是其他类型的元素,你觉得好看即可。

这里只提供11中卡片的效果,可以扩展添加,需要修改代码。

接下来是:

// 七个槽位 // const footerList = ref([0, 1, 2, 3, 4, 5, 6]); const footerList: Ref = ref([]); const colors = ref(constants.colors); // 关卡响应式 const totalList: Ref = ref([]); totalList.value = data["list1"]; // 默认第一关 // 控制动画效果结束才能点击 const isNotClick = ref(false);

7个槽位在底部需要变化展示,做成响应式。totalList是动态变化的卡片数据集。totalList.value = data["list1"],默认第一关。爆炸的电话效果有延迟,需要控制在结束之后才能进行卡片的点击。

然后就是核心的卡片点击事件,需要做哪些逻辑控制呢?先看源代码,已经提前做了备注:

// 点击控制事件 function handleClick( i: number, k: number, onei: { oneSub: string | Array }, onek: number, oneiSub: Array, onekSub: number ) { console.log(i, k, onei, onek, oneiSub, onekSub, "测试"); if (isNotClick.value) { return false; } // 内层不能点击 if (onekSub !== onei.oneSub.length - 1) { return false; } // 前置点击如果槽位满了还没有消除完 fullFun() // 关卡的消除 let tempList = fixFun(k, onekSub, onek, oneiSub) // 消除动作 和 添加爆炸效果 if (footerList.value.length > 2) { isNotClick.value = true const { list, flag } = eliminationFunction(footerList.value) footerList.value = list; if (flag) { footerList.value = addBoomFunction(footerList.value); } setTimeout(() => { const { list, flag } = eliminationFunction(footerList.value) footerList.value = list; isNotClick.value = false }, 1000); // 进入下一关 nextFun(tempList) } // 挑战失败 failFun(tempList) console.log(footerList, tempList, "tempList"); }

首先是函数的签名,接受最上层级的i对象,k索引,然后是中层的onei对象,onek索引,最后是父级的oneiSub对象,onekSub索引。判断条件需要前置,判断能否点击isNotClick,内层不能点击

if (isNotClick.value) { return false; }

// 前置点击如果槽位满了还没有消除完

fullFun()函数判断如果槽位满了还没有消除完,就是挑战失败

function fullFun() { if (footerList.value.length === 7) { ElMessage.closeAll(); ElMessageBox.alert("挑战失败,点击确定返回!", "Warning", { confirmButtonText: "确定", type: "warning", showClose: false, }).then(() => { location.reload(); }); return false; } }

如何添加爆炸效果:

思路是在三个相同消除之后添加,添加在totalList数据之中,效果展示完成之后立即进行totalList数据重置操作。

// 关卡的消除 let tempList = fixFun(k, onekSub, onek, oneiSub) // 消除动作 和 添加爆炸效果 if (footerList.value.length > 2) { isNotClick.value = true const { list, flag } = eliminationFunction(footerList.value) footerList.value = list; if (flag) { footerList.value = addBoomFunction(footerList.value); } setTimeout(() => { const { list, flag } = eliminationFunction(footerList.value) footerList.value = list; isNotClick.value = false }, 1000); // 进入下一关 nextFun(tempList) }

css 添加的方法:

.boom-class { font-size: 3rem; animation: myMove 3s ease-in-out infinite; } @keyframes myMove { 0% { opacity: 1; } 100% { opacity: 0; } }

消除函数eliminationFunction逻辑的控制,flag用来进行是否成功消除:

// 消除函数 function eliminationFunction(list: any[]) { let flag: boolean = false; for (let k = 0; k < list.length - 2; k++) { const temp = list; const arr = temp.slice(k, k + 3); console.log(k, arr); if (arr[0] === arr[1] && arr[1] === arr[2] && arr[0] === arr[2]) { list.splice(k + 2); list.splice(k + 1); list.splice(k, 1); flag = true break; } } return { list, flag }; }

添加addBoomFunction爆炸函数:

// 实现爆炸效果 function addBoomFunction(list: any[]) { const temp = JSON.parse(JSON.stringify([...list, ...['boom', 'boom', 'boom']])) return temp; }

挑战失败如何判断呢?

//fail function failFun(tempList: any[]) { setTimeout(() => { if (footerList.value.length > 0 && !jugeList(tempList)) { ElMessage.closeAll(); ElMessageBox.alert("挑战失败,点击确定返回!", "Warning", { confirmButtonText: "确定", type: "warning", showClose: false, }).then(() => { location.reload(); }); return false; } }, 1002) }

jugeList函数是对目前存在的卡片集合进行长度判断,如何卡片不存在,但是槽位的数据不为空的情况下,说明没有消除完,就判断要重新开始挑战:

// 判断是否过关 function jugeList(list: any[]) { let temp: any = []; list?.forEach((oeni: { one: any }) => { oeni?.one?.forEach((sub: { oneSub: any }) => { temp = [...temp, ...sub.oneSub]; }); }); return temp.length; }

最后是挑战成功就可以进行下一关:

// next function nextFun(tempList: any[]) { setTimeout(() => { if (!footerList.value.length && !jugeList(tempList)) { // debugger ElMessage.closeAll(); ElMessage.success("恭喜您,挑战成功!进入下一关"); store.step++; const inStep: string = "list" + (store.step + 1); totalList.value = JSON.parse(JSON.stringify(data))[inStep]; footerList.value = []; } }, 1001) }

如何卡片不存在,但是槽位的数据为空的情况下,说明消除完了,就可以进入下一关进行挑战,难度也将升级!

 

4.总结

最近是由于玩了羊了个羊的小程序,有所感悟,思考了这个游戏的整体的玩法,如何去操作,然后想到了可以实现一个前端网页版本的羊了个羊,这里面有一些自己的设计思考是很重要的,花了一个星期左右来实现,中间遇到了如何消除,如何控制挑战失败,成功的问题,并且一一解决了,可以想到如果前端来做这个游戏怎么在最优的方案上,书写可以扩展的dom,来适配很多不同的关卡的元素或者是我们需要什么样的数据结构,方便后续的关卡的升级。这里解决的方案是配合json,数据是数组嵌套类型,元素是需要循环来调用的,什么类型的卡片是需要提前有个范围的,这样是可扩展的。最后的操作,或者撤销,恢复等操作(这里没有实现)本质上也是对于数据的操作。终而言之:数据驱动页面,才是我们追求的。最后,各位同学一起多思考一下背后的实现,让我们用技术来创作更多有趣的事情吧~❤️

关于vue3版本网页小游戏的文章就介绍至此,更多相关vue3网页小游戏内容请搜索编程宝库以前的文章,希望以后支持编程宝库!

下一节:JavaScript设计模式手写示例讲解JS 编程技术

想分享的几种设计模式目前模式:工厂模式,单例模式,适配器模式,装饰者模式,建造者模式 建造者模式简介:建造者模式(builder pattern)比较简单,它属于创建型模式的一种。白话:4 ...



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有