交互媒体设计教案Unity插件Unity

可视化编程插件Bolt的入门介绍及与PlayMaker的对比

2018-11-23  本文已影响56人  shimmery

Bolt是什么?

Bolt是一个比较新的Unity可视化编程插件,目前稳定版本为1.4,alpha测试版本为2.0。

官网地址:https://ludiq.io/bolt
Asset Store购买地址:https://assetstore.unity.com/packages/tools/visual-scripting/bolt-87491

Bolt在设计理念和使用上都很类似于UE4的蓝图(Blueprints),属于“流(flow)”式设计。简单地说,“流”式设计就是“按顺序依次执行每一步”,这其实才是最符合程序代码执行逻辑的设计,因为程序代码的执行逻辑就是“一行命令执行完再执行下一行命令”。

市面上用“流”式设计的可视化编程插件其实也蛮多的,早期的uScript Professional,现在的flowCanvas都属于此类。

我们可以这么来理解这些“流”式设计的可视化编程插件:

在Bolt中,其基本的“节点”(被称为“单元(unit)”)是Unity的各种API命令。Bolt号称支持所有Unity内置命令(大概有23000多种),还允许手动添加其他的第三方插件的自定义类(class)。因此,Bolt自夸:“凡是可以用代码实现的功能,都可以用Bolt来实现”。

If it can be done with code, it can be done with Bolt.

这句话说得有一点夸张,但也代表了Bolt的特征。它本质上就是直接调用Unity的API命令,用和程序脚本差不多的执行逻辑来运行,这在本质上与c#脚本是一致的。比如下面这个Bolt的Graph,就完全等价于后面的那个c#脚本:

public class test : MonoBehaviour {

    // Use this for initialization
    void Start () {
        Debug.Log ("Hello World!");
    }
    
    // Update is called once per frame
    void Update () {
        transform.Rotate (0f, Time.deltaTime * 30f, 0f);
    }
}

可以看到,Start ()Update ()函数变成了Event节点,Debug.Log()Transform.Rotate ()Time.deltaTime也变成了普通节点。有专门的String节点用来获得一个String类型的值,也有Multiply节点来进行乘法计算。程序员如何“设计”一段代码,我们就可以如何“设计”一个Bolt的Graph。

当然,从实际的使用感受来看,这样的设计方式并不是很“人性化”,那些“节点”的功能对于初学者来说也不是那么“一目了然”,还需要用户具备一定的程序思维能力,才能将自己的需求转换成流式的节点运行逻辑。坦白来讲,Bolt的上手难度是要高于PlayMaker的。但是,一旦用户跨过了“新手期”,就可以大量将已有的“代码类”教程翻译成Bolt流图,获得快速提升。而不像PlayMaker,还需要将“流”式的代码改写成“状态机”,然后再才能予以实现。

本质上,PlayMaker中一个State中的Action也是以“流”的方式运行的(上方Action先执行,下方Action后执行),但一方面各种“Every Frame”和非“Every Frame”的Action混合在一起非常难以分辨,另一方面很难表达“分叉”式的逻辑(比如“if... else...”这样)。因此,对于稍复杂一些的“流”式逻辑,就很难在一个State中实现出来,而要改写成多个States之间的转移变化。
但实际上,“状态机”其实是为了解决在一些特定问题上“流"式逻辑太复杂而提出的一种归纳整理的方案,将复杂的”流“拆成几个相对简单的”状态“,而到了PlayMaker里面,往往不得不将本来很简单的”流“式逻辑改成甚至于更复杂的”状态机“方案,颇有点”脱了裤子放屁“的意思。
Bolt本身也有状态机(State Machine),但它的状态机就是在流(Flow Machine)之上的,这才是符合程序设计逻辑的做法。


Bolt的安装和设置

Bolt需要在Asset Store或者其官网上购买,购买后会获得一个.unitypackage文件,用于导入到Unity项目中。

导入完成后会自动弹出设置窗口:

首先是欢迎界面,点击Next按钮设置Naming:

这里建议使用Human Naming,比较不那么“反人类”,但依然还是需要经常反查Unity的脚本帮助文件来了解哪些可用的Bolt单元/Unity函数。

Documentation栏是用来建立本项目的帮助文件的,点击Generate Documentation可以建立编辑器内显示的帮助文档,如果出错也不要紧,忽略即可。Inspectors栏也可以直接点击Generate Inspectors,让Bolt自动完成这一步工作。

