一、安装

1. 付费方式

尽管 Yarn Spinner 是开源项目,不过项目组仍然提供了付费渠道。通过付费方式安装该插件能够为项目组提供资金支持以维持项目的运转。因此,笔者在此还是比较推荐使用付费方式进行安装。

(1) 在 Itch.io 上购买

前往 Yarn Spinner Itch.io Store 购买后,即可下载 .unitypackage 格式的 unity 插件安装包。

(2) 在 Unity Asset Store 上购买

前往 The Unity Asset Store page for Yarn Spinner for Unity 购买后,即可在 Unity 的 Package Manager 中直接安装。

2. 免费方式

Yarn Spinner 是以 MIT 协议开源的项目,这意味着你可以在非付费的情况下自由使用,并拥有许可。

(1) 从 Github 安装

因为 Yarn Spinner 在 Github 上开放源代码,因此可以从 Github 仓库中直接下载源代码包来进行安装。

注意:使用 Github 方式安装的存在缺点,即您无法通过 Package Manager 对来源于 git URL 的包进行更新。

① 安装 Git (最低版本要求 2.14.0)。

② 在 Unity 中选择"Window(窗口)"菜单,并选择"Package Manager(包管理器)" 以启动包管理器窗口。

③ 点击左上角的 "+" 按钮,然后选择"Add package from git URL"。

④ 在弹出的文本框内,输入以下URL:https://github.com/YarnSpinnerTool/YarnSpinner-Unity.git#current,并点击"Add"。

⑤ 等待下载并安装完成即可。

(2) 从 Unity Package Manager 进行安装 (推荐)

除通过git URL 安装外,还可以像正常安装来自 Unity Asset Store 中购买的包一样安装 Yarn Spinner ,具体来说,Yarn Spinner 可以通过 Unity 的开源仓库注册表 Open UPM 获取。

注意:从 OpenUPM 获取插件包要求您的项目使用的最低版本至少为 Unity 2020.1 ,如果您的项目使用更早期的 Unity 版本,那么推荐从 Github 安装。

在项目中设置 OpenUPM 注册表

Unity 的 Package Manager 默认不会扫描来自 Open UPM 的源,因此,首先要在项目中配置包管理器如何找到内置包之外的注册表的源。

① 在 Unity 中选择"Edit(编辑)"菜单,并选择"Project Settings...(项目设置...)"以启动项目设置窗口。

② 在窗口左侧列表中选择"Package Manager(包管理器)"选项。

③ 在弹出的右侧窗口中,输入以下信息:

  • Name(名称) 字段中,输入 OpenUPM

  • URL 字段中,输入 https://package.openupm.com

  • Scope(范围) 字段中,输入 dev.yarnspinner

最后,单击 Save(保存) 按钮进行保存即可。

完成上述操作之后,即可在 Unity 中安装 Yarn Spinner 插件包了。

安装 Yarn Spinner 包

① 在 Unity 中选择"Window(窗口)"菜单,并选择"Package Manager(包管理器)" 以启动包管理器窗口。

② 点击 Packages: ... ,然后选择 My Registries 选项。

③ 此时便可以看到 Yarn Spinner 已经出现在菜单中了,在左侧菜单中选择到推荐的版本,然后点击 Install 按钮进行安装即可。

二、核心要素解释

1. Yarn 文件

在 Unity 中,Yarn Spinner 主要会使用到以下两种文件类型:Yarn ScriptYarn Project

(1) Yarn Script

Yarn Script 即为 Yarn 格式的脚本、或称台本文件,在游戏中的对话内容将主要书写在该文件中。

对于 Yarn Script 的详细说明,请参见:Yarn 脚本语法解析

Yarn 脚本是纯文本文件,因此除在 Unity 的右键上下文菜单中选择创建以外,也可以直接在资源管理器中右键新建文本文档,并将其后缀名更改为 .yarn 来创建一个 Yarn 脚本。

(2) Yarn Project

Yarn Project 代表一个文本工程,主要用于绑定一系列的 Yarn 脚本,同时也可以配置其他例如本地化、语音等。并且,实际运行对话的组件 Dialogue Runner 也以 Yarn Project 为单位进行对话内容读取。

其中,Source Files 表单用以指示该 Project 如何寻找到对应的脚本文件,正常情况下,Yarn Project 会根据该规则在目录下自动索引 Yarn Script 文件。

可以通过以下符号来自定义 Yarn Project 对脚本文件的索引规则。

符号

描述

示例

*

任意文件名。

