☰
微信小程序看图找茬游戏开发:从开源包到上线完整指南
2026/10/27 22:42:02 网站建设 项目流程

简介:这是一套面向小程序开发者与休闲游戏创业者的「看图找茬找不同」完整源码,基于微信小程序前端技术栈实现,可直接用于二次开发或上线运营。包内共2007个文件,以1628个png与291个jpg关卡图片资源为主体,辅以js业务逻辑、json配置、wxml与wxss页面结构、php后端接口及sql建表脚本,压缩包约404MB,素材与代码分层清晰。功能上覆盖2510个关卡、7点能量与激励视频回复机制、金币与段位升级体系、闯关提示扣费、失败延时60秒、最多8人实时好友对战,并预留流量主横幅、激励视频与插屏广告位,后台填写广告id即可自动生效,另含分享裂变与邀请助力模块。目前已有86人学习下载,适合希望快速搭建找茬类小程序、研究关卡与对战逻辑或接入广告变现的开发者参考。

1. 看图找茬小游戏:从开源包到能跑起来的小程序,中间隔着什么

微信小程序里「看图找茬」这类玩法,用户点开就能玩,两幅图摆在那,找出五处不同点一下,进度条走满过关。它看起来简单,但真要从一个小程序游戏 看图找茬找不同开源版v2.0.0+前端+完整版.zip落地成能上线的项目,中间要处理的东西不少:图片资源怎么组织、点击判定怎么算准、关卡数据怎么配、计时和提示怎么联动、分享和排行榜怎么接。这个标题对应的就是一套前端完整版源码,适合两类人:一类是想快速搭一个休闲小游戏练手的小程序开发者,一类是手里有流量、想加个轻量互动模块的产品或运营同学。下面按「先看懂结构、再动手跑通、最后避坑」的顺序拆开讲,每一步都尽量给到能直接抄的配置和命令。

2. 拆开压缩包先看什么:目录结构与运行前置条件

拿到一个前端完整版的小程序源码包,别急着点「编译」。先花十分钟把目录结构和依赖关系理清楚,后面能省掉大量「为什么点不动」的排查时间。看图找茬这类项目的代码组织通常比较规整,核心逻辑集中在少数几个文件里,资源占大头。

2.1 典型目录长什么样

解压后一般能看到这样的层级(不同作者命名略有差异,但职责划分大同小异):

看图找茬/ ├── app.js # 全局逻辑,登录态、全局数据 ├── app.json # 页面注册、窗口样式、tabBar ├── app.wxss # 全局样式 ├── project.config.json # 开发者工具项目配置 ├── pages/ │ ├── index/ # 首页/关卡选择 │ ├── game/ # 核心游戏页 │ └── result/ # 结算页 ├── components/ │ └── diff-mask/ # 差异点遮罩组件(可选) ├── utils/ │ ├── level.js # 关卡数据加载 │ └── judge.js # 点击判定逻辑 ├── images/ │ ├── level1_a.png # 关卡1 图A │ ├── level1_b.png # 关卡1 图B │ └── ... └── data/ └── levels.json # 差异点坐标配置

关键点在于data/levels.json和utils/judge.js这两个文件。前者决定每关有几处不同、坐标在哪;后者决定你点下去之后系统怎么判断「点中了」。很多人跑不起来,问题往往出在这两处对不上。

2.2 运行前置条件与导入步骤

微信小程序开发需要先装好开发者工具,并且有一个可用的 AppID(测试号也行)。导入项目的操作不复杂,但有几个参数容易填错:

配置项填什么说明
目录解压后的根目录选到含 app.json 的那一层
AppID自己的测试号或正式号点「测试号」可免注册
项目名称随意仅本地显示
后端服务不使用云服务纯前端项目选这个

导入后如果编译报错,先看控制台第一条红字。常见的是app.json里注册了某个页面但pages目录下没有对应文件夹,补上空的.js/.json/.wxml/.wxss四件套即可。

提示:纯前端版本不依赖服务器,但如果你要接排行榜或登录,需要额外配置合法域名,这一步在开发阶段可以勾选「不校验合法域名」绕过。

