拼图游戏,开源代码

拼图游戏

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

请添加图片描述

体验地址
代码开源地址

心得体会

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

方块大小

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

请添加图片描述

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

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

方块显示

每个方块都是在原图的一小部分,很容易就想到了使用背景偏移显示的方式,不过方块还有个问题就是他有地方突出来点,有的地方凹进去点,这个就用到了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.id
  
  let styles = []
  let index = 0
  for (let i = 0; i < row; i++) {
    for (let j = 0; j < column; j++) {
      let classIndex = 0
      if (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 = styles

  window.onmousemove = handleMouseMove

  window.onmouseup = () => {
    console.log('onmouseup')

    if (currentIndex == -1) {
      return
    }

    let styles = itemStyles.value
    let x = styles[currentIndex].x
    let y = styles[currentIndex].y
    for(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 + yy
      if (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] = 1
      fixed[otherIndex] = 1

      for(let oIndex of currentRelative) {
        styles[oIndex].top = styles[otherIndex].top - (styles[otherIndex].x - styles[oIndex].x) * 100
        styles[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 = 0
    for(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.value
    let x = styles[currentIndex].x
    let y = styles[currentIndex].y
    let classIndex = styles[currentIndex].classIndex
    let offsetx = event.clientX - rect.left
    let offsety = event.clientY - rect.top
    styles[currentIndex].left = offsetx - styles[currentIndex].w / 2 + 30
    styles[currentIndex].top = offsety - styles[currentIndex].h / 2 + 30

    for(let otherIndex of currentRelative) {
      styles[otherIndex].left = (styles[otherIndex].y - y) * 100 + styles[currentIndex].left
      styles[otherIndex].top = (styles[otherIndex].x - x) * 100 + styles[currentIndex].top
    }

    itemStyles.value = styles
  }
}

const handleMouseDown = (index) => {
  console.log('handleMouseDown ' + index)
  currentIndex = index

  currentRelative = new Set()
  for(let s of relatives) {
    if (s.has(currentIndex)) {
      currentRelative = s
      break
    }
  }

  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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

技术探秘:开发抖音小程序实现餐饮团购的关键步骤

本文将深入研究在抖音小程序上开发餐饮团购功能的关键步骤&#xff0c;揭示技术探秘背后的秘密。 一、了解抖音小程序开发环境 注册成为抖音小程序开发者&#xff0c;配置开发工具&#xff0c;并确保您的开发环境设置正确&#xff0c;以便顺利进行后续的开发工作。 二、制定团…

在 Android 上简单安全地登录——使用凭证管理器和密钥

我踏马很高兴地听说&#xff0c; Credential Manager的公开版本将于 11 月 1 日开始提供。Credential Manager 为 Android 带来了身份验证的未来&#xff0c;简化了用户登录应用程序和网站的方式&#xff0c;同时使其更加安全。 登录可能具有挑战性 - 密码经常使用&#xff0c…

C#中数组、ArrayList与List对象的区别及使用场景

在C#编程中&#xff0c;数组、ArrayList和List对象是常用的数据结构和容器。它们在存储和管理数据方面都有各自的特点和用途。本文将深入探讨这三者的区别&#xff0c;并通过实际的代码示例来说明它们的使用场景和优缺点。 目录 1.数组特点使用场景 2.ArrayList特点使用场景 3.…

windows10上使用Visual Studio对树莓派进行交叉编译示例

本文主要介绍通过Visual Studio对树莓派进行交叉编译的方法。 1 环境 宿主机&#xff1a; 系统&#xff1a;Windows10 开发平台&#xff1a;Visual Studio 2022 (我用的是社区版) VisualGDB: VisualGDB - Download (我下的试用版本) GNU工具链: Prebuilt GNU toolchain f…

python matlplotlib/seaborn 绘制曲线的平均值标准差阴影图

1. seaborn 旧版本(0.8.1)中使用tsplot&#xff0c;新版本中使用lineplot 直线代表均值&#xff0c;阴影代表meanstd&#xff08;带有置信区间&#xff0c;参数ci&#xff09; import seaborn as sns import matplotlib.pyplot as plt import numpy as np import pandas as p…

647. 回文子串 516.最长回文子序列

647. 回文子串 题目&#xff1a; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&#xff0c;即使是由相…

负公差轧钢测径仪 多规格可定制 普通智能随意选择

负公差轧制的意义&#xff1a; 轧钢厂生产的螺纹钢是按理论重量销&#xff0c;因此稳定的高负差产品极具市场竞争力。负差率即实际重量与理论重量的差值&#xff0c;除以理论重量&#xff0c;乘100%。以螺纹12为例&#xff0c;不按负差生产&#xff0c;在坯重2450kg的情况下&am…

Windows装机必装软件|每款都好用到起飞!

1.GeekUninstaller&#xff1a;这款软件是一款功能强大的卸载工具&#xff0c;可以完全删除无用的程序和插件&#xff0c;并清理与之相关的文件夹。它没有广告或捆绑软件&#xff0c;非常干净。 2.PotPlayer&#xff1a;作为一款纯粹的播放器&#xff0c;PotPlayer可以流畅播放…

KOSMOS-G-图像文本结合控制生成

文章目录 摘要引言算法多模态语言建模图像解码器对齐微调instruction 实验结论 论文&#xff1a; 《Kosmos-G: Generating Images in Context with Multimodal Large Language Models》 github&#xff1a; https://github.com/microsoft/unilm/tree/master/kosmos-g 摘要 当…

喜报不断!箱讯平台获评2023年上海市促进现代航运服务业创新示范项目

近期&#xff0c;可谓捷报频传&#xff01;在箱讯科技子公司苏州箱讯获评苏州市软件和信息服务业 “头雁”培育企业没过多久&#xff0c;就又迎来好消息&#xff01; 日前&#xff0c;上海市交通委发布“2023年上海市促进现代航运服务业创新项目”评选结果&#xff0c;箱讯An…

桌面便签软件用哪个?10款全球好用的便签软件推荐,告别杂论无章!

在如今的快节奏社会中&#xff0c;我们的生活和工作节奏越来越快&#xff0c;每天面对的信息成倍地增长。有时候&#xff0c;我们需要随手记下一些重要的事情&#xff0c;或者是一些突然的灵感&#xff0c;这时候就需要一款好用的桌面便签软件。 桌面便签软件可以帮助我们更好…

Ladybug 全景相机, 360°球形成像,带来全方位的视觉体验

360无死角全景照片总能给人带来强烈的视觉震撼&#xff0c;有着大片的既视感。那怎么才能拍出360球形照片呢&#xff1f;它的拍摄原理是通过图片某个点位为中心将图片其他部位螺旋式、旋转式处理&#xff0c;从而达到沉浸式体验的效果。俗话说“工欲善其事&#xff0c;必先利其…

《深入浅出.NET框架设计与实现》阅读笔记(四)

静态文件系统 通过ASP.NET Core 提供的静态文件模块和静态文件中间件&#xff0c;可以轻松的让应用程序拥有访问静态文件的功能&#xff0c;同时可以基于IFileProvider对象来自定义文件系统&#xff0c;如基于Redis做扩展文件系统 启动静态文件服务 在Program.cs 类中&#x…

ERP是什么意思?看这一篇就够了!

如果你身在制造业&#xff0c;那么一定对ERP不陌生。天天把ERP挂在嘴边&#xff0c;但你真的了解什么是ERP吗&#xff1f;本篇文章将介绍以下几点&#xff1a;1.ERP是什么意思&#xff1b;2.ERP的功能&#xff1b;3.ERP的落地案例。 一、ERP是什么意思 ERP是企业资源计划&…

【Apache Doris】审计日志插件 | 快速体验

【Apache Doris】审计日志插件 | 快速体验 一、 环境信息1.1 硬件信息1.2 软件信息 二、 审计日志插件介绍三、 快速 体验3.1 AuditLoader 配置3.1.1 下载 Audit Loader 插件3.1.2 解压安装包3.1.3 修改 plugin.conf 3.2 创建库表3.3 初始化3.4 验证 一、 环境信息 1.1 硬件信…

【机器学习5】无监督学习聚类

相比于监督学习&#xff0c; 非监督学习的输入数据没有标签信息&#xff0c; 需要通过算法模型来挖掘数据内在的结构和模式。 非监督学习主要包含两大类学习方法&#xff1a; 数据聚类和特征变量关联。 1 K均值聚类及优化及改进模型 1.1 K-means 聚类是在事先并不知道任何样…

Vue项目的学习一

1、Vue项目里面的.js文件里面对象添加属性 例如&#xff1a;在对象&#xff1a;row&#xff0c;需要在对象row里面添加一个属性状态&#xff1a;type&#xff0c;使用里面的Vue.set函数 Vue.set(参数1,参数2,参数3) Vue.set(row,type,false)解析&#xff1a; 参数1&#xff1…

不应该被忽视的10个好用的PDF文档修改器

您在寻找最好的免费开源 PDF 编辑器吗&#xff1f;您是否正在寻找免费编辑 PDF 文档的解决方案&#xff1f;如果您正在寻找此类问题的答案。那么&#xff0c;亲爱的朋友&#xff0c;您来对地方了&#xff0c;因为今天&#xff0c;在本文中&#xff0c;我将讨论一些适用于 Windo…

能够导出源代码的低代码平台有哪些?

目录 一、源码的优势 &#xff08;1&#xff09;定制性需求&#xff1a; &#xff08;2&#xff09;适应未来需求变化&#xff1a; &#xff08;3&#xff09;安全和可靠性&#xff1a; &#xff08;4&#xff09;高级功能和集成&#xff1a; 二、支持源代码的厂商 目前国内大多…

数据结构—队列的实现

前言&#xff1a;上次我们已经学习了数据结构中一个重要的线性表—栈&#xff0c;那么我们这一次就来学习另外一个重要的线性表—队列。 目录&#xff1a; 一、 队列的概念 二、 队列的实现&#xff1a; 1.队列的创建 三、 队列的操作 1.初始化队列 2.队尾入队列 3.队头出队列…