预览点这里 ,代码在相应的 github 仓库中

(首次加载比较慢,以后可以看看怎么优化一下打包)

(一)准备工作

  • vue3 + vite + p5 + eslint

     // .eslintrc.cjsmodule.exports = {env: {browser: true,es2021: true,},extends: ["eslint:recommended", "plugin:vue/vue3-essential"],parserOptions: {ecmaVersion: "latest",sourceType: "module",},plugins: ["vue"],rules: {},
    };
    
  • // 数据结构
    cells = [{ x, y, avaliable, // 格子是否可用:初始化游戏时,用来判断此格子能不能放图片imgId
    }];
    
  • 图片是在 icones 下载的 svg,方便且图片体积小

  • 使用 p5 的 library
    click 时需要判断是否点到了某个格子。之前写五子棋时就有同样的需求,那时自己写了个方法实现的。这次想锻炼一下检索能力,就查了查有没有现成的包,果然找到了—— p5.collide2D
    但是说明里没讲怎样引用。在项目里直接 import 会报错:Uncaught ReferenceError: p5 is not defined
    只能去看 p5.collide2d.js 的源码了:

    p5.prototype._collideDebug = false;
    p5.prototype.collideRectRect = function (x, y, w, h, x2, y2, w2, h2) {...};
    p5.prototype.collideRectRectVector = function (p1, sz, p2, sz2) {...};
    

    这样的写法是不支持模块化引用的,只能自己改一改了。
    把代码下载到本地,然后在文件开头加上 import p5 from "p5";

