索引
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;
}
#endif
height为延续属性面板高度的参数,每添加一行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!