如果希望在Bolt中控制第三方插件,则需要在Assemblies栏中加载对应的第三方插件的DLLs文件。

如果只是一些自定义的库(class)或者构造体(struct),则需要在Types栏中予以添加,然后点击Generate让Bolt创建本项目所需要的codebase。这个codebase就是我们可以在Bolt中使用的全部单元的集合。

正在构建Bolt单元数据库

在使用过程中,我们也可以通过Bolt菜单中的相关命令,来添加新的Assemblies或者Types,或重建单元数据库。

在偏好设置窗口中,我们可以对Bolt进行一些设置,其中大部分选项都可以保持默认,但我个人比较建议在Ludiq一页中把Snap To Grid选项勾上,这样强迫症患者就不需要对着参差不齐的流图而揪心了。


Bolt的基本概念

Machine(机)

Machine(机)是我们添加给游戏对象(Game Object)的Bolt组件,相当于一个完整的脚本。Bolt有两种Machine:Flow Machine(流机)和State Machine(状态机)。前者是用一个个Unit串成完整的流式逻辑,后者则是用流机来描述一个个不同的状态(states)以及状态与状态之间转换的条件(Transition)。

本文中不会多讲State Machine,在刚刚接触Bolt的时候,能用Flow Machine完成的交互设计都应该尽量用Flow Machine来完成,一方面是因为没必要,另一方面也是因为在Bolt中的State Machine设置起来其实还是蛮复杂的。

Embed VS. Macro

Machine可以内嵌(Embed)于Unity场景,也可以作为宏(Macro)保存在Asset文件夹里作为一个可以重复使用的游戏资源。

建议最好在刚创建完Machine就决定究竟是使用哪种方式,因为虽然可以从一种方式转换为另一种方式,但这个过程并不是100%无损的,复杂的Graph几乎不可能不在转换后出现各种各样的问题。

我个人的建议是,如果一个Machine所代表的功能会被用在不同的地方,就请尽量使用Macro,或者说,如果你准备“复制/粘贴”某个Machine中的Graph到一个新的Machine中去,那么就将这个Machine转换成Macro,然后在新Machine中调用。

Graph(图)

在Bolt中,Graph(图)是程序命令执行逻辑的可视化呈现。Flow Machine中呈现的是Flow Graph,State Machine中呈现的是State Graph。

Flow Graph呈现出来的是一堆彼此相连的units(单元),其中必定有一个起点,这个起点通常是一个Event类型的unit,比如常见的StartUpdate。代表着“当……发生时,开始执行本Graph”。

比如前面示例中的左边的Graph,就是在说:“当本游戏对象被载入时,在console面板中输出一行字符串’Hello World!’”。

一个Machine中可以有多个Graphs,用来区分在不同事件发生时所触发的不同程序命令执行逻辑。通常还可以将同一事件所触发的多段逻辑分开成不同的Graph,以方便调试。比如“行走”和“跳跃”都会被Update所触发,但我们可以在一个Flow Machine中用两个Update打头的Graph来分别制作这两个交互行为。不过,如果这两个交互行为互相之间有所关联,或者需要有严格的前后执行顺序,那么最好还是做成一个单独的Graph,避免出现问题。

严格来说,Bolt其实是将Machine中的所有内容定义成一个Graph,但我个人认为这样反而不好,所以在这里解释成“一个graph代表一个完整的彼此相连的units执行流程结构”。希望不会对大家的理解造成误会。

Unit(单元)

单元的类型

Unit是Bolt中最基本的元素,一个unit代表一个操作命令,多个unit按照顺序组合成Graph,从而实现某一个特定的程序功能。默认情况下,Bolt有超过23000个不同的unit,我们可以把它们分成几个大的类别:

新建/替换单元

在Graph窗口的空白处点击鼠标右键,选择Add Unit...,就可以通过搜索关键字来创建新的unit。如果在一个已有的unit上点击鼠标右键,选择Replace...,可以替换这个unit。

新建Unit 替换Unit

Fuzzy Finder(单元搜索器)

用来搜索unit的窗口叫“Fuzzy Finder”,目前还是比较好用的,搜索速度也比较快。但要注意的是,如果用多个关键字搜索,这多个关键字的顺序必须和结果中这些关键字的出现顺序保持一致才行,比如我搜“position” + “transform”就得不到“transform.position”,必须搜“transform” + ”position”才行。

