前言

由于 Snap! 出色的可理解性和强大的灵活性/可扩展性, 我们的合作伙伴决定在 Snap! 投入资源。

DynaLab 正在为合作伙伴英荔构建增强版的 Snap! , 我们将 Scratch 社区最强大的一些插件移植到了 Snap! , 同时也为 Snap! 设计了一些强大的 primitives:

  • BLE primitives
  • websocket primitives
  • EIM primitives

此外, 我们还为 Snap! 设计了全新的物理引擎:

Snap! 平台本身也在快速发展, 不久将推出 Snap! 10 , 它的可理解性和可扩展性将有进一步的飞跃。 用户可以 “Blocks all the way”, 你可以理解并修改最基础的积木:

Jens(Snap! 创始人) 为 Snap! 构建了强大的 Hyperblocks, 它将提供类似 numpy 的能力(和 numpy 一样, 也受到 APL 的启发), 这将有助于提升神经网络教学的可理解性。

Snap! 拥有一流的数据结构(list), 也许是所有图形化语言中最优秀的, 它不仅有出色可理解性(非常适合用于教学复杂的算法), 而且具有出色的性能, 足以在浏览器页面处理超过 400 万的数据:


说回正题, 作为合作的一部分, 我们的合作伙伴希望基于 Snap! 实现 Future Park 那样的交互项目。

思路

要想基于 Snap! 实现上图中的交互项目, 需要完成以下几个工作:

  • 接入激光雷达用于互动
  • 允许用户绘制的图像动态地进入舞台
  • 为这些造型预定义行为(诸如被用户碰到就逃跑)

由于我们在 Snap! 里完成了 CodeLab Adapter 插件(EIM primitives), 因此, 激光雷达的所有功能都可以在 Snap! 里实现, 没有写一行 js 代码。 这使得它支持用户继续扩展它。

后两项工作正是本文的主题: 动态添加 Snap! 造型, 并提供预定义行为。

在个人计算环境中编程的一项出色体验是, 在用户环境里可以完成几乎所有的工作(包括开发工作), 不需要进入系统"下一层"。 这意味着 (a) 用户具有和开发者一样多的能力/权力, (b) 用户可以利用先前学到的技能做新的事情。

很自然地, 我们希望尽可能在 Snap! 完成绝大多数工作, 逼不得已, 才引入尽可能少的 primitive。 在整个项目实施过程中, 我们最终只引入了一个 primitive, 用于将 base64 字符串(png 或 svg)转化为 Snap! 内部 costume 对象 。

动态添加 Snap! 造型

Future Park 体验的一部分是用户拿着真实的彩笔给角色涂颜色, 然后角色被 “投放” 到虚拟舞台上, 栩栩如生地活动起来.

为了将用户绘制的角色 “投放” 到舞台上, 需要进行以下工作:

  1. 在纸张上沿着图案边缘 “抽取” 出角色, 可以使用 opencv 边缘检测之类的算法来做。
    • 考虑到 Python 生态大量的视觉处理库, 使用 Python 来做比较省力
    • 随着 Snap! 社区对神经网络感兴趣, 估计 Snap! 也会有原生的视觉处理能力
  2. 将抽取的图案编码后传输到 Snap! , 可以使用 base64 来编码数据, 使用 MQTT 来传输数据
  3. 在 Snap! 中将接收到的 base64 数据转化为 costume

第 2 项工作的相关 Python 代码: future-park-client.py

第 3 项工作相关的 Snap 示例: future-park-demo.xml 。 示例中还包含了一个简单的模拟程序, 用于模拟Python客户端发送 base64 图像数据, 我喜欢尽可能地在 Snap! 这样具有 Smalltalk 特质的编程环境里工作, 它要比在 Python 中工作愉快很多。

预定义行为

前边提到:

为这些造型预定义行为(诸如被用户碰到就逃跑)

我们使用 “克隆” 来做这件事。

我们为每个精灵(sprite)预定义它的行为, 每当这个角色的造型传入系统的时候, 就根据传入的数据, 启动一个克隆体, 克隆体穿上传入的造型。克隆体的行为和它所属的精灵(sprite)是一样的。

这可以类比面向对象编程,我们在类(Class)里预定义了行为(在 sprite 里编写行为代码), 不同的初始化参数进行实例化(在Snap!中是创建克隆体)。

克隆是一个很大的话题, Snap! 的克隆改进自 Scratch, 易用性和一致性都更好。Scratch 的克隆资料大多适用于 Snap!

参考