(二) 画图

  • 预加载图片

    new p5(function (p) {// 在 p5.preload 中加载图片p.preload = function () {imageNames.forEach((name) => {const url = new URL(`./assets/svg/${name}.svg`, import.meta.url).href; // vite 中使用 `new URL` 获取图片地址imageMaps[name] = p.loadImage(url); // p5.loadImage 可以直接加载 svg 格式});};p.setup = function () {};p.draw = function () {};
    }
    
  • 初始化游戏
      const cells = []function newGame() {cells.length = 0;initCells();placeImges();}function initCells() {for (let i = 0; i < 8; i++) {for (let j = 0; j < 14; j++) {cells.push({ x: i, y: j, available: true , imgId: undefined });}}}function placeImges() {const cellsToPlace = cells.filter((cell) => cell.available); // avaliable 为 false 的格子,不放图片while (cellsToPlace.length > 0) {const [cell1] = cellsToPlace.splice(0, 1);const randomIndex = p5.prototype.random(0, cellsToPlace.length);const [cell2] = cellsToPlace.splice(randomIndex, 1);const image = p5.prototype.random(imageNames);cell1.imgId = image;cell2.imgId = image;}}
    

(三)关键算法:判断相连

判断两个格子是否能相连。如果能相连,还要返回相连路径

3.1 傻瓜算法

先写了个傻瓜算法,粗暴的遍历查找

  • y 从 min 到 max( cell3和 cell4 沿 y 轴向下移),判断 cell1 -> cell3 -> cell4 -> cell2 通不通
  • x 从 min 到 max( cell3和 cell4 沿 x 轴向右移),判断 cell1 -> cell3 -> cell4 -> cell2通不通

这样就简化为判断三条直线通不通

但有很多不必要的计算,找的也不是最短路径

3.2 优化算法

小伙伴提供了一个有趣的思路,步骤如下:

  1. check if 不拐弯
  2. check if 拐一个弯
  3. check if 转两个弯,从上走
  4. check if 转两个弯,从外走,找最短的

具体解释如下:

passableCells

passableCells:cell 沿着上下左右四个方向延伸,直到遇到障碍物为止

如下图:cell1 的 passableCells 是黄色格子,cell2 的 passableCells 是蓝色格子;

更近一步,passableCells 还可以分为圈上的圈外的
如下图: 指的是 cell1 和 cell2 围成的矩形,有三角标记的格子就是“圈上的”

对于圈外的 passableCells,要知道它的 方向到圈的距离
如下图,三角标记的格子到 的距离都是1

step1: check if 不拐弯
    // check if 不拐弯const isDirectConnecting = checkIsDirectConnectable(cell1, cell2);if (isDirectConnecting) {return [cell1, cell2];    // 联通路径}
step2:check if 拐一个弯

等价于判断 passableCells_cell1_innerRect(cell1的延伸格子,内圈上的) 和 passableCells_cell2_innerRect 是否有交集

如下图:无交集,说明不可能只拐一个弯就连上

有交集(星星格子),说明能连上。联通路径是 celll1->交集点->cell2

    // check if 拐一个弯const [intersectionCell] = getIntersection(passableCells_cell1_innerRect ,passableCells_cell2_innerRect);if (intersectionCell) {return [cell1, intersectionCell, cell2];    // 联通路径}
step3:check if 转两个弯 在圈上走

等价于判断:passableCells_cell1_innerRect 中的格子 和 passableCells_cell2_innerRect 的格子,是否有能直接连接的

如下图:找到能直接连接的了:

没有能直接连接的:

    // check if 转两个弯 从圈上走for (let i = 0; i <= passableCells_cell1_innerRect.length - 1; i++) {const cell3 = passableCells_cell1_innerRect[i];for (let j = 0; j <= passableCells_cell2_innerRect.length - 1; j++) {const cell4 = passableCells_cell2_innerRect[j];if (checkIsDirectConnectable(cell3, cell4)) {return [cell1, cell3, cell4, cell2];   // 联通路径}}}
step4:check if check if 转两个弯 从 外走,找最短路径

等价于判断:passableCells_cell1_outerRect中的格子 和 passableCells_cell2_outerRect 的格子,是否有能直接连接的

    // check if check if 转两个弯 从圈外走,找最短路径// 找最短路径let distance = 1;let leftCount = passableCells_cell1_outerRect.length;while (leftCount > 0) {const cell5s = passableCells_cell1_outerRect.filter((o) => o.toInnerRectDistance === distance);for (let i = 0; i <= cell5s.length - 1; i++) {const cell5 = cell5s[i];const cell6s = passableCells_cell2_outerRect.filter((o) => o.dir === cell5.dir); // 能连上的肯定是“方向”相同的for (let j = 0; j <= cell6s.length - 1; j++) {const cell6 = cell6s[j];if (checkIsDirectConnectable(cell5, cell6)) {return [cell1, cell5, cell6, cell2];}}leftCount--;}distance++;}

再看一遍路径长度

step1 不拐弯 肯定是最近的

step2 拐一个弯 和 step3 转两个弯,在圈上走 ,路径长度是一样的,都是 x坐标差 + y坐标差

step4 转两个弯 从圈外走,路径长度是 x坐标差 + y坐标差 + 2 * toInnerRectDistance

(四)完善功能

图片自动引入

  const imageNames = [];const svgs = import.meta.glob("../assets/svg/*.svg");for (const path in svgs) {const matchRes = path.match(/\/svg\/(\w+)\.svg/);const imageName = matchRes[1];imageNames.push(imageName);}

提供多种初始牌型

// 修改 initCells 方法
function initCells() {const strategy = randomPick([function(i,j){ ...},function(i,j){ ...},]);for (let i = 0; i < 8; i++) {for (let j = 0; j < 14; j++) {cells.push({x: i,y: j,// avaliable: true,available: strategy(i, j), imgId: undefined,});}}}

消除时显示路径

点击 cell1 和 cell2 后,若能相连,则消除:

添加显示相连的路径:

上图中,路径出现时,两端的图片已经消失了,感觉怪怪的。

所以要让图片多显示一会儿:(要保证图片还显示,但不能“堵路”。不然会影响游玩手感)

  p.touchEnded = function () {...if (connectingLine) {// 消除的时候,添加 tempDisplayConnectingLines 和 tempDisplayEliminatedCellsaddTempDisplayItem({ type: "connectingLine", value: connectingLine, time: 800 });addTempDisplayItem({type: "eliminatedCells",value: [cloneDeep(activeCell), cloneDeep(targetCell)],time: 800,});activeCell.active = false;// imgId 必须立刻清空,不能“堵路”activeCell.imgId = undefined;targetCell.imgId = undefined;      }};
  const tempDisplay = ref([]);const tempDisplayConnectingLines = computed();const tempDisplayEliminatedCells = computed();function addTempDisplayItem(obj) {const id = uniqueId();tempDisplay.value.push({ id, type: obj.type, value: obj.value});// 添加进来的item,一段时间后会被清理掉setTimeout(() => {removeTempDisplayItem(id);}, obj.time);}function removeTempDisplayItem(id) {}

其他

新游戏、判断是否需要洗牌、洗牌、提示等,这些功能都没什么好说的

为了支持移动端,点击事件用 p5.touchEnded 替代 p5.click

部署

写完基本功能后截了个动图,爸妈看后说想玩一玩,所以我就发布在 github pages 上了。

(途中发现一个事情,vite build后的页面不能直接打开。因为打包后的代码还是模块化的,不能在 file:// 中打开)

github pages 可以设置 docs 为发布的目标文件夹

修改打包设置

export default defineConfig({// ...build: {outDir: "docs",
});

每次修改完代码之后,重新打包,然后再 commit push(新的打包 docs 文件夹一定要 push 上去)
github 就会自动 build 和 deploy 了,需要一定时间,进度可以看 repo 的 actions 页签:

p5.js 写个连连看相关推荐

  1. java连连看小程序,用JS写一个连连看小程序

    思路 决定内容区域的大小和图片种类数量 图片应该放多少行,多少列.必须是偶数 整个区域应该是在图片外多围上一圈,也就是行数和列数都应该比图片的多1 放多少对图片,一对两张.注意对数不能超过所有图片总数 ...

  2. 用p5.js编写简单的动态图形——波纹扩散

    用p5.js编写简单的动态图形--波纹扩散 第一次使用p5.js写程序,如有错误请指出,多多指教. 没有下载p5.js的小伙伴可以直接使用网页版的,简单注册一个账号之后就可以保存代码啦. 网站:htt ...

  3. 用p5.js制作烟花特效

    前言 之前看过一篇文章,使用processing制作烟花特效.效果如下 网上调查了一圈了,发现processing是一个互动编程软件,java语言发展而来.而且动画效果是跑在processing专门的 ...

  4. p5.js 开发点彩画派的绘画工具

    theme: smartblue 本文简介 点赞 + 关注 + 收藏 = 学会了 这几天在整理书柜时看到这套书,看到梵高,想起他的点彩画. 想到点彩画派,不得不提的一个画家叫乔治·皮埃尔·秀拉.据说梵 ...

  5. 使用processing编译器写p5.js代码

    1.打开processing编译器,点击右上角Jav的下拉小箭头 2.选择添加模式进入如下界面,在上方菜单栏选择Modes,在屏幕中选P5.js一项,点击右下角installed.3.安装完成后返回, ...

  6. 用Three.js写h5小游戏-3d飞机大战

    用Three.js写h5小游戏-飞机大战 博主的话 运行图片 目录路径![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829103702978.jpg?x- ...

  7. 原生js实现一个连连看小游戏(一)

    前几天使用原生的js写了一个连连看小游戏,地址:连连看(js),基本功能都实现了,运行截图为: 根据游戏规则获取开发思路 创建棋盘格 生成随机不重复数字 映射到棋盘格 鼠标点击事件 寻路,无通路,则到 ...

  8. P5.JS绘制动态图形

    P5.JS绘制动态图形 一.平台 第一次使用p5.js进行码绘,此次直接使用P5.JS官网的在线编辑器进行编写,完成后点击file->download即可保存到本地. 在正式绘制之前,我经过小小 ...

  9. p5.js 编程基础学习合集【2】

    之前在< p5.js 和 Processing 的恩怨情仇>曾提及 p5.js 与 Processing 的不同点之一就是: 在 JavaScript 中,变量没有类型.使用 var 而不 ...

最新文章

  1. 8月第2周中国五大顶级域名增4.1万 美国减6.8万
  2. python excel处理框架_django框架基于模板 生成 excel(xls) 文件操作示例
  3. fragment的基本使用
  4. PHP安装之configure的配置参数
  5. 【ArcGIS Pro微课1000例】0012:ArcGIS Pro属性表中文乱码完美解决办法汇总
  6. docker下gitlab安装配置使用(完整版)
  7. 工作127:子向父亲传值
  8. 解决:vue.esm.js?efeb:591 [Vue warn]: Do not use built-in or reserved HTML elements as component id: me
  9. 嵌入式C语言之struct内存分配分析
  10. ie浏览器怎么取消代理浏览器_微软和IE渐行渐远,IE浏览器终将成为回忆
  11. python基本数据类型第三周_python3第二天(基本数据类型)
  12. 大数据平台组件布置 与 进程查看
  13. python在同一行输入n个数转义符_python:转义符\
  14. SIGIR‘22 推荐系统论文之对比学习篇
  15. sql优化的几种思路
  16. 74ls20设计半加器_实验二++组合逻辑电路的设计与测试.ppt
  17. 医学影像常用Python包
  18. 阿里云的PolarDB要开源了?这个数据库到底强在哪?
  19. 黄哥python培训骗局
  20. linux怎么查看是不是centos版本

热门文章

  1. 员工满意度模型定期检讨工作规范有哪些?
  2. 申请msn邮箱小方法
  3. 自媒体时代网红电子商务
  4. 一行代码解决网站防挂IFRAME木马方案,小鸽子序列(灵儿)
  5. 金钩钓鱼java代码_金钩钓鱼
  6. windows删除不了文件解决方法(亲测有效)
  7. 基于C++仿真的MIPS32指令系统虚拟计算机设计与实现
  8. hdu 1074 状压dp
  9. 网狐、6878子游戏下载失败
  10. 寒假刷题实录-基础语法-02判断语句