2.3 先跑通再改:最小验证路径

不要一上来就改代码。先按默认配置编译一次,点进游戏页,随便点几下,确认三件事:图片能正常显示、点击有反馈、进度能推进。这三件事都正常,说明基础链路是通的,后面改关卡数据、换图片才有意义。如果图片显示空白,八成是路径问题——小程序里图片路径区分大小写,Images/和images/是两个东西。

3. 差异点判定怎么做:坐标配置与点击命中逻辑

看图找茬的核心不是「显示两张图」,而是「点对了算数」。这一章把判定逻辑讲透,包括坐标怎么存、点击怎么换算、容差怎么设。搞懂这块,你才能自己加关卡、调难度。

3.1 差异点坐标的存储格式

最常见的做法是在levels.json里为每关存一个差异点数组,每个点记录相对坐标和半径:

{ "level1": { "imageA": "/images/level1_a.png", "imageB": "/images/level1_b.png", "total": 5, "points": [ { "x": 0.23, "y": 0.41, "r": 0.05 }, { "x": 0.67, "y": 0.18, "r": 0.05 }, { "x": 0.45, "y": 0.72, "r": 0.06 }, { "x": 0.81, "y": 0.55, "r": 0.05 }, { "x": 0.12, "y": 0.83, "r": 0.05 } ] } }

这里x、y用的是 0 到 1 的相对比例,不是像素值。为什么?因为不同手机屏幕宽度不一样,用相对坐标才能保证同一张图在 iPhone 和安卓上判定位置一致。r是命中半径,同样是相对值,一般取 0.04 到 0.07 之间。

3.2 点击坐标换算与命中判断

用户点击时拿到的是屏幕像素坐标,需要先减去图片容器的偏移,再除以容器宽高,换算成相对坐标,然后和每个差异点比距离:

// utils/judge.js function checkHit(tapX, tapY, containerRect, points, foundSet) { // 换算成相对坐标 const relX = (tapX - containerRect.left) / containerRect.width; const relY = (tapY - containerRect.top) / containerRect.height; for (let i = 0; i < points.length; i++) { if (foundSet.has(i)) continue; // 已找到的跳过 const p = points[i]; const dist = Math.sqrt( Math.pow(relX - p.x, 2) + Math.pow(relY - p.y, 2) ); if (dist <= p.r) { return i; // 返回命中的差异点索引 } } return -1; // 没点中 }

逻辑说明:containerRect是图片容器的位置和尺寸,通过wx.createSelectorQuery()获取。foundSet记录已经找到的点,避免重复计数。距离公式就是两点间直线距离,落在半径内就算命中。

参数说明:r设太小,用户点不准会骂人;设太大,点空白也能中,失去挑战性。经验值是图片宽度的 5% 左右。如果图片本身差异区域很大(比如一整块颜色不同),r可以适当放大到 0.08。

3.3 命中反馈与进度联动

命中之后要有即时反馈,否则用户不知道自己点对了没有。常见做法是在命中位置画一个圆圈动画,同时更新进度:

// pages/game/game.js 片段 onTapImage(e) { const { x, y } = e.detail; const idx = checkHit(x, y, this.data.rect, this.data.points, this.data.found); if (idx === -1) { // 点错,扣时间或抖动提示 this.shakeScreen(); return; } const found = new Set(this.data.found); found.add(idx); this.setData({ found, progress: found.size / this.data.total }); if (found.size === this.data.total) { this.onLevelPass(); } }

e.detail里的坐标来自bindtap事件,注意要用e.detail.x而不是e.touches[0].pageX,前者是相对页面的,后者在某些机型上会有偏差。进度用found.size / total算,直接绑定到进度条宽度即可。

注意:setData里不要放整个 points 数组,只传变化的部分。小程序setData是性能瓶颈,传大对象会导致点击卡顿,这是血泪经验。

4. 关卡数据与图片资源:怎么加自己的图和差异点

跑通默认关卡之后,下一步就是换成自己的图。这一步的难点不在代码,而在「怎么标出差异点坐标」。手工一个个试太慢,得有个顺手的流程。

4.1 图片规格与命名约定

