拼棋派对开发实战系列封面

看图找茬的页面可以把图片缩小、左右摆放或上下排列,但同一处差异必须始终点得中。若差异坐标写成某个屏幕的像素值,换一个窗口大小,圈出来的位置就会偏离实际物体。

《拼棋派对》把每处差异保存为图片中的百分比区域,点击时再把局部坐标转换到相同坐标系。本文结合风景主题的命中、重复点击和完成过程,分析显示尺寸、热区和结果反馈如何配合。环境为HarmonyOS7 API26模拟器,主应用1.0.19。

本文目录

差异区域跟随图片保存

主题数据保存original、changed两张图片与spots列表。每个spot包含x、y、size、mark与color。x和y是区域左上角百分比,size分别作为宽度与高度百分比,mark描述差异内容。

假设一个区域为x=20、y=30、size=10,它在横向覆盖图片20%—30%,纵向覆盖30%—40%。这里的“10”不是10像素,也不是独立于图片比例的正方形像素尺寸。

数据用途
original / changed对照原图与实际差异图
spots固定差异区域与说明
differenceFound每个区域是否已找到
differenceMistakes未命中的点击次数
differenceHintIndex当前提示位置
differenceFeedback正确、错误、重复、提示或答案状态

难度为4、5或6处,differenceTarget取难度与主题实际差异数量的较小值。难度筛选的是前若干区域,后面的差异可能仍在图片上,却不属于本局目标。内容制作时需要让提示与本局范围一致。

显示尺寸决定坐标换算

页面原图与找茬图都使用ImageFit.Fill,图片外层维持主题宽高比。找茬图上方叠加透明点击层,与图片同宽同高,通过onAreaChange记录交互区域实际尺寸。

.onAreaChange((oldArea, newArea) => {
  if (interactive) {
    this.updateDifferenceSceneSize(
      this.areaNumber(newArea.width),
      this.areaNumber(newArea.height));
  }
})

记录的是当前显示区域,不是原图文件分辨率。手机上下排布与宽屏左右排布改变尺寸后,换算继续使用最新width和height。缓存首次尺寸而不处理窗口变化,会让折叠或旋转后的热区偏移。

ImageFit.Fill在这个组合中让图片内容覆盖整个点击矩形。如果改成Contain并出现留白,必须先减去图片实际显示区域的偏移;如果改成Cover裁剪,又需要处理裁剪后的内容范围。改变objectFit不只是视觉调整,也会改变坐标映射前提。

从局部点击映射到百分比

点击事件提供相对于交互层的x和y,页面除以当前宽高再乘100。随后遍历本局spot,判断点是否位于该区域内。

const x = event.x / this.differenceSceneWidth * 100;
const y = event.y / this.differenceSceneHeight * 100;
const spots = this.currentSpotTheme().spots.slice(0, this.differenceTarget());
for (let index = 0; index < spots.length; index++) {
  const spot = spots[index];
  if (x >= spot.x && x <= spot.x + spot.size &&
      y >= spot.y && y <= spot.y + spot.size) {
    this.handleDifferenceTap(index);
    return;
  }
}
this.handleDifferenceTap(-1);

尺寸必须大于0,否则不能除法换算,当前会按未命中处理。多个区域重叠时,这个遍历返回第一个命中的区域,因此制作数据时应尽量避免重叠,或者明确重叠的优先策略。

显示标记和命中区域不是同样大小。项目用较小的圈标记spot中心,减少遮挡图片;命中则使用完整spot区域。不能看到圈比较小,就按圈的像素边界重写点击判断。

命中、误点与重复点击

handleDifferenceTap先检查是否结束,未开始时会启动。传入-1或不在本局范围的索引,误点数加一;已找到区域返回重复提示,不再次增加进度。

首次命中时克隆found数组,把对应项设为true,再重算找到的数量。它不直接无条件累加hits,所以重复点同一处不会凑够完成条件。

if (this.differenceFound[spotIndex]) {
  this.differenceFeedback = 'repeat';
  this.differenceLastSpot = spotIndex;
  return;
}
const next = this.differenceFound.slice();
next[spotIndex] = true;
this.differenceFound = next;
this.differenceHits = this.differenceFoundCount();

进度由found数组推导,圈标记也读取同一个数组。让画面与统计共享来源,能减少“已经圈住了,计数没增加”或“分数增加了,圈没出现”的部分更新。

提示和答案走不同结算路径

提示找到第一个尚未完成的区域,只设置differenceHintIndex并显示说明,不把found改成true。玩家仍需要点击才能计入进度。

“显示答案”则把目标区域全部标出,设置finished并停止计时,但明确提示本局不计入胜利,也不调用正常完成战绩路径。正常找齐最后一处才调用finishSpotDifference,保存主题、数量、用时与误点数。

这两条路径的界面都可能显示全部圈,但业务含义不同。只凭截图里圈满了,不能判定玩家已经完成一局。验收要结合提示、战绩处理和实际点击过程。

模拟器操作与适配检查

本次使用风景主题、标准5处,开始后在找茬图内点一个无差异位置,观察误点反馈。再依次点击目标区域中心;第一处找到后重复点击,检查进度保持1/5。最后找齐五处,出现完成状态。

看图找茬误点、命中、重复点击,找齐五处完成

操作过程中图片可能从设置页进入聚焦布局,点击位置因此每次按当前图片边界重新计算。演示验证的是已显示图片的局部坐标换算,不是给所有窗口复用固定屏幕坐标。

若再补一轮折叠和旋转验收,应在变化后点击同一个百分比中心,观察标记仍覆盖相同物体;同时检查边缘目标是否被容器裁掉、原图与找茬图是否保持相同比例。本文本轮使用手机单屏,未把它当作全部设备形态已验证。

内容方面,差异图必须实际包含对应变化,热区也需要包住那个物体。坐标正确不能弥补图片内容错误。增加主题时,应先逐处对照两张原始资源,再检查缩小后的可辨认性,最后验证命中与完成路径。

源码入口:common/GameConfig.ets 的 SPOT_THEMES;pages/Index.ets 的 DifferenceImagePanel、handleDifferenceSceneTap、handleDifferenceTap、showDifferenceHint 与 revealDifferences。参考HarmonyOS官方文档。下一批从寻物和文字玩法继续扩展状态模型。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