跳到教程内容
教程首页
App Inventor 2 教程 扫码寻宝
准备开始
动手创造 · PROJECT LAB

扫码寻宝

准备探索你的新作品
0%
★★ 项目难度每一步,都离作品更近

先了解你将做出什么

游戏介绍

在家里、教室或者小区里藏几张二维码,App 给你一条谜语线索,你猜出地点、跑过去、扫码验证—— 扫对了才解锁下一关。这是一个要满屋子跑的寻宝游戏,藏宝图就在你手机里。

生日会、班级活动、家里带小朋友玩,都能直接用上。你还可以改线索,做成给某个人的专属寻宝路线。

教程主图

界面示意图

技术上它只做三件事:读二维码、比对暗号、记住你闯到第几关。但组合起来就是一个能玩很久的游戏。

扫码寻宝教程(难度系数:★★)

准备工作:做几张二维码

这一步在电脑上做,不用写代码。

二维码里存的不是网址,就是一个词——我们叫它”暗号”。比如藏在钟表旁边的那张, 里面就存着 钟表 两个字。

去任意一个免费的二维码生成网站(搜「二维码生成」就有),选”文本”类型, 输入暗号,生成、下载、打印出来。这一关的暗号是什么,就写什么。

本教程默认用这四个暗号,对应四个藏宝点:

关卡 暗号(二维码里的内容) 贴在哪里
1 钟表 挂钟或闹钟旁边
2 书架 书架上
3 洗手台 洗手池的镜子上
4 冰箱 冰箱门上

能,而且建议写复杂点

如果暗号就是 冰箱 两个字,聪明的小朋友随便找个二维码生成器自己做一张就”通关”了。

想防作弊,暗号可以写成 bx-7h2k 这种没人猜得到的乱码——反正玩家看到的是线索, 不是暗号,暗号长什么样根本不重要。

App基本逻辑设计

  1. App 里存两个一一对应的列表:一个放线索(给玩家看的谜语),一个放暗号(用来对答案)。
  2. 玩家看线索 → 找到地点 → 点”扫一扫” → 扫码 → 和当前关卡的暗号比对。
  3. 对了就进入下一关,并且把进度存起来;错了提示再找找。
  4. 下次打开 App,从存的进度接着玩,不用重头来。

两个并排的列表

这是本篇最值得学的一个技巧:用两个列表存同一批东西的不同侧面,靠下标对应起来。

global 线索 = []
global 暗号 = []
global 关卡 = 1

线索 的第 1 项,配的就是 暗号 的第 1 项。只要 关卡 是 1,两边取的都是第 1 项,永远对得上。

这个套路在很多地方都能用:一个列表存题目、一个存答案;一个存商品名、一个存价格。

也可以——App Inventor 里可以做”列表的列表”,每一项是 ["线索", "暗号"] 这样一小对。

两种都对。并排列表的好处是取值简单select(线索, 关卡) 一眼就懂), 缺点是加一关要同时改两个地方,容易漏。

数据一多、每项的字段一多(比如再加个”提示图片”“得分”),就该换成列表的列表或者字典了。 现在只有两个字段,用并排列表最省事。

显示当前关卡的线索

把”显示”这件事抽成过程,因为进入下一关、重新开始、App 启动,三个地方都要用。

procedure 显示线索() {
  if 关卡 > length(线索) {
    标签_关卡.Text = "全部通关!"
    标签_线索.Text = "恭喜你找到了所有宝藏!"
  } else {
    标签_关卡.Text = join("第 ", 关卡, " 关 / 共 ", length(线索), " 关")
    标签_线索.Text = select(线索, 关卡)
  }
}

注意开头那个 if 关卡 > length(线索)——这是防止程序崩溃的关键

最后一关通过后 关卡 会变成 5,而列表只有 4 项,这时候再去 select(线索, 5) 就会报错。 凡是拿变量当下标去取列表项,都要先想一想:这个下标会不会超出范围?

App 启动时读取进度

when Screen1.Initialize() {
  线索 = ["我会滴答滴答,抬头就能看见我", "我住在装满书的格子里", "你每天早晚都来找我刷牙", "我很冷,肚子里装满了吃的"]
  暗号 = ["钟表", "书架", "洗手台", "冰箱"]
  关卡 = 微数据库_进度.GetValue("关卡", 1)
  显示线索()
}

微数据库_进度.获取值("关卡", 1) 里的第二个参数 1默认值: 第一次打开 App,微数据库里还什么都没有,就用 1(从第一关开始)。

有默认值这件事很重要。 如果不给默认值,第一次打开时读到的是空字符串, 后面拿它当下标就全乱了。

扫码并比对

when 按钮_扫码.Click() {
  条码扫描器1.DoScan()
}

when 条码扫描器1.AfterScan(result) {
  if 关卡 > length(暗号) {
    标签_线索.Text = "已经通关啦,点「重新开始」再玩一次。"
  } else if result = select(暗号, 关卡) {
    关卡 = 关卡 + 1
    微数据库_进度.StoreValue("关卡", 关卡)
    文本朗读器1.Speak("答对了")
    显示线索()
  } else {
    标签_线索.Text = join("扫到的是「", result, "」,不是这一关的宝藏,再找找~")
  }
}

扫码是两步的:点按钮只是 执行扫描,把摄像头叫起来; 真正扫到东西是稍后 扫描结束时 这个事件被触发,扫到的内容在参数 result 里。

这种”我发起请求 → 过一会儿结果才回来”的写法,在网络请求、拍照、选图片时到处都是, 是个必须习惯的思维方式

答对之后马上 微数据库_进度.存储值("关卡", 关卡) 存进度。存早不存晚—— 万一玩家手机没电了,至少这一关的成果保住了。

重新开始

when 按钮_重来.Click() {
  关卡 = 1
  微数据库_进度.StoreValue("关卡", 1)
  显示线索()
}

变量和微数据库两个都要改。只改变量的话,下次打开 App 又跳回原来的关卡了。

开始测试

先在桌上把四张二维码摊开,一关一关试:

  1. 扫对的码,是不是进入下一关?
  2. 扫错的码(比如第 1 关去扫”冰箱”),是不是提示”再找找”,关卡没变?
  3. 扫个别的二维码(随便找个商品条码),会不会崩?应该只是提示不对。
  4. 关键一步:闯到第 2 关,把 App 完全退出再打开,是不是还在第 2 关?
  5. 四关全过之后,再扫一次码,会不会报错?(有那个 if 挡着,应该好好的)
  6. 点”重新开始”,退出重进,是不是真的回到第 1 关了?

第 4 步和第 6 步是这个 App 最容易出问题的地方,一定要试。

剩余工作

  • 加计时。记下开始时间,通关时算出总用时,比比谁找得快。
  • 加提示。卡住超过 2 分钟,出个按钮给更直白的提示(但要扣分)。
  • 拍照留念。每关找到后用照相机拍一张,通关时做成一个相册。
  • 自己编线索。加一个”出题模式”,让玩家自己输入线索和暗号, 存进微数据库,就能给朋友出一套专属的寻宝路线了。
  • 换成室外版。配合位置传感器,走到指定坐标附近才允许扫码,做成一场城市定向赛。

做完记得真的藏一次给家里人玩,比自己测试有意思多了ヾ(◍°∇°◍)ノ゙