先定规矩,后面批量处理才不乱。建议:

  • 两张图尺寸必须完全一致,否则叠加显示会错位;
  • 宽度建议 750px(小程序常用设计宽度),高度按原图比例;
  • 命名用level{编号}_a.png和level{编号}_b.png,编号从 1 开始;
  • 格式优先 PNG,JPG 在差异边缘会有压缩噪点,影响观感。

如果原图太大,先用工具压到 750px 宽再放进images/目录。小程序对包体积有 2MB 主包限制,图片多了要考虑分包或放 CDN。

4.2 用脚本批量生成差异点坐标

手工量坐标效率太低。我一般会写一个本地小脚本,把两张图做像素级对比,自动找出差异区域,再输出成 JSON:

# tools/diff_points.py from PIL import Image, ImageChops import json def find_diff_points(path_a, path_b, threshold=30, min_area=200): img_a = Image.open(path_a).convert('RGB') img_b = Image.open(path_b).convert('RGB') diff = ImageChops.difference(img_a, img_b) gray = diff.convert('L') w, h = gray.size pixels = gray.load() # 简单连通区域标记(示意,生产可用 opencv) visited = [[False]*w for _ in range(h)] points = [] for y in range(h): for x in range(w): if visited[y][x] or pixels[x, y] < threshold: continue # BFS 找连通块 stack = [(x, y)] visited[y][x] = True xs, ys, count = [], [], 0 while stack: cx, cy = stack.pop() xs.append(cx); ys.append(cy); count += 1 for dx, dy in ((1,0),(-1,0),(0,1),(0,-1)): nx, ny = cx+dx, cy+dy if 0<=nx<w and 0<=ny<h and not visited[ny][nx] and pixels[nx,ny]>=threshold: visited[ny][nx] = True stack.append((nx, ny)) if count >= min_area: cx = sum(xs)/len(xs)/w cy = sum(ys)/len(ys)/h r = max(max(xs)-min(xs), max(ys)-min(ys))/2/w points.append({"x": round(cx,3), "y": round(cy,3), "r": round(max(r,0.04),3)}) return points if __name__ == '__main__': pts = find_diff_points('images/level1_a.png', 'images/level1_b.png') print(json.dumps(pts, ensure_ascii=False, indent=2))

逻辑说明:ImageChops.difference得到两张图的差异图,转灰度后用阈值过滤掉轻微噪点,再对剩下的亮区做连通块分析,每个块算一个中心点和半径。threshold控制灵敏度,min_area过滤掉太小的噪点块。

参数说明:threshold设 30 左右比较合适,太低会把压缩噪点也算进去,太高会漏掉颜色接近的差异。min_area根据图片尺寸调,750px 宽的图取 200 到 500 之间。跑出来的坐标直接粘进levels.json即可,个别不准的手动微调。

4.3 关卡难度曲线怎么排

关卡不是越多越好,难度要递进。前几关差异明显、数量少(3 处),中间关增加到 5 处、差异变小,后面可以加时间限制或干扰项。levels.json里可以加一个timeLimit字段,游戏页读取后启动倒计时。没有这个字段就默认不限时。难度曲线排得好,用户才有「再来一关」的动力,这是留存的关键。

5. 避坑与排查:那些让游戏「点不动」的常见问题

这一章全是踩过的坑。看图找茬这类项目,代码量不大,但小问题特别多,而且现象往往很迷惑。下面按「现象 → 原因 → 解决」列几条高频的。

5.1 点击没反应,控制台也不报错

现象:图片正常显示,但怎么点都没反馈,进度条纹丝不动。

原因:多半是bindtap绑在了外层容器上,而图片用了position: absolute盖在上面,事件被图片拦截了但图片没绑事件。或者containerRect获取时返回了null,导致坐标换算全错。

解决:把bindtap直接绑在图片元素上,或者给图片加pointer-events: none。获取containerRect时用wx.createSelectorQuery().select('#imgBox').boundingClientRect(),并在回调里判断rect是否存在再往下走。

5.2 命中位置偏移,点左边中右边

现象:明明点在差异点上,系统却判定没中,或者点空白反而中了。

