拼图游戏,开源代码

拼图游戏

最近玩了玩孩子的拼图游戏,感觉还挺好玩的,心血来潮要不动手做一个吧,偷懒摸鱼的时候可以来一把。
以下就是拼图游戏的界面截图。

请添加图片描述

体验地址
代码开源地址

心得体会

虽说是一个小游戏,但是需要注意的地方还是挺多的

方块大小

譬如说这个方块的显示就比较麻烦,也是经历过几个版本的迭代,比较直观的想法可能是这个方块有九种,分为好几个尺寸,开始也是这么弄的。不过各种鼠标拖动之类的计算就比较麻烦了。后来的拼接也比较费劲,各种计算位置是否合适。

请添加图片描述

后来又更新了一个版本把所有的方块都弄成同样大小了,这样后续的计算就会简单很多,代码看起来也简洁了。

这样也方便后续如果说想要支持不同尺寸的方块,只需要改改相应的大小及偏移就能完全搞定了。

方块显示

每个方块都是在原图的一小部分,很容易就想到了使用背景偏移显示的方式,不过方块还有个问题就是他有地方突出来点,有的地方凹进去点,这个就用到了css的蒙版图片功能,也就做了上边的那种蒙版图片。

拖动拼接

由于方块目前设计的是5x8的方块进行游戏,如果太多了话其实是有一个缩放的问题,目前没有考虑,毕竟这样操作起来感觉还是挺麻烦的。实现来说就是改改相应的大小,倒是没有那么难弄。后续考虑给加上。

方块的拼接能够使用选中的一个图片进行拼接,也可能是已经拼好的部分进行拼接,也都是设计的取舍,当前游戏来说感觉还是以选中拖动移动的为主,只拼接这个方块周围的方块,更加专注一下,避免大力出奇迹的随便就完成游戏了。

代码

整体代码量也不多,也涉及到一些素材,可以从开源项目位置查看。
以下是部分代码,仅供参考。

