前言

上一篇文章提到:

@Lounsen 上周末向我展示了 Unity Visual Scripting 出色的面向对象设计,使我对 Unity 产生强烈兴趣。我们预计很快就可以在 Unity 中制作兼容于 DynaTalk 的 Agent。

我们打算让 DynaTalk 支持 Unity 的一个主要原因是: 对项目有更多控制权。我们之前的探索环境–Roblox 是一个云端优先的软件,你无法部署自己的作品,它必须运行在 Roblox 的云端设施上。

Visual Scripting

在 Unity 中编程,比预想的愉快。Unity 中编程体验,是强烈的面向对象风格, 场景(Scene)中所有实体的基类都是 GameObject。Unity 甚至有不错的 Liveness(活性),支持 Live Coding。

最惊艳我的是 Visual Scripting, Visual Scripting 是 Unity 的图形化编程环境。

我们来看看官方对它的介绍:

Visual scripting 支持创作者使用图形环境开发游戏机制或交互逻辑,而不是编写传统代码。

实现程序员、艺术家和设计师之间更加无缝的协作,更快地进行原型制作和迭代

Visual scripting 是设计师和艺术家的绝佳选择,他们可以通过它来测试想法、进行修改或更直接地控制自己的工作。非程序员也可以利用技术成员创建的自定义节点和图。

程序员可以赋予团队力量。使用 Visual scripting 创建自定义节点,可以让团队成员(从艺术家到设计师)之间的协作更加高效。无论他们编程知识水平如何,都可以方便地在原型设计或生产过程中共同合作。

Visual Scripting 相当强大,几乎涵盖 Unity 引擎的大部分功能,你在 C# 脚本中能够做到的事情,在 Visual Scripting 基本都能做到。

有过 Scratch 编程经验的用户,其心智模型可以轻松应用于 Visual Scripting。 如果你有过 Scratch 编程经验,Unity from Scratch 系列视频能够帮助你快速起步。

Visual Scripting 相当面向对象(和 Scratch 相似,友好性和一致性比 Scratch 差些):

  • 通过拼搭积木块,编写当前对象的行为
  • 对象之间通过消息进行通信

Visual Scripting 与 C# 之间的消息传递

尽管我打算将大多数工作都放在 Visual Scripting 里做,但有些工作,在代码中做起来会轻松很多。

所以我们需要让 Visual Scripting 与 C# 双向传递消息。

我喜欢将彼此通信的 C# 脚本和 图形化脚本 视为 2 个对象,这样一来,就可以使用"对象之间通过消息进行通信"进行思考。

有许多方式可以在 Visual Scripting 与 C# 之间传递消息,最简单、通用的一种可能是 CustomEvent

CustomEvent 支持以下方向的消息传递:

  • Visual Scripting -> Visual Scripting
  • C# -> Visual Scripting

但不支持:

  • Visual Scripting -> C#

查看 Visual Scripting(1.8.0) 源代码,发现这部分功能官方目前还没实现。

通过与 ChatGPT 的合作,我在没有入门 C# 的情况下实现了这个方向的通信:

1
2
// Visual Scripting -> C#
EventBus.Register<CustomEventArgs>(new EventHook(EventHooks.Custom, gameObject), HandleCustomEvent);

以下是一个更完整的例子,展示了如何在 Visual Scripting 与 C# 之间双向传递消息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Message Passing Manager

using Unity.VisualScripting;
using UnityEngine;

public class CodeTriggerCustomEvent : MonoBehaviour

    {
      private void Start(){
            EventBus.Register<CustomEventArgs>(new EventHook(EventHooks.Custom, gameObject), HandleCustomEvent);
      }

      void Update()
      {
         if (Input.GetKeyUp(KeyCode.C))
         {
            Debug.Log("C# Trigger...");
            CustomEvent.Trigger(gameObject, "test", "C# trigger");
         }
      }
      private void HandleCustomEvent(CustomEventArgs eventData)
         {
            Debug.Log($"C# Received event data: {eventData.name} {eventData.arguments[0]}");
         }
   }

Unity 与外部系统之间的消息传递

为了

在 Unity 中制作兼容于 DynaTalk 的 Agent

我们需要在 Unity 中寻找消息传递机制,与 DynaTalk 通信。 Unity 提供了很多消息传递机制与外部系统通信, 诸如 HTTPwebsocket, 社区里有更过的第三方插件, 支持 MQTT 等消息传递机制。

我们打算采用之前探索得到的架构设计,以尽可能保持简单,同时尽可能复用我们的基础设施: 转发器、观察器、模拟器…

在我们之前的设计中,只需要使用 HTTP request, 就能完成与 DynaTalk 的双向通信。 在 Unity 中,这带来一个额外好处,编译为 WebGL 的游戏(运行在浏览器里!),也可以与 DynaTalk 通信。

DynaTalk Unity SDK

以下是一个简单演示:

以上演示使用了 DynaTalk Unity SDK(目前只是两个简单的C#文件)

DynaTalk Unity SDK 会将 DynaTalk 消息接口暴露给 C# 脚本 和 Unity Visual Scripting

以上演示视频,对应的 Visual Scripting 脚本如下所示(完全在 Unity 可视化编程环境中完成):

架构设计

架构的核心想法来自 Smalltalk:

  • 一切都是对象
  • 对象解释它所理解的消息
  • 对象通过消息进行沟通

它在 Actor 模型被表述为, 一个 Actor 可以:

  1. 响应收到的消息
  2. 发送消息
  3. 创建更多 Actor

面向对象(采用 Alan Kay 赋予它的含义)

Point of View is worth 80 IQ points – Alan Kay

面向对象不只是组织和编写代码的范式,更是一种强大隐喻和世界观。

  • 一切都是对象
  • 对象解释它所理解的消息
  • 对象通过消息进行沟通

这些观点,提供了强大的视角来看待和思考系统。很遗憾,现代面向对象(OOP),只看到了第一点。

一切都是对象。Unity 已经很好地支持了这点。我们无需再做什么。

对象解释它所理解的消息。 通过编写 Visual Scripting 与 C# 脚本,让对象使用 UnityEngine API 来合理地响应消息,实现消息所搭载的语义。

对象通过消息进行沟通。Unity 在这点上做的也不错。 Unity 支持很多消息通信的机制, 也许太多了:

  • Event
  • UnityEvent
  • EventBus
  • send message
  • CustomEvent

CustomEvent 足够通用和简单,我们将其用作内部的消息传递机制。而将 UnityWebRequest 用作 Unity 与外部系统的消息传递机制。

参考