原因:坐标换算时用的容器尺寸和实际显示尺寸不一致。常见于图片设置了mode="aspectFit",图片实际显示区域比容器小,但换算时用了容器尺寸。

解决:要么把图片mode改成scaleToFill让图片铺满容器,要么在换算时按图片实际显示比例修正。最稳的做法是让容器尺寸和图片显示尺寸严格一致,用aspectFit时通过图片原始宽高比算出实际显示区域再换算。

5.3 图片加载慢,进关卡白屏好几秒

现象:点进游戏页,图片半天不出来,用户以为卡死了。

原因:图片没压缩,单张好几 MB;或者图片放在主包里,包体积过大导致加载慢。

解决:所有图片压到 750px 宽、PNG 量化到 8 位色,单张控制在 200KB 以内。图片多的话放 CDN,用wx.getImageInfo预加载下一关的图。进关卡前加个 loading 态,别让用户对着白屏。

5.4 安卓上点得中,iOS 上点不中

现象:同一关卡,安卓手机正常,iPhone 上命中率明显偏低。

原因:iOS 的e.detail.x和安卓在部分机型上取值基准不同,有的相对页面,有的相对元素。另外 iOS 的点击热区比安卓小。

解决:统一用e.touches[0].clientX减去containerRect.left来算,不依赖e.detail。同时把命中半径r适当放大 10% 到 20%,兼顾 iOS 的热区差异。

5.5 进度条走满但不结算

现象:五处都点中了,进度条满了,但没跳结算页。

原因:found.size === total的判断用了==但类型不一致,或者total从 JSON 读出来是字符串"5"而不是数字5。

解决:读 JSON 后统一Number()转换,判断用===。结算逻辑放在setData的回调里执行,确保数据更新完成后再跳转。

6. 进阶技巧:用「提示」和「分享」把留存拉起来

基础玩法跑通之后,真正决定这个小游戏能不能留住人的,是提示机制和分享路径。这两块做得好,用户卡关时不会直接退出,通关后也愿意拉人进来。

6.1 提示功能:消耗式设计比无限提示更耐玩

无限提示会让游戏失去挑战,但完全不给提示又容易劝退。我一般做成「每关 3 次提示,点一次高亮一个未找到的差异点 2 秒」。实现上就是在data里存一个hintLeft,点击提示时找found里没有的第一个点,给它加个highlight标记,2 秒后清除:

onHint() { if (this.data.hintLeft <= 0) return; const { points, found } = this.data; const idx = points.findIndex((_, i) => !found.has(i)); if (idx === -1) return; this.setData({ hintLeft: this.data.hintLeft - 1, highlightIdx: idx }); setTimeout(() => this.setData({ highlightIdx: -1 }), 2000); }

highlightIdx绑定到 WXML 里对应差异点的样式,加个闪烁动画即可。提示次数可以做成看广告或分享获得,但纯前端版本先做本地计数就行。

6.2 分享与排行榜的轻量接法

小程序分享用onShareAppMessage,返回title和path即可。看图找茬的分享文案可以带关卡进度,比如「我玩到第 8 关卡住了,你来试试」。排行榜如果不想搭服务器,可以用微信云开发的数据库,几行代码就能存分数:

// 结算时写入云数据库 wx.cloud.database().collection('rank').add({ data: { score: this.data.score, level: this.data.level, time: Date.now() } });

云开发免去了自建服务器的麻烦,适合这种轻量小游戏。查询排行榜时按score降序取前 50 即可。注意云开发有免费额度,个人项目完全够用。

6.3 一个我常用的验证习惯

每次改完关卡数据或判定逻辑,我不会直接上真机,而是先在开发者工具的「自定义编译」里把屏幕尺寸切成 iPhone SE(小屏)和 iPhone 14 Pro Max(大屏)各跑一遍。小屏验证命中半径够不够大,大屏验证坐标换算有没有偏移。这个习惯帮我提前拦下了至少三次「安卓正常 iOS 偏移」的问题。看图找茬这类游戏,判定手感就是生命线,多花十分钟验证,比上线后被用户骂「点不准」强得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询