【unity】基于命令模式的剧情系统

前言

回想某次项目,我负责一个剧情模块的搭建。当时才疏学浅,只会用链式结构绑定每个UI对象,这种做法非常费事而且耦合过高。现在我的这个系统也算是给当年一个交代吧,以后有类似的需求也可以直接拿过来用。

系统简介

这是一个能够在Unity中运行的,基于命令模式的剧情系统,支持自定义命令,编辑剧情数据,查看历史对话等。

该系统旨在提高对于游戏剧情的开发效率,同时遵循开闭原则,以确保扩展性良好。

仓库地址->https://github.com/AshScops/Dialogue-style-plot-system

各个类的职责

CommandConfig:继承自ScriptableObject。它用于存储一系列命令对象。

CommandBase:一个抽象类。它作为所有命令的基类,声明了三种抽象方法:

  1. Execute :在该命令对象出队后立刻被调用,且只调用一次。
  2. OnUpdate :在Execute 后每帧被调用,直到返回true;
  3. IsFinished:返回值类型为bool,在该命令对象执行完毕时返回true,未执行完则返回false。

CommandSender:一个单例,包含一个命令队列。它将从外部传入的CommandConfig中获取一系列命令对象并入队,按队列顺序执行各个命令。

PlotUISettings:一个单例,负责在进入和退出剧情时,对某些参数进行设置和清理;同时对外暴露界面的像素大小、打字效果的速度等可供用户调整的参数。

PlotModule:一个单例,用于存放各个Event,并对外提供各类方法,专门负责来自模块外的交互。

以下是各个命令类,均继承自CommandBase。

类名 可自定义的字段 该命令何时结束 注释
HEADER 章节名、该段剧情是否可跳过 立刻 负责进入剧情时的补间动画
Background 背景图片 立刻 无
Delay 需要等待的时间 等待自定义时间后 无
Dialogue 说话者的名字、说话的内容 打字效果结束且用户点击后 无
Character 说话者的图片 立刻 最多支持显示两个立绘
Decision 用户可选择的对话选项 用户选择一个对话选项后 最多支持五个剧情选项
Predicate 标志位,作为用户所选项对应的命令段的首位 立刻 最多支持五个剧情选项
END 标志位,作为整个剧情的出口 永不 负责退出剧情时的补间动画

如何使用

初始化、开始和结束

plot_module.PlotModule是专门供外部调用方法的类。

该系统初始化,仅需全局调用一次plot_command_executor.PlotModule.Instance.PlotInit(GameObject ui_prefab))即可,界面会持久存在。

该类还提供了两个UnityEvent:

plot_module.PlotModule.Instance.plotBegin;
plot_module.PlotModule.Instance.plotEnd;

当你想进入某段剧情时,首先确保已经初始化了,并且通过SetPlotConfig设置好了剧情配置文件。然后调用plotBegin即可开始剧情。

当你想在剧情结束时执行某些回调,你可以向plotEnd注册它们,在剧情界面完全退出后会自动调用。

获取玩家的选择

在PlotModule中提供了GetPlayerDecisions方法,返回一个List<int>,第i位上的数x意为玩家在第i次选择了第X个选项(均从0开始)。你可以在剧情结束后的任意时间获取它,它总是返回最近一次发生剧情的抉择。

配置剧情文件

HEADER,负责配置章节标题,同时在代码中负责进入剧情时的补间动画。

Background,负责配置剧情界面最底部的背景图片。注意:图片要放在Asset/Resource文件夹下,暂时不能自定义存放路径,这个以后改进。

Delay,等待自定义时间。由于补间动画的命令是立刻执行完的,而补间动画需要一定时间播放,由此产生的时间差就由它来填补。你也可以选择在合适的位置加入或不加入此命令,以实现对应效果。

Dialogue,没什么好说的。唯一要注意的是:人名和文本不要太长,人名在15个汉字以内,文本大概在70个汉字左右,不然会溢出屏幕。

Character,在两个立绘都不为空时,要使用focus字段来选择高亮显示哪个立绘。注意:图片要放在Asset/Resource文件夹下,暂时不能自定义存放路径,这个以后改进。

Decision,文本不要太长,在15个汉字以内即可,太长了后面的文字会被隐藏。

Predicate,拦截Decision的选择。一旦玩家选择了某选项,则会使命令队列不断出队,直至检测到出队的元素为对应选项的Predicate或是PredicateEnd。也正是这个原因,该系统暂时不支持循环式的选择。这一点可能以后改进。

END,意味着整个剧情的结束。一段剧情可以有多个END出口。

尾声

显然,这个系统还不够完善,比如:没有和镜头控制、屏幕效果、音频播放相关的命令,不支持循环式对话,没有对抉择进行细分等。但时间紧迫,短期内不会再对这个系统进行修改了。

posted @ 2023-02-01 21:23  AshScops  阅读(1066)  评论(2)    收藏  举报