# 前言

CryDialogSystem 是基于 Unity3D Editor 的对话系统。代码基于之前的项目 “Cry Story Editor”,并进行修改、添加功能而成。

之所以单独将 “对话系统” 单独从 Story Editor 中独立出来,而不是将功能弄在一块,主要是考虑到对话与剧情本身的差别。按照逻辑,不同于 CryStoryEditor 全局的控制,CryDialogSystem 设计为适用于单个对象的处理。因此,结合 CryStoryEditor 可以更方便地处理 NPC 之间的对话逻辑控制。

出现这样的逻辑分化的原因,是由于在这之间,我又去体验了一下传统的 RPG 游戏,当时玩了一下 3DS 平台的一个非常金典的 RPG 游戏《METALMAX》。该游戏当年最先出现在 FC 游戏机平台,与同一时期的《勇者斗恶龙》、《最终幻想》属于同一类型 —— 虽然后两者现在都已经出了各种 “大作” 了。《METALMAX》依然还是一如既往的传统 RPG 类型。

上述几个传统 RPG 游戏,我不知道大家有没有玩过,不过对于我个人的话,还是比较喜爱的。另外,作为都是以 “剧情” 为主的游戏,我觉得它们的任务设计还是有相当值得参考的地方。

在玩游戏的时候,不知道大家有没有发现一个重要的地方:这些游戏的剧情发展,只会影响 “一部分” 需要的 NPC,而另外的普通 NPC,甚至在剧情发展了相当远的地方,都是同样的功能、对话。所以我猜测:游戏中的对话,是独立保存在 NPC 本身的,并且默认具有一个初始对话,然后在 “出生” 后,通过剧情发展,不断更换需要更换的指定 NPC 对话。若没有被剧情 “需求”,则一直保持自己的初始对话不变。

这样的一个好处就是:每一个任务,都只需要管理自己的内容,以及所要影响的 NPC 对话,对于无关 NPC,则完全不用管。

对于目前我所在的项目来说,我就觉得对话那块设计得不是很合理,他们将对话固定在数据库文件中,然后通过 Story 的发展,然后由策划手动指定每一个任务流程中,所有 NPC 的对话。逻辑都完全与 Story 结合在了一块儿,若某一个 NPC 没有经过策划处理,则将一直保持默认对话,而且当该任务流程结束(我们项目所使用的 StoryEditor 会同时运行当前 Mission【注:不同于游戏流程中的任务】,并且当运行一个 Mission 时,会同时运行之下的所有 Trigger,注册事件 —— 即完全并行执行),里面所有的处理都将会失效,甚至会无法选择 “是否” 保存该任务流程中,改变出来的 NPC 对话。

基于上述原因,我决定在自己设计的 Cry Story Editor 编辑器工具中,将其完全从 Story Editor 中独立出来。

# 简介

基本逻辑与 Cry Story Editor 基本一致,只是相关功能进行相应的修改或优化。
比如增加的对话选择节点,可显示相应选项的 Index 等,如图:

选择节点

所以,基本功能及概念,可以参考在下前一篇介绍 Cry Story Editor 的文章。

# 使用

# 运行

Cry Dialog System 主要提供的一个类是 “DialogManager”。该类可以实例化在任何需要的地方,只要能够保证每帧调用 Tick () 方法即可。
当然对此不用担心性能问题,因为当一个对话结束,或者没有对话时,该方法是不会执行任何操作的。

s
public void Tick()
        {
            if (_accomplish || _currentDialog == null) return;
            if (_currentDialog.Tick() != EnumResult.Running)
            {
                _accomplish = true;
                if (OnDialogEnd != null)
                    OnDialogEnd.Invoke(_currentDialog._name);
            }
        }

例如,在项目下 DialogSystem/System/Runtime 文件夹下,提供了一个名为 “DialogRunner” 的简单运行一个对话的示例:

s
/// <summary>
    /// Just for test
    /// </summary>
    public class DialogRunner : MonoBehaviour
    {
        public DialogObject _defaltDialog;
        private DialogManager _dialogManager;
        void Start()
        {
            _dialogManager = new DialogManager();
            _dialogManager.LoadDialog(_defaltDialog);
        }
        void Update()
        {
            _dialogManager.Tick();
        }
    }

该方法就是运行一个对话所需最少的操作了,若是应用于自己的项目,远远是不够了,所以请务必做出相应修改。

编辑器另外提供了一个 Advance Edit 工具,可以编辑数组字符串类型的时候使用:

![Advance Edit](/blogimages/oldpictures/2016.10.26-Advance Editor.jpg)

后续有需要的话,也会考虑增加其他类型的高级编辑功能。

# 添加节点

添加一个新的节点,非常简单。就像 CryDialogSystem 一样,只需要根据需求,选择继承于 Decorator/Action/Condition 或者 Event 就可以了。
注意在类名前面加上相应的说明,或者路径属性:

[Help("这是显示在编辑器中的帮助文字")]
    [Category("System")]// 分类,将会影响右键点击添加之后,出现的菜单

当然同样也是可以选择不添加的额,当然那样的话,在编辑器中,创建路径将会保持默认,点击节点之后,也不会有帮助说明了。

最后,通过 override 相应的方法,即可实现功能。这一步看看代码就知道相当简单了,所以不多解释。
注意下 “ToDescription ()” 重载方法即可,该方法影响了节点本身显示的信息,比如策划填写了对话内容,就可以选择在这儿读取,然后返回。

如图:

对话节点

# 提示

在 CryDialogSystem 中,同样也可以像 CryStoryEditor 中一样,包含实际游戏逻辑控制等节点 —— 这个我也是考虑过一些时候,本来又打算去掉的意思,最终还是决定留下来,灵活性可以提高很多,用不用就看需求了。

# 最后

截个图吧:

截图

虽然 CryStoryEditor 和 CryDialogSystem 都是因为项目 “现在的剧情编辑器不好用,你可以私下研究下 XXXX....” 的原因而搞了下,嘛... 最后也因为担心 “改动” 而被放弃了。