"*.yarn" 将匹配任意的 .yarn 文件,如 "Foo.yarn" 或者 "Bar.yarn"。

**/*

所有文件,且包含子目录中的文件。

"**/*.yarn" 将匹配本目录包括子目录下的所有 .yarn 文件,如 "Foo.yarn" 和 "Foo/Bar.yarn"。

..

父目录。

"../*.yarn" 将匹配父目录下的所有 .yarn 文件,如 父目录中的 "Foo.yarn" 。

此外,Localisation 表单用以配置该工程的本地化,即翻译。

2. Yarn 组件

(1) Dialogue Runner

顾名思义,Dialogue Runner 是 Yarn Spinner 的管理器组件,该组件负责加载、运行和管理 Yarn Project 中的内容,从中读取 Yarn Script,并在游戏时将脚本中的内容传递给其他游戏元素,如GUI等。

Yarn Script、Yarn Project 和 Dialogue Runner 之间的关系大概如下图所示:

Dialogue Runner 的参数参考如下:

属性

描述

Yarn Project

表示该 Dialogue Runner 所绑定的工程文件。

Variable Storage

设置/存储脚本运行中产生/使用的变量的组件,未设置时会默认产生一个 In Memory Variable Storage 组件,详情见下文。

Line Provider

设置指示脚本内容如何转化为本地化文本对象的组件,未设置时会默认产生一个 Text Line Provider 组件,详情见下文。

Dialogue Views

设置接收文本、命令和选项内容并进行处理的组件。

Start Automatically

如果启用,那么游戏运行时 Dialogue Runner 会自动运行配置的起始节点。若未启用则需要在游戏中手动调用 Dialogue Runner 的 StartDialogue() 方法进行运行。

Start Node

当 Start Automatically 选项启用时,Dialogue Runner 将在启动时运行此选项输入的节点名称对应的节点。注意:若当前绑定的 Yarn Project 中不存在对应名称的节点将会报错。

Run Selected Options as Lines

如果启用,那么在游戏中当用户选择任意选项后,会将选项内容作为对话文本再输出一行。

Verbose Logging

如果启用,那么 Dialogue Runner 将在运行时在控制台内输出调试信息。

On Node Start

当 Dialogue Runner 开始运行新的节点时将会触发的事件回调。一个节点运行过程中可能触发多次该事件。

On Node Complete

当 Dialogue Runner 当前运行的节点内容结束时将会触发的事件回调。一个节点运行过程中可能触发多次该事件。

On Dialogue Complete

当 Dialogue Runner 停止运行时将会触发的回调函数。

On Command

当 Dialogue Runner 运行命令时可能会触发的回调函数。仅当系统中没有能处理该命令的组件时会触发该回调。

(2) Variable Storage

Variable Storage 是一个抽象组件,其顾名思义,是变量的数据库。主要用于存储和检索 Yarn 脚本中定义的的变量,使用三个词典作为存储数据结构,分别对应 String、Float 和 Bool 三种数据类型。

该组件的定义如下所示:

    public abstract class VariableStorageBehaviour : MonoBehaviour, Yarn.IVariableStorage
    {
        public abstract bool TryGetValue<T>(string variableName, out T result);
        public abstract void SetValue(string variableName, string stringValue);
        public abstract void SetValue(string variableName, float floatValue);
        public abstract void SetValue(string variableName, bool boolValue);
        public abstract void Clear();
		/// 检查某变量是否已存在, 即已被定义
        public abstract bool Contains(string variableName);
        /// 使用三个新变量词典来覆盖当前已存在的词典。
        public abstract void SetAllVariables(FloatDictionary floats, StringDictionary strings, BoolDictionary bools, bool clear = true);
        /// 获取所有变量
        public abstract (FloatDictionary FloatVariables, StringDictionary StringVariables, BoolDictionary BoolVariables) GetAllVariables();
    }

当 Yarn 脚本需要获取变量的值时,会调用 Variable Storage 的 TryGetValue<T> 方法;当 Yarn 脚本进行变量赋值操作时,会调用它的 SetValue 方法。

在插件中,该组件只有一个默认实现,即前文提到的 In Memory Variable Storage,顾名思义,是只将变量保存在内存中,而不会进行任何持久化的 Variable Storage 实现。

因此,若要对 Yarn 的脚本变量进行持久化,需要自行对 Variable Storage 进行实现。

关于数据持久化的内容已不在本文的讨论范围内,因此我们不再做过多的说明。

(3) Line Provider

