更新日期:2020年2月14日。 Github源码:[点我获取源码] Gitee源码:[点我获取源码]
索引
- TaskEditor简介
- 使用TaskEditor
- 创建Task Content Asset
- 打开TaskEditor窗口
- TaskEditor窗口详解
- 任务内容
- 任务内容属性详解
- 新建任务内容类型
- 新建任务内容
- 任务点
- 任务点属性详解
- 新建任务点类型
- 新建任务点
- 任务点完成
- 任务点依赖
- 在代码中总控
- 运行时检视面板
TaskEditor简介
任务编辑器,可以自定义任务点,设置任务达成条件,多个任务点组成一个任务内容,使用一系列任务内容完成角色扮演的设计。
使用TaskEditor
创建Task Content Asset
Task Content Asset(任务内容资源)为TaskEditor使用的标准资源,创建方法:Project界面右键 -> Create -> HTFramework -> TaskContentAsset
如下,新创建的Task001,点击Open按钮(或双击资源)便可以打开TaskEditor窗口编辑此资源:
打开TaskEditor窗口
打开任意一个TaskContentAsset资源后,TaskEditor初始窗口如下图:
TaskEditor窗口详解
①.Task Content List(任务内容列表)
1.这里显示所有的任务内容,点击右上角的+按钮可以新增任务内容,或者创建新的任务内容类型。
2.选中任意一个任务内容后,点击右端的铅笔按钮可以打开并编辑此任务内容的脚本,点击垃圾桶按钮可以删除此任务内容。
②.Task Property(任务内容属性) 1.选中任意一个任务内容后,这里显示该任务内容的属性。
③.Task Point Area(任务点显示区域) 1.选中任意一个任务内容后,这里显示该任务内容的所有任务点。 2.右键点击可以新增任务点,或者创建新的任务点类型。
任务内容
任务内容属性详解
当在①面板中选中一个任务内容时,②面板则显示此任务内容的属性:
1.ID:任务内容唯一标识符,不能重复(默认会自动累加,永不重复,前提是你不手动修改)。
2.Name:任务内容名称简述。
3.Details:任务内容详述。
4.Target:任务内容默认自带的一个GameObject属性,可以通过拖拽关联至场景、预制体中的任意对象,拖拽后通过ID关联,在任何时候都能找到该目标。
不过,上述为TaskContentDefault默认任务内容,我们点击编辑脚本,可以看到里面什么也没有:
/// <summary> /// 默认的任务内容 /// </summary> [TaskContent("默认")] public sealed class TaskContentDefault : TaskContentBase { }新建任务内容类型
框架会自带TaskContentDefault这个默认任务内容类型,通过选择New Task Content Script选项启动创建向导来快捷创建新的任务内容类。
如下,我们新建一个任务内容类型TaskKill:
[TaskContent("击杀任务")]public class TaskKill : TaskContentBase{ protected override void OnStart() { base.OnStart(); } protected override void OnUpdate() { base.OnUpdate(); } protected override void OnComplete() { base.OnComplete(); }#if UNITY_EDITOR protected override int OnPropertyGUI() { int height = base.OnPropertyGUI(); GUILayout.BeginHorizontal(); GUILayout.Label("[新建任务内容]"); GUILayout.EndHorizontal(); height += 20; return height; }#endif}然后我们就可以直接在编辑面板创建该类型的任务内容:
新建任务内容
新建如下这样一个常见的击杀任务:
通过重写OnPropertyGUI方法可以扩展任务内容属性面板UI:
#if UNITY_EDITOR protected override int OnPropertyGUI() { int height = base.OnPropertyGUI(); return height; }#endifheight为延续属性面板高度的参数,每添加一行UI,理论上将height+=20最合适,如下,我们在TaskKill添加一个属性:
[TaskContent("击杀任务")]public class TaskKill : TaskContentBase{ //怪物地图标记 public int Sign; #if UNITY_EDITOR protected override int OnPropertyGUI() { int height = base.OnPropertyGUI(); GUILayout.BeginHorizontal(); GUILayout.Label("怪物地图标记:"); Sign = EditorGUILayout.IntField(Sign); GUILayout.EndHorizontal(); height += 20; return height; }#endif}查看属性面板的变化:
任务点
一个任务内容包含多个任务点,任务内容和任务点都有一个完成标记,默认情况下,任务内容的完成标记会在其所有任务点的完成标记均为true时为true。
任务点属性详解
1.ID:任务点唯一标识符,不能重复(默认会自动累加,永不重复,前提是你不手动修改)。
2.Name:任务点名称简述。
3.Details:任务点详述。
4.Target:任务点目标。
5.触发方式:此为默认任务点携带的属性。
6.指引时高亮目标:此为默认任务点携带的属性。
7.持续时间:此为默认任务点携带的属性。
如下,我们点击【铅笔】按钮,打开TaskPointDefault默认任务点:
[TaskPoint("默认")] public sealed class TaskPointDefault : TaskPointBase { [SerializeField] private TaskTrigger _trigger = TaskTrigger.MouseClick; [SerializeField] private bool _highlighting = true; [SerializeField] private float _duration = 0; private TaskTarget _target; protected override void OnStart() { base.OnStart(); if (GetTarget == null) { Log.Error("任务点 " + GetName + " 的目标为空,这是不被允许的!"); } else { _target = GetTarget.GetComponent<TaskTarget>(); } } protected override void OnUpdate() { base.OnUpdate(); switch (_trigger) { case TaskTrigger.MouseClick: if (Main.m_Input.GetButtonDown(InputButtonType.MouseLeft)) { if (Main.m_Controller.RayTargetObj && Main.m_Controller.RayTargetObj == GetTarget) { Complete(); } } break; case TaskTrigger.StateChange: if (_target != null && _target.State == TaskTargetState.Done) { Complete(); } break; } } protected override void OnGuide() { base.OnGuide(); if (_highlighting && GetTarget) { Collider collider = GetTarget.GetComponent<Collider>(); if (collider && collider.enabled) { switch (Main.m_TaskMaster.GuideHighlighting) { case MouseRay.HighlightingType.Normal: GetTarget.OpenHighLight(); break; case MouseRay.HighlightingType.Flash: GetTarget.OpenFlashHighLight(); break; case MouseRay.HighlightingType.Outline: GetTarget.OpenMeshOutline(); break; } } } } protected override IEnumerator OnComplete() { yield return base.OnComplete(); if (!_duration.Approximately(0)) { yield return YieldInstructioner.GetWaitForSeconds(_duration); } } /// <summary> /// 默认的任务点触发类型 /// </summary> public enum TaskTrigger { /// <summary> /// 鼠标点击目标触发 /// </summary> MouseClick, /// <summary> /// 目标状态变为Done时触发 /// </summary> StateChange }#if UNITY_EDITOR protected override int OnPropertyGUI() { int height = base.OnPropertyGUI(); GUILayout.BeginHorizontal(); GUILayout.Label("触发方式:", GUILayout.Width(90)); _trigger = (TaskTrigger)EditorGUILayout.EnumPopup(_trigger); GUILayout.EndHorizontal(); height += 20; GUILayout.BeginHorizontal(); GUILayout.Label("指引时高亮目标:", GUILayout.Width(90)); _highlighting = EditorGUILayout.Toggle(_highlighting); GUILayout.EndHorizontal(); height += 20; GUILayout.BeginHorizontal(); GUILayout.Label("持续时间:", GUILayout.Width(90)); _duration = EditorGUILayout.FloatField(_duration); GUILayout.EndHorizontal(); height += 20; return height; }#endif }新建任务点类型
框架会自带TaskPointDefault这个默认任务点类型,通过空白处点击右键,选择New Task Point Script选项启动创建向导来快捷创建新的任务点类。
如下,我们新建一个任务点类型TaskPointKill:
[TaskPoint("TaskPointKill")]public class TaskPointKill : TaskPointBase{ protected override void OnStart() { base.OnStart(); } protected override void OnUpdate() { base.OnUpdate(); } protected override void OnGuide() { base.OnGuide(); } protected override IEnumerator OnComplete() { yield return base.OnComplete(); } protected override void OnAutoComplete() { base.OnAutoComplete(); } protected override void OnEnd() { base.OnEnd(); }#if UNITY_EDITOR protected override int OnPropertyGUI() { int height = base.OnPropertyGUI(); GUILayout.BeginHorizontal(); GUILayout.Label("[新建任务点]"); GUILayout.EndHorizontal(); height += 20; return height; }#endif}然后我们就可以直接在编辑面板创建该类型的任务点:
新建任务点
我们新建如下两个任务点,用来细分任务内容:
不过,我们的任务点目前并不知道如何才算杀了黄蜂,所以我们要在TaskPointKill中写一些东西:
[TaskPoint("击杀任务点")]public class TaskPointKill : TaskPointBase{ //任务的击杀目标 public string KillTarget; //任务的击杀数量 public int KillNumber; protected override void OnUpdate() { base.OnUpdate(); if (KillPool.Target[KillTarget].KillNumber >= KillNumber) { Complete(); } } protected override IEnumerator OnComplete() { yield return base.OnComplete(); //等待1秒后再改变自身完成状态 yield return YieldInstructioner.GetWaitForSeconds(1); }#if UNITY_EDITOR public override int OnPropertyGUI() { int height = base.OnPropertyGUI(); GUILayout.BeginHorizontal(); GUILayout.Label("任务击杀目标:"); KillTarget = EditorGUILayout.TextField(KillTarget); GUILayout.EndHorizontal(); height += 20; GUILayout.BeginHorizontal(); GUILayout.Label("任务击杀数量:"); KillNumber = EditorGUILayout.IntField(KillNumber); GUILayout.EndHorizontal(); height += 20; return height; }#endif}查看属性面板的变化:
任务点完成
任务点的OnUpdate会每帧执行,当此任务内容激活,且此任务点激活时:
public override void OnUpdate() { base.OnUpdate(); if (KillPool.Target[KillTarget].KillNumber >= KillNumber) { Complete(); } }所以,在OnUpdate中判断合适的时机,调用Complete(),便是标记此任务点完成。
任务点依赖
对于如上的两个简单的任务点,不存在任何的依赖(连线),也就不存在任何的先后关系,先杀大黄蜂或是先杀小黄蜂都可以,只要两个任务点完成,整个任务内容就算完成。
但是,如果我们的要求是先杀3只小黄蜂,然后再杀2只大黄蜂,那么就要用到接下来的任务点依赖了。
注意:任务点杀死2只大黄蜂的左侧连接至任务点杀死3只小黄蜂的右侧,表示任务点杀死2只大黄蜂依赖于杀死3只小黄蜂,如果A依赖于B,则必须B任务点完成以后,A任务点才会激活。
注意:请不要在任务内容和任务点中定义不可序列化类型的字段,对于GameObject这个常用的类型,理论上他是可以序列化的,但我们的TaskContentAsset资源是全局的,并不针对某一个Prefab,所以GameObject在这里也是不可序列化类型。
注意:在这里可以使用TaskGameObject代替GameObject。
如下,定义一个TaskGameObject字段,在OnPropertyGUI中必须使用TaskGameObjectField才能画出该字段:
public TaskGameObject Target;#if UNITY_EDITOR public override int OnPropertyGUI() { int height = base.OnPropertyGUI(); GUILayout.BeginHorizontal(); GUILayout.Label("任务击杀目标:"); KillTarget = EditorGUILayout.TextField(KillTarget); GUILayout.EndHorizontal(); height += 20; GUILayout.BeginHorizontal(); GUILayout.Label("任务击杀数量:"); KillNumber = EditorGUILayout.IntField(KillNumber); GUILayout.EndHorizontal(); height += 20; TaskGameObject.DrawField(Target, "Target:", 50, Anchor.width); height += 20; return height; }#endif查看属性面板变化:
我们可以拖拽Scene中的任意GameObject到属性面板:
我们把鼠标放在TaskGameObject字段的名称(比如这里的Target)上,会显示该对象的GUID,任何时候,当对象丢失时,任务执行器会通过GUID找到他,只要场景中存在这个对象,不管他是名字改变了,还是层级改变了!只有点击后面的垃圾桶按钮才能彻底删除这个对象。
在代码中总控
1、首先,需要将TaskContentAsset资源指定给TaskMaster(静态指定和动态指定均可):
2、然后,开始整个Task流程:
//重新编译所有任务内容 Main.m_TaskMaster.RecompileTaskContent(); //任务流程开始 Main.m_TaskMaster.Begin();3、终止整个Task流程:
//任务流程终止 Main.m_TaskMaster.End();4、监听全局事件:
//任务流程开始事件 EventTaskBegin //任务流程结束事件 EventTaskEnd //任意任务内容激活事件 EventTaskContentStart //任意任务内容完成事件 EventTaskContentComplete //任意任务点激活事件 EventTaskPointStart //任意任务点完成事件 EventTaskPointComplete运行时检视面板
在编辑器中运行时将会出现运行时检视面板(Runtime Data),主要用以调试或数据监测,目前面板如下:
1.No Runtime Data!