本文作者赵杭天。他参加了“2022 RTE 编程挑战赛”——“赛道二 场景化白板插件应用开发” , 并凭借作品“成语解谜”获得了该赛道大奖。“成语解谜”是一个基于互动白板 SDK 的互动小游戏应用。通过前端编码、调用白板 API 能力、定制化后端逻辑等,实现了一个老少咸宜、寓教于乐的成语解谜游戏。其中的流程、步骤与相关的技术栈在白板互动应用开发上具有一定的通用性。本文将分享该项目的开发过程,包括一些关键功能的实现,希望与各位同学一起交流,共同进步。大家可以访问

零几年刚上小学的时候,第一次接触到电脑和教育软件,里面有一些小游戏,真的会被引导去学习到一些东西,比如一些名词概念、科学常识,对小孩子挺有帮助。

“白板”两个字,给我的第一感觉是回到了校园。在学校里都能遇到很好的同学和老师,有很多美好的回忆。小时候喜欢读成语字典,就像看故事书,然后在教室里也会玩一些类似成语解谜这样的字谜游戏。

20 年疫情在家会玩一些益智休闲游戏,能玩到自己做的游戏,感觉很开心。另外,这类游戏很适合碎片化的时间,并且能让用户学习到一些东西。尤其适合小朋友和喜欢休闲游戏的大朋友;对于长辈,操作上比较友好,内容也容易引起共鸣。从市场和社会上看,都是有价值的。

互动白板的正式名称叫声网 Flat(点击文末“阅读原文”,了解更多),官方的解释是:“个人老师可直接使用的在线授课软件,开箱即用,前后端完全开源,快速搭建简约美观的在线教室”。它运行起来初始界面长这样子:

1.互动性,每个房间对应一个互动白板,默认情况下,房间内所有人都可以操作白板,并且交互效果所有人可见的;

2.扩展性,除基本的书写、涂鸦功能外,互动白板支持自定义应用(点击工具栏最下面的“田”字型图标查看所有应用);

个人认为支持各种 APPs 是 Flat 互动白板最强大的功能,通过 Flat 提供的 SDK 能力,我们可以实现许多复杂的功能的白板应用。

互动白板的内容,包括文字、涂鸦以及 App,可由 SDK 中的 Window Manager 对象来控制。可以通过官方提供的 demo来快速熟悉一个App开发流程。利用 Window Manager 的 API 接口,我们可以完成应用实例通信等操作,具体例子请见后文。

在展开具体例子前,先介绍“成语解谜“项目的整体框架。如下图,我们将前后端分离的方式,前端专注页面绘制与互动,后端专注题目生成与结果判断。用户访问前端页面无需下载全量词库,大幅提高访问速度。前端利用 Window Manager 的 context API 接口,在声网服务器上进行 App 实例的同步与广播。

我们采用“设计驱动”的开发模式,首先画出设计图,然后一步一步的把脑海里的画面通过代码变成现实 :

适合游戏开发的前端框架很多,Three.js、Phaser、Cocos2d-js等,针对具体需求选择。个人感觉 Three.js 比较底层,用来写游戏代码量可能比较大。Cocos2d-js 封装程度较高,需要熟悉Cocos的工具链,对于非专业做游戏的同学而言,上手难度不低而且技术可迁移性不高。

以“成语解谜”为例,我们来介绍编码的一些细节。首先我们找到自己代码的挂载点,根据文档给出的 demo或者本文提供的例子,找到这个入口文件:

我们为每个场景写一个 Scene 类,这里只有一个场景。App 类实例化了 Scene 类,并使用addChild将scene实例加入渲染。接下来我们为主界面写一个 Scene。关键代码如下:

在 Scene 的构造函数里实例化了“提交”、“重置”和“答案”三个按键,并定义了对应事件。我们在 Scene 里实例化了类 Idiom,一个 Idiom 实例对应一套字谜与候选字,Idiom 又有子对象 Piece,Piece 对应具体的每一个字块。由于 Scene 的按键事件函数的需要,我们把Piece 状态的保存/读取方法写在了 Scene 类里。

我们在 Idiom 类里定义了谜面与候选字的(Piece)字块生成方法、重置方法、拖拽生效方法。在 Piece 类中实现拖拽时的外观行为。

由于词库比较大,用户每次加载完整词库会消耗较多的带宽和时间,对用户体验影响较大。我们通过搭建后端将谜面的获取、提交结果的验证、答案的获取,进行服务化,提升用户体验。

如上文“架构规划”所述,我们和每个 App 实例均持有一个 token,用于与后端通信时,对应上后端的游戏实例对象。UserGames 的 key 即为 token,在接受到浏览器发来的请求后,后端会在 UserGames 中查找相应的游戏实例 BoardGame,并得到当前的游戏状态,包括谜面 table、答案 answers、答案解析 answerDetail 等。

2.使用 DFS 递归搜索谜面。在当前成语找一个字 k 作为下一个生成开始的节点,根据约束条件,选定新成语以及新成语摆放位置:

我们通过生成算法形成的谜面同时会产生 1 个唯一的答案。但实际上可能答案并不唯一,尤其是在成语较多时,交换某几个字,亦可生成合理的答案。针对这种情况,我需要逐个校验用户提交的成语。若成语库里总共有 N 个成语,对成语库的成语生成字典树 Trie,可以将查找时间复杂度从 O(N) 下降到 O(1),最多 4 次搜索。

负责游戏实例生成的结构体 GlobalBoard 储存了全量成语以及中间数据信息,作为全局单例,减少内存拷贝;对于每个问题(谜面)获取的请求,直接返回 GlobalBoard 生成结果的拷贝。

到目前为止,我们基本实现单用户的游戏。但是当我们打开两个浏览器 tab 模拟多用户操作时会发现,App 的交互仅对当前用户生效,其他用户是无感知的。表现为,A 用户打开 App,拖拽到 App 窗口合适的位置,开始游戏,将候选词与空字块交换,然后提交;同时,B 用户在同一房间,却只看到了 A 打开 App,拖拽 App,看到的 App 内容与 A 的 App 展示内容并不同步,也感知不到 A 对 App 做的操作(能看到 A 鼠标光标运动,这是 Flat 兜底的同步逻辑)。

针对当前问题,我们可以自然想到必须有某种机制,使用户在本地对 App 实例操作后,同步状态到某个所有用户可访问的远端服务里,然后通知所有用户将远端服务储存的状态同步到本地 App 实例中,重新渲染 App 画面,这样才可以实现多用户的互动。

谈到这里,大家可能会想到,那我们是否可以在自己写的后端服务中加入同步功能呢?让我们构思一下做这样的同步功能需要做哪些事:

仔细思考会发现,稳定可靠的实时通信其实是一个比较大的课题,并不应该成为实现业务、产生业务价值的一个主要工作,换言之,自己造轮子的投入产出并不高。声网在实时网络通信领域耕耘多年,基于其技术积累,在 Flat 项目中提供一系列非常有用的通信 APIs,这些 APIs 设计与 React 很像,比较容易上手。下面我们通过这些 APIs 进行同步与广播,解决互动性的问题。

我们给每个 App 实例持有一份storage对象,storage对象来自白板应用创建时得到的 context。这里的storage.ensureState用以确保storage.state包含某些初始值。context.storage实际上关联了远端服务的一个存储实例,它实时到本地 storage 的变化,当变化发生时,将自动同步最新的 storage 到服务端。即使是不同的用户,同一房间相同的应用实例,实际上会对应到同一个远端 storage,画一张图直观一些:

弄明白 storage 的同步特性,我们要做的就是在游戏状态发生变化的时候更新 context.storage,以及增加 context.storage 变化的回调事件,将远端 context.storage 同步到游戏(应用实例)中。

设想这么一个场景,我们的用户需要共同操作同一个 App 实例,比如共同完成一场解谜游戏,用户 A、B 几乎同时点击了“提交”,后端接到提交请求,判断答案正确,然后为游戏实例分发新的题目,此时,若后端在为 A 分发题目的过程中 B 的请求到达,且也给 B 分发新的题目,会导致 A、B 前端收到不一致的新题目。此外,还有一种场景,用户 C 因为弱网或其他原因,提交后未马上收到反馈,重复频繁地点击提交,将导致发起重复请求,用户较多且请求时间集中时,容易导致负载波动,影响服务质量。

因此,我们有必要为“提交”增加一个分布式的锁,使在某个 App 实例里,所有时间里,只能由一个用户提交。

当对于某个 App 实例,某个用户提交通过得到新的游戏状态(新的谜面与候选词等)后,需要将状态同步给其他用户。实际上我们可以将获取新游戏与状态写入本地游戏这两步分离,在进行广播时自己也会接收到,所有包括自己在内的用户到广播立即写入本地游戏。如图所示:

至此,我们的跨越前后端的实例通信部分也完成了,实现了用户对 App 实例操作时交互的同步,并处理了如同时、重复提交这类的并发问题。此类问题在其他互动应用的开发中也普遍存在,这里提供了一些参考。

声网 Flat 开源项目提供了白板 SDK,支持开发自定义 App,为在线教育和白板应用提供了巨大的想象空间。本次分享从一个初次接触 Flat 开发者的视角,介绍了互动白板的特点,并从基于实际例子——完成一款互动小游戏,分享了小游戏前端框架的选择与使用、整体架构设计思路、后端开发流程等。同时介绍一些实用的 window-manager API,并在实战中如何使用这些 APIs 来快速解决一些原本比较复杂的问题。希望能对大家开发Flat白板自定义应用、在线互动小游戏中提供一些参考和帮助。由于时间仓促,仍存在许多有待完善和优化的点,请大家不吝指出。抛砖引玉,互动教育、教育游戏等在国内外仍有较大的市场前景,希望与大家有更多的交流与合作,谢谢大家。

作者 admin