<script setup>
import { onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import HeaderCompoent from '../../components/HeaderComponent.vue'const itemStyles = ref([])
var currentIndex = -1
var fixed = []
var relatives = []
var currentRelative = new Set()
const row = 5
const column = 8
const route = useRoute()
const picId = ref('0001')
const router = useRouter()const handleNaviBack = () => {router.back()
}onMounted(() => {picId.value = route.params.idlet styles = []let index = 0for (let i = 0; i < row; i++) {for (let j = 0; j < column; j++) {let classIndex = 0if (i == 0) {if (j == 0) {classIndex = 1} else if (j == (column - 1)) {classIndex = 3} else {classIndex = 2}} else if (i == (row - 1)) {if (j == 0) {classIndex = 7} else if (j == (column - 1)) {classIndex = 9} else {classIndex = 8}} else {if (j == 0) {classIndex = 4} else if (j == (column - 1)) {classIndex = 6} else {classIndex = 5}}styles.push({offsetx: -j * 100,offsety: -i * 100,classIndex: classIndex,// top: i * 100,// left: j * 100,top: (row-1) * 100 * (Math.random() * 1.2 - 0.1),left: (column-1) * 100 * (Math.random() * 1.2 - 0.1),x: i,y: j,w: 160,h: 160,index: index,})fixed.push(0)index += 1}}itemStyles.value = styleswindow.onmousemove = handleMouseMovewindow.onmouseup = () => {console.log('onmouseup')if (currentIndex == -1) {return}let styles = itemStyles.valuelet x = styles[currentIndex].xlet y = styles[currentIndex].yfor(let offset of [[0,1],[0,-1],[1,0],[-1,0]]) {// 判断一下是否越界let xx = x + offset[0]let yy = y + offset[1]if (xx < 0 || xx > (row-1) || yy < 0 || yy > (column-1)) {continue}let otherIndex = xx * column + yyif (otherIndex >= styles.length) {continue}if (currentRelative.has(otherIndex)) {continue}console.log('currentIndex ' + currentIndex)console.log('otherIndex ' + otherIndex)// 判断试一下是否很近if (Math.abs(styles[currentIndex].top - styles[otherIndex].top + (styles[otherIndex].x - x) * 100) > 20 || Math.abs(styles[currentIndex].left - styles[otherIndex].left + (styles[otherIndex].y - y) * 100) > 20) {continue}// 判断一下是否已经设置过fixed[currentIndex] = 1fixed[otherIndex] = 1for(let oIndex of currentRelative) {styles[oIndex].top = styles[otherIndex].top - (styles[otherIndex].x - styles[oIndex].x) * 100styles[oIndex].left = styles[otherIndex].left - (styles[otherIndex].y - styles[oIndex].y) * 100}if (!currentRelative.has(otherIndex)) {currentRelative.add(otherIndex)}if (relatives.indexOf(currentRelative) < 0) {relatives.push(currentRelative)}for(let sindex in relatives) {if (relatives[sindex] == currentRelative) {continue}if (relatives[sindex].has(otherIndex)) { // 如果说对上的元素在其他的分组里边for(let v of relatives[sindex]) {currentRelative.add(v)}relatives.splice(sindex, 1)}}console.log('fixed '+ currentIndex + ' ' + otherIndex)console.log('handleMouseUp - ' + Array.from(currentRelative), relatives)}let count = 0for(let i = 0; i < row * column; i++) {count += fixed[i]}if (count == row * column && relatives.length == 1) {setTimeout(() => {alert('恭喜完成拼图')}, 200);}currentIndex = -1}
})const handleMouseMove = (event) => {if (currentIndex > -1) {let ele = document.getElementById('grid')let rect = ele.getBoundingClientRect()let styles = itemStyles.valuelet x = styles[currentIndex].xlet y = styles[currentIndex].ylet classIndex = styles[currentIndex].classIndexlet offsetx = event.clientX - rect.leftlet offsety = event.clientY - rect.topstyles[currentIndex].left = offsetx - styles[currentIndex].w / 2 + 30styles[currentIndex].top = offsety - styles[currentIndex].h / 2 + 30for(let otherIndex of currentRelative) {styles[otherIndex].left = (styles[otherIndex].y - y) * 100 + styles[currentIndex].leftstyles[otherIndex].top = (styles[otherIndex].x - x) * 100 + styles[currentIndex].top}itemStyles.value = styles}
}const handleMouseDown = (index) => {console.log('handleMouseDown ' + index)currentIndex = indexcurrentRelative = new Set()for(let s of relatives) {if (s.has(currentIndex)) {currentRelative = sbreak}}if (currentRelative.size == 0) {currentRelative.add(currentIndex)}console.log('handleMouseDown - ' + Array.from(currentRelative), relatives)
}</script><template><div class="container"><div id="grid" class="grid"><img :src="`/images/pintu/${picId}.jpg`" alt="" class="backgroundImage"><div v-for="ss in itemStyles" :key="ss" class="imageContainer":style="{top: `${ss.top}px`,left: `${ss.left}px`,}"@mousedown="handleMouseDown(ss.index)"><div :class="['imageClass', `imageCover00${ss.classIndex}`]":style="{backgroundPosition: `${ss.offsetx}px ${ss.offsety}px`,backgroundImage: `url(/images/pintu/${picId}.jpg)`}"></div></div></div></div><HeaderCompoent></HeaderCompoent><div class="backBtn" @click="handleNaviBack">返回目录</div>
</template><style scoped>
.container {width: 100%;height: 100vh;display: flex;flex-direction: column;justify-content: center;align-items: center
}
.backBtn {position: fixed;top: 20px;right: 20px;
}
.grid {position: relative;width: 800px;height: 500px;
}
.backgroundImage {width: 100%;height: 100%;position: absolute;top: 0;left: 0;right: 0;bottom: 0;opacity: 0.2;
}
.imageContainer {position: absolute;width: 100px;height: 100px;
}
.imageClass {width: 160px;height: 160px;background-image: url('/images/pintu/0001.jpg');background-size: 800px 500px;overflow: visible;
}
.imageCover001 {mask-image: url('/images/pintu/cover/001.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 160px;-webkit-mask-image: url('/images/pintu/cover/001.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 160px;
}
.imageCover002 {mask-image: url('/images/pintu/cover/002.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 100%;-webkit-mask-image: url('/images/pintu/cover/002.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 100%;
}
.imageCover003 {mask-image: url('/images/pintu/cover/003.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 100%;-webkit-mask-image: url('/images/pintu/cover/003.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 100%;
}
.imageCover004 {mask-image: url('/images/pintu/cover/004.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 100%;-webkit-mask-image: url('/images/pintu/cover/004.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 100%;
}
.imageCover005 {mask-image: url('/images/pintu/cover/005.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 100%;-webkit-mask-image: url('/images/pintu/cover/005.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 100%;
}
.imageCover006 {mask-image: url('/images/pintu/cover/006.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 100%;-webkit-mask-image: url('/images/pintu/cover/006.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 100%;
}
.imageCover007 {mask-image: url('/images/pintu/cover/007.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 100%;-webkit-mask-image: url('/images/pintu/cover/007.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 100%;
}
.imageCover008 {mask-image: url('/images/pintu/cover/008.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 100%;-webkit-mask-image: url('/images/pintu/cover/008.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 100%;
}
.imageCover009 {mask-image: url('/images/pintu/cover/009.png');mask-repeat: no-repeat; mask-position: -30px -30px;mask-size: 100%;-webkit-mask-image: url('/images/pintu/cover/009.png');-webkit-mask-repeat: no-repeat; -webkit-mask-position: -30px -30px;-webkit-mask-size: 100%;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/171883.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器视觉目标检测 - opencv 深度学习 计算机竞赛

文章目录 0 前言2 目标检测概念3 目标分类、定位、检测示例4 传统目标检测5 两类目标检测算法5.1 相关研究5.1.1 选择性搜索5.1.2 OverFeat 5.2 基于区域提名的方法5.2.1 R-CNN5.2.2 SPP-net5.2.3 Fast R-CNN 5.3 端到端的方法YOLOSSD 6 人体检测结果7 最后 0 前言 &#x1f5…

Python---综合案例:通讯录管理系统---涉及点:列表、字典、死循环

需求&#xff1a; 开个一个通讯录的管理系统&#xff0c;主要用于实现存储班级中同学的信息&#xff08;姓名、年龄、电话&#xff09; 涉及点&#xff1a;列表、字典、死循环 相关链接&#xff1a;Python--列表及其应用场景---增、删、改、查。-CSDN博客 Python---字典---…

高防CDN:构筑网络安全的钢铁长城

在当今数字化的世界里&#xff0c;网络安全问题日益突显&#xff0c;而高防CDN&#xff08;高防御内容分发网络&#xff09;正如一座坚不可摧的钢铁长城&#xff0c;成为互联网安全的不可或缺之物。本文将深入剖析高防CDN在网络安全环境中的关键作用&#xff0c;探讨其如何构筑…

upload-labs关卡5(点和空格绕过)通关思路

文章目录 前言一、回顾上一关知识点二、靶场第五关通关思路1.看源代码2.点和空格绕过3、验证上传 总结 前言 此文章只用于学习和反思巩固文件上传漏洞知识&#xff0c;禁止用于做非法攻击。注意靶场是可以练习的平台&#xff0c;不能随意去尚未授权的网站做渗透测试&#xff0…

嵌入式行业算青春饭吗?

今日话题&#xff0c;嵌入式行业算青春饭吗&#xff1f;嵌入式行业的技术要求确实非常广泛&#xff0c;需要深厚的知识广度和深度。这意味着入行门槛较高&#xff0c;我们需要了解不仅是软件和硬件&#xff0c;还要熟悉底层接口和硬件信号的处理方式&#xff0c;了解数据在计算…

漏洞复现--用友U8-cloud RegisterServlet SQL注入

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

格式化或删除了存储卡的照片?值得收藏的几个有效方法

最好的恢复软件可以从 SD 卡、固态硬盘和硬盘恢复已删除的照片、视频和数据 您是否不小心重新格式化了存储卡或删除了想要保留的照片&#xff1f;最好的照片恢复软件可以提供帮助&#xff01;如果您使用数码相机拍摄的时间足够长&#xff0c;当您错误地删除了您想要保留的图像…

远勤山丨于无声处听惊雷——写在远航汽车投产下线之际

“只要未来有前途的产业必然竞争激烈。新能源汽车目前还有很多新进入者&#xff0c;证明还有很大的成长空间。对于远航汽车&#xff0c;我们非常有信心坐上最后的牌桌。” 2023年11月8日&#xff0c;在山西运城大运新能源生产基地举办的“远航汽车下线仪式”上&#xff0c;大运…

软件测试自学指南,十年阿里测试工程师的建议

通过技能提升&#xff0c;入行IT可以的&#xff0c;但得先积累足够的经验&#xff0c;才能拿高薪&#xff0c;有个成长的过程。 软件测试岗介绍 软件测试岗位主要负责系统的测试工作&#xff0c;属于IT项目中的质量管理&#xff08;QA&#xff09;模块。 这个岗位分为两种类…

R程序 示例4.3.2版本包 在centos进行编译部署

为了在CentOS上下载和编译R语言4.3.2包&#xff0c;可以按照以下步骤进行操作&#xff1a; 1.首先&#xff0c;需要安装一些必要的依赖项。可以使用以下命令安装它们&#xff1a; sudo yum install -y epel-release sudo yum install -y gcc gcc-c gcc-gfortran readline-dev…

C#使用时序数据库 InfluxDB

一、安装 https://docs.influxdata.com/influxdb/v2/install/?tWindows 解压后使用cmd运行 访问 localhost:8086 配置 第一次登入会初始化 配置登入账号 保存TOKEN 这个TOKEN用于后期代码链接访问数据库&#xff0c;忘记了只能删除重新生成 点击QUCK START进入管理页面 …

华为笔记本电脑原装win10/win11系统恢复安装教程方法

华为电脑matebook 14原装Win11系统带F10智能还原 安装恢复教程&#xff1a; 1.安装方法有两种&#xff0c;一种是用PE安装&#xff0c;一种是华为工厂包安装&#xff08;安装完成自带F10智能还原&#xff09; 若没有原装系统文件&#xff0c;请在这里获取&#xff1a;https:…