Connections(连接) & Ports(接口)

并不是每个unit都需要用被Connection相连接才能起效,有的unit根本就没有Connection端口。一般来说,执行计算任务的unit都不需要连接Connection,比如下图:

当然,这个图里面输出的数据最终还是需要被传递给某个连接了Connection的unit才会真的被执行。

Overloads(分身)

一个Unity命令可以有多种调用方式,叫做Overloads。Bolt中使用不同的unit来对应各个Overloads,我个人将其翻译成“分身”。

c#脚本中的Overloads 对应Bolt Unit的Overloads

我个人不太喜欢这种将每个Overload都做成单独节点的做法,每次都要找半天,而且很容易看错。但貌似也没有更合适的办法了。

Variables(变量)

在Bolt中,有5种不同形式的变量:

个人认为,Bolt的变量形式被设计得很奇怪。很难将其与正常脚本中设置的变量对应起来理解。

  • Graph Variable有点类似private variable(私有变量),但又必须提前定义好,远不如脚本中那么方便使用;
  • Object Variable类似public variable(公开变量),可以被外部访问,但Bolt的Object Variable是通过在同一个Game Object上的一个叫做“Variables”脚本来定义的,相当于是在获取另一个组件的变量在用,而不是在本组件中定义可以公开访问的变量;
  • Scene Variable类似global variable(全局变量),但其实是在场景中新建了一个叫“Scene Variables”的空游戏对象,然后通过“Variables”脚本来定义,相当于是在获取同一场景中另一个游戏对象的变量在用;
  • Application Variable才好像是真的全局变量;
  • Saved Variable实际上是在操作Unity的PlayerPrefs,算是一个简单实现“存档”功能的方法吧,但还是显得不伦不类的。

此外,Bolt能够用几乎所有Unity数据类型做为变量,比如:

变量窗口可以通过菜单Window -> Variables打开:

我个人强烈建议将这个窗口dock到编辑器的UI里,虽然Graph窗口中会自动出现Variables窗口,但那需要Graph窗口足够大,而大多数时候,我们都不会将Graph窗口放大到那么大。

最左边是我自己dock的窗口,紧挨着的是自动弹出来的窗口,但自动弹窗并不是一直都在的

最后,我们可以通过将variable拖动到Graph窗口中来创建Get Variable单元,以使用这个variable,也可以按住Alt键拖动variable到Graph窗口来创建Set Variable单元,以设置这个variable

这个Graph做的事情是:获取自身(self)游戏对象的位置(Vector3),并减去储存在“Player”这个变量中的游戏对象的位置(Vector3),并将其结果(Vector3)设置给Offset变量。

Bolt的限制

Bolt所号称的“凡是可以用代码实现的功能,都可以用Bolt来实现”其实是有水份的。

虽然Bolt支持几乎所有的Unity函数命令,但并不等于我们就能完全无缝地将所有c#脚本代码翻译成Bolt流图。至少:

这些缺失大多可以通过自己写一些脚本来“弥补”,但如果自己都能写脚本的,那还用Bolt干什么呢?况且,如果有专门的程序员帮我们写扩展来“弥补”缺失,那我们干嘛不用PlayMaker呢,反正缺什么Action就让人写就是了。

另外,Bolt的Graph的复杂度其实要比程序代码高很多,比如上面一条简单的transform.Rotate (0f, Time.deltaTime * 30f, 0f);就要用3层结构4个单元来组合完成,可想而知一个复杂的程序代码翻译成流图之后会有多么复杂。而且,Bolt的运行效率(尤其是编辑器中的运行效率)比起代码来要低很多,项目小还好,一旦项目大起来,估计电脑差了连调试都调试不动呢。

因此,Bolt并不是一个“完美”的可视化编程插件,只是比现有的解决方案要好一些罢了。与其将其当做一个“生产力工具”,不如将其当做一个“学习工具”或者一个“原型工具”。在学习Bolt的过程中,尽量去了解正常的程序设计思维是如何进行的,去熟悉Unity的各种API命令,去掌握不同交互逻辑的实现手段和技巧,才是这个插件对于我们的最大价值。

上一篇下一篇

猜你喜欢

热点阅读