Line Provider 也是一个抽象组件,该组件的主要功能是根据输入的由脚本内容转换而来的行 ( Line ) 对象来产生对应的本地化行 ( Localized Line ) 对象,并插入一些其他内容(如语音等)。

该组件的定义如下所示:

    public abstract class LineProviderBehaviour : MonoBehaviour {
        /// 核心方法
        public abstract LocalizedLine GetLocalizedLine(Yarn.Line line);
		/// 当前的本地化语言代号
        public abstract string LocaleCode { get; }

    }

默认情况下,插件已经提供了 Text Line ProviderAudio Line Provider 以及 Unity Localised Line Provider 三种实现。

Text Line Provider 使用由 Yarn Spinner 内置的本地化系统实现,而 Audio Line Provider 是在前者的基础上增加了获取语音的功能(在 Yarn Project 中可以设置与语音资源相关的路径,即 Assets Folder 。),最后,Unity Localised Line Provider 使用 Unity 的 Localisation 库进行实现。并且,以上的实现均会在检测到安装 Addressables 库后使用其进行远程载入。

默认的实现已经覆盖了绝大多数场景,因此该组件一般使用默认配置即可。

三、快速入门

使用 Yarn Spinner 能够在极快的速度下制作出最基本的对话功能。

1. 创建 Yarn Script 及 Yarn Project 文件

创建顺序可以不分先后,此处演示先创建脚本之后创建工程文件。

首先,在项目窗口中的 Assets 路径下,创建任意文件夹用于装载对话系统相关的文件,此处我们创建了一个名为 Dialogue 的文件夹。然后,点击右键在上下文菜单中找到 Create -> Yarn Spinner -> Yarn Script 然后点击创建即可。

创建得到的脚本文件可以直接用记事本打开,并且我们可以输入一些有的没的作为演示:

title: Start
---
Guard: I used to be an adventurer like you.
Guard: Than I took an arrow in the knee...

-> A sad story.
-> Who care.
===

此时由于我们新创建的脚本文件未有绑定的工程文件,因此会弹出提示信息,要求你创建一个工程文件。此处我们点击 Create New Yarn Project... 进行创建即可。

新创建的工程文件也会自动地与我们的脚本文件产生绑定。

2. 创建 Dialogue Runner

在 Yarn Spinner 的 Unity 插件中,已经存在完整封装的 Dialogue Runner 预制件,我们只需要在 Hierarchy 中右键打开上下文菜单,找到 Yarn Spinner -> Dialogue Runner 并点击选择,即可完成创建。

该预制件中已经包含一套完整的对话系统,包括UI等。当创建完成后,应该可以在场景中看到 Yarn Spinner 的默认GUI对话框了。

我们的最后一步,是将刚刚准备好的 Yarn Project 拖放到 Dialogue Runner 中对应的参数输入框中进行绑定。

3. 运行游戏查看效果

可以看到,此时对话系统已经运行的非常好了。

不过,默认的GUI交互和显示其实都存在问题,例如只能按 Continue 按钮继续对话,这显然不符合我们实际开发中的使用习惯。而且,这一套默认组件使用了 Text Mesh Pro 进行文本显示,这意味着如果不配置字体文件,这套GUI也是无法显示中文的。

为了解决这些问题,我们需要对GUI进行自定义,请参见下一节。

四、定制化

得益于 Yarn Spinner 良好的模块化封装,我们可以轻易的对 GUI 进行自定义操作,只需要像传统的 UGUI 工作流程一样将 UI 布置好后,附加 Yarn Spinner 的相关视图组件,再将带有组件的 GameObject 拖放到 Dialogue Runner 的 Dialogue Views 选项中即可。

1. 初始化

可以基于插件自带的预制体,也可以从零开始创建,此处我们演示从零开始创建的方法。

首先,我们在场景中创建一个空对象,并附加 Dialogue Runner 组件。

此时,准备工作便完成了。Dialogue Runner 的健壮性非常强,我们无需任何参数设置也可以直接运行而不报错。

2. 行文字输出

Yarn Spinner 的文字输出由 Line View 组件进行控制,因此,我们首先新建一个空对象,并附加上 Line View 组件。并且,由于我们希望在 UI 视图上输出文字,因此我们直接将该对象创建在 Canvas 中。

Line View 拥有很多可以自定义的选项。首先,它支持在文字切换时的淡入淡出,只要勾选 Use Fade Effect 即可。其次,Use Typewriter Effect 即启用打字机效果,能够让文字逐字出现。

