[toc] 前序:
技能编辑器主要是用来编辑一个行为过程中触发的事件,而这些行为本质上是存在相互依赖的关系的,比如: A 准备攻击 B, 前摇阶段被 B 打断
A 离开攻击行为,进入被击行为。所以,实际上角色的每个行为本身都可以抽象成为一个状态,行为之间的转移就可以抽象为状态转移
而时间轴则可以给状态加入时间 (帧) 的概念,当前状态的每个时间点 (帧) 会发生什么事,发生的事情会转换到哪个状态… 即事实上的技能编辑器本意应该是专注在技能释放过程中的行为的有限状态机
当然,也可以直接用蓝图来做,但是由于技能的时间段特征,蓝图需要自己去计算时间点发生什么事,这就很麻烦了.
所以最好的方法应该是:
技能编辑器编辑时间轴与触发的行为 -> 蓝图用来处理行为状态间的转移 (不关心行为实际的表现)-> 统一借助中间数据来达成双端显示同步

# 1. 如何实现可以弹出的多窗体

设计图如下

主窗体绘制部分

  1. 首先需要将窗体的行为和属性抽象成一个中间结构 IView
  2. 最开始的根窗口持有所有的编辑器 IView派生类 ,如 FrameListView/ActionListView 等
  3. 根窗体每帧 (OnGUI) 调用 所有子View的Draw方法
    > 3.1. 子 View 根据每次 OnGUI 数据,重写的 Draw 方法来进行实际的 Draw
  4. 在 OnGUI 时可以通过行为来更改 IView 里的数据

窗体弹出部分

  1. 窗体本身的绘制是依赖 IView
  2. 如果需要在其他窗体绘制,只需要把 IView 赋值给其他窗体,并由其他窗体来调用 IView 的 Draw 方法
  3. 如果需要适配,建议使用相对大小
  4. 如果想要支持弹出窗体还可以回到原窗体,只需要给 IView 加一个 isPop 的标记即可
    > 4.1 如果在 Pop 状态,生成一个新的 EditorWindow, 把 IView 的 Draw 控制权交给新的 Window
    > 4.2 需要注意的一点是,如果有全局 Context, 则子 View 无论是否弹出,数据的更新一定是要在根窗口的 Context 上更新

# 2. 两个主要界面的介绍

# 2.1 状态列表 (StateListView)

如上图,其中每个 State 在 表现上是一个Button , 在存储上是 List中的一个StateConfig
每次在 StateListView 调用 Draw 方法的时候,同时绘制 List<StateConfig> , 通过得到每个 StateConfig 中存储的 StateName 来显示 Button 的描述,点击的时候更改当前 StateListView 的选中状态

# 2.2 帧列表 (FrameListView)

这个帧序列需要支持:
1. 实时在场景中查看动画的状态
2. 将动画在时间序列上拆成自己指定的帧数
3. 每帧触发的事件需要在帧上进行编辑

提供一个动作列表,可以在一帧上添加很多个动作

# 2.2.1 实现

帧 View 存储一个 List<FrameConfig>
对于每个 FrameConfig, 和上述状态一样,在 View 中以 Button 的形式表现出来 但是同理, 技能编辑器 只负责更改数据,实时的表现同步需要借助 编写一个读取编辑器数据,并且转换成显示 的工具。

# 2.2.2 SceneGUIDrawer 场景绘制

除开 WindowEditor 的 OnGui 外,Unity 的 Scene 也提供了 GUIDraw 的方法 SceneView.duringSceneGui , 这是个委托,需要将自己的 OnSceneGui 方法注册进去
可以通过每帧检查当前选中的帧下标来更新动画,
也可以直接在 OnGui 的 Draw 之后更新动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void OnGui(){
Draw();
UpdateAnimation();
}

private void UpdateAnimation()
{
AnimationClip clip = GetCurrentAnimationClip();
int frameIndex = frameSelectIndex;
if(clip == null frameIndex < 0)
{
return;
}
float time = frameIndex * setting.frameRate;
var state = currentState;

Animator animator = GetAnimator();
clip.SampleAnimation(animator.gameObject, time);
}