此处,我们重点关心的是 Line Text 选项,该选项用于指定文本内容输出到哪个文字组件中,而且只支持 Text Mesh Pro 的文字组件,另外是 Character Name Text ,该选项指定角色名内容输出到哪个文字组件。

此处,我们新建一个 TMP Text 并修改其样式和字体风格,并拖放到 LineView 的 Line Text 选项中。

Canvas Group 选项是 Line View 所必须的,因此我们也要为其附加一个该组件,在 Line View 对象上附加即可,以便 Line View 能够控制其子对象的显示和隐藏。

最后不要忘了将 Line View 对象拖放到 Dialogue Runner 的 Dialogue Views 选项中。

最后,运行游戏并查看效果。

此时,文字已经成功的被输出出来了。

3. 行前进控制

默认情况下,通过调用 Line ViewOnContinueClicked() 方法可以让当前脚本文字前进到下一行。该方法一般是为 Button 准备的,但是其实也可以用别的组件来调用该方法。

此外,插件还提供了 Dialogue Advance Input 组件,该组件对按键输入进行了浅封装,用以支持如使用空格等按键进行前进等需求。

(1) 按键控制

如上文所说,我们可以使用 Dialogue Advance Input 组件实现用按键操作对话前进。

因此,首先我们直接在 Line View 对象中附加一个 Dialogue Advance Input 组件,并将 Line View 组件拖放到其 Dialogue View 选项中,以指示该输入应操作哪个视图前进。

其次,我们需要指定一个输入方案,此处已经提供了涵盖 Unity 原版的所有输入方案,包括按键码,虚拟按键和输入系统。此处按需选择即可,我们直接使用默认的按键码方案。

设置完成后,我们即可通过按键来控制对话前进了。

(2) 鼠标任意点击控制

基于上文中提到的方法,即调用 OnContinueClicked() 来使对话前进,我们只需要编写一个简单的脚本,即可实现鼠标点击任意位置前进:

using UnityEngine;
using Yarn.Unity;

public class LineViewController : MonoBehaviour {
    
    public LineView lineView;

    private void Update() {
        if (Input.GetMouseButtonDown(0)) {
            lineView.OnContinueClicked();
        }
    }

}

具体细节自行按需修改即可。

(3) UGUI Button 控制

使用 Button 控制是最简单的方法,只需使 Button 按钮的 OnClick() 回调触发 Line View 组件的 OnContinueClicked() 方法即可。

别忘了在 Line View 中绑定该控制按钮,以便 Line View 能够控制按钮的显示和隐藏。

4. 选项输出

Yarn Spinner 的选项系统由两个组件组成,分别是 Option List ViewOption View 。前者是选项的主要容器和控制器,后者是选项按钮组件,其继承于 UGUI 的 Button 类。

Option List View 在运行时使用 Option View 的预制体来动态生成选项,因此,我们需要先创建 Option View 的预制体。

此处,我们直接创建了一个 UI -> Button - Text Mesh Pro 的按钮,并且把其中的 Button 组件替换成 Option View 组件,然后根据需求自行调整 Button 和 Text 的样式和尺寸即可。

最后,我们需要将一个 TMP Text 组件拖入到 Text 选项中,以便输出选项的文字内容。

接着,我们准备创建 Option List View ,此处我们创建了一个简单的 Image 面板,然后附加上 Option List View 组件即可。

注意 Option List View 也会淡入淡出的显示,因此需要绑定一个 Canvas Group ,之后,将我们准备好的 Option View 预制体拖入对应的选项即可。

Last Line Components 是指显示选项前的最后一行的组件。这是因为显示选项时,显示正常文本的组件会被隐藏,同时因为弹出选项的前一句可能是问句,因此提供了该自定义选项。该选项的设置这里不再赘述。

在游戏运行时,选项会被生成为当前附加 Option List View 组件的游戏对象的子对象,因此若要调整选项的布局,我们还要为 OptionList 对象附加布局组件。

传统的文本显示习惯中,选项一般为垂直布局,因此我们使用了一个 Vertical Layout Group 组件,并将 Child Alignment 选项选择为 Middle Center ,这样做的效果是让子对象以正中心为基准点垂直排列,已经很好的满足了我们的需求。

若有其他需求也可以根据需求自行定义,此处不再赘述。

我们复制了三个选项预制体来查看当前的布局效果,可以看到当前布局已经运行的非常良好。

最后,我们将 OptionList 对象拖入 Dialogue Views 列表中。

运行游戏,查看效果。