This commit is contained in:
2024-10-16 00:03:41 +08:00
commit 897058435c
5033 changed files with 1009728 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
## version 1.3.9 更新内容
1.添加 OnDisable 时重置状态机的选项!
## version 1.3.8 更新内容
1.添加还原状态机参数的逻辑(当游戏物体被隐藏时)!
## version 1.3.7 更新内容
1.添加StateNode.nameHash字段!
## version 1.3.6 更新内容
1. 优化编辑器参数列表显示!
2. 添加参数不存在的警告提示!
## version 1.3.5 更新内容
1. 优化状态节点重命名逻辑!
## version 1.3.4 更新内容
1. 优化状态类型查询!
## version 1.3.3 更新内容
1. 优化状态机初始化耗时!
## version 1.3.2 更新内容
1. 优化编辑器模式下,删除参数的逻辑!
## version 1.3.1 更新内容
1. 调整FSMController初始化方式,适配编辑器模式未开启重新编译程序集的情况!
## version 1.3.0 更新内容
1. FSMState添加OnCreate回调
2. 优化状态编辑窗口的内容显示!
## version 1.2.9 更新内容
1. 修复在特定的情况下 状态退出仍调用了一帧Update的回调!
## version 1.2.8 更新内容
1. 优化编辑器中状态节点的显示
## version 1.2.7 更新内容
1. 优化状态切换
2. 优化条件是否满足的判断
## version 1.2.6 更新内容
1. 优化编辑器状态节点拖拽
2. 优化编辑器状态节点显示
## version 1.2.5 更新内容
1. 优化编辑器添加脚本逻辑
## version 1.2.2 更新内容
1. 删除无效的命名空间引用
## version 1.2.1 更新内容
1. 修改创建状态脚本名称的默认值
## version 1.2.0 更新内容
1. 修改添加脚本的方式
2. 添加子状态机功能
3. 过渡可设置多组条件
4. 修改空条件切换逻辑
5. 添加状态切换的事件
6. FSMState添加transform字段
7. 添加获取参数的方法
## version 1.1.8 更新内容
1. 优化FSMController.StartUpFSM代码防止重复启动!
## version 1.1.7 更新内容
1. 添加状态基类字段(lastState,nextState)
## version 1.1.6 更新内容
1. 优化手动启动状态机(FSMController.StartupFSM)的参数赋值
## version 1.1.5 更新内容
1. 修复状态切换错误的问题(参数改变时没有等待所有的条件状态更新完毕就执行了检测切换的方法,导致切换异常)
2. 修复运行时删除状态机导致编辑器报错
## version 1.1.4 更新内容
1. 优化编辑器Context的RuntimeFSMController获取
## version 1.1.3 更新内容
1. XFFSM 添加启动全局状态的功能(适用于没有具体游戏物体的状态管理,例如:游戏状态)
2. 修复删除默认状态会把AnyState设置为默认状态的bug
3. FSMController修改为可以配置多个状态配置文件
4. 添加双击状态配置文件打开State Machine Window窗口的功能
5. 空条件的过渡改为会自动过渡
6. 添加获取当前正在执行的状态数据(GetCurrentStateInfo)
7. SetTrigger改为会记录次数,调用几次就会触发几次,可通过ResetTrigger取消
## version 1.1.2 更新内容
1. 修复多层状态条件满足没有正常切换的bug!
## version 1.1.1 更新内容
1. 添加 参数类型 Trigger
## version 1.1.0 更新内容
1. 修复删除状态时没有删除对应过渡的bug!
## version 1.0.9 更新内容
1. 修复状态改变时,如果条件已经满足状态不会切换的bug!
## version 1.0.8 更新内容
1. 添加 FSMController.ShowInEditorWindow 字段,如果同一个游戏物体身上有多个FSMController组件,可用此字段来控制FSMEditorWindow窗口显示哪一个!
## version 1.0.7 更新内容
1. 添加 动态修改 FSMController.RuntimeFSMController 的功能
## version 1.0.6 更新内容
1. 添加 RuntimeFSMController中的保存方法 UNITY_EDITOR 预处理指令
## version 1.0.5 更新内容
1. 添加获取当前正在执行的状态逻辑脚本对象的属性 FSMController.CurrentFSMState
2. 添加状态机销毁时清理逻辑脚本对象
## version 1.0.4 更新内容
1. 修复编辑器过渡动画显示错误
2. 优化默认状态执行方式
## version 1.0.3 更新内容
1. 添加 设置默认状态功能 2.修复 状态类型获取失败
## version 1.0.2 更新内容
1. 修复 bug : 当选中状态 或 过渡时 点击 Inspector 面板会取消选中的问题
## version 1.0.1 更新内容
1. 添加参数层背景颜色 2.删除 MouseButton 的引用,使用数字代替 , 这个类在 2020 版本命名空间发生改变,导致引用出错
## version 1.0.0 更新内容
1. 添加 readme.txt

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1d6c79278f2af604e909cb11507aaf21
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
# FSMController
class in XFFSM
### 说明:
状态机控制器,可通过此类来修改状态机参数,获取当前状态等!
### 事件
| 名称 | 说明 |
| ----------- | ----------- |
| [onInitFinish](FSMController/onInitFinish.md) | 初始化完成的回调 |
| [onStateChange](FSMController/onStateChange.md) | 状态改变的回调 参数1:状态机名称 参数2:当前状态 |
### 属性
| 名称 | 说明 |
| ----------- | ----------- |
| [RuntimeFSMController](FSMController/RuntimeFSMController.md) | 当前状态机执行的状态配置文件列表 |
| Initialized | 状态机是否初始化完成 |
### 方法
| 名称 | 说明 |
| ----------- | ----------- |
| [SetBool](FSMController/SetBool.md) | 设置bool类型参数的值 |
| [SetFloat](FSMController/SetFloat.md) | 设置float类型参数的值 |
| [SetInt](FSMController/SetInt.md) | 设置Int类型参数的值 |
| [SetTrigger](FSMController/SetTrigger.md) | 触发Trigger |
| [GetBool](FSMController/GetBool.md) | 获取Bool类型参数的值 |
| [GetFloat](FSMController/GetFloat.md) | 获取Float类型参数的值 |
| [GetInt](FSMController/GetInt.md) | 获取Int类型参数的值 |
| [GetTrigger](FSMController/GetTrigger.md) | 获取Trigger类型参数的值 |
| [ResetTrigger](FSMController/ResetTrigger.md) | 还原Trigger |
| [AddRuntimeFSMController](FSMController/AddRuntimeFSMController.md) | 运行时添加并执行状态配置文件 |
| [RemoveRuntimeFSMController](FSMController/RemoveRuntimeFSMController.md) | 运行时移除状态配置文件 |
| [GetCurrentStateInfo](FSMController/GetCurrentStateInfo.md) | 获取当前正在执行的状态信息 |
| [GetCurrentTransition](FSMController/GetCurrentTransition.md) | 获取当前的状态切换信息 |
### 静态方法
| 名称 | 说明 |
| ----------- | ----------- |
| [StartupFSM](FSMController/StartupFSM.md) | 启动状态(适用于没有具体游戏物体的状态管理,例如:游戏状态) |
| [GetFSM](FSMController/GetFSM.md) | 查询通过FSMController.StartupFSM启动的状态机 |
| [RemoveFSM](FSMController/RemoveFSM.md) | 移除通过FSMController.StartupFSM启动的状态机 |

View File

@@ -0,0 +1,40 @@
# FSMController.AddRuntimeFSMController
### 方法:
public void AddRuntimeFSMController(RuntimeFSMController controller);
### 说明:
运行时添加并执行状态配置文件
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| controller | 状态配置文件 |
### 代码示例:
> ```none
>
>using UnityEngine;
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 加载状态配置文件(这里只是示例,请根据自己的需求使用合适的方式加载)
> RuntimeFSMController fsmController = Resources.Load<RuntimeFSMController>("test_state");
> // 添加并执行状态配置
> controller.AddRuntimeFSMController(fsmController);
> }
>}
> ```

View File

@@ -0,0 +1,34 @@
# FSMController.GetBool
### 方法:
public bool GetBool(string name);
### 说明:
获取bool类型参数的值
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 获取New Bool参数的值
> controller.GetBool("New Bool");
> }
>}
>
> ```

View File

@@ -0,0 +1,61 @@
# FSMController.GetCurrentStateInfo
### 方法:
public FSMStateNode GetCurrentStateInfo(int index,string subStateName = "");
### 说明:
获取当前正在执行的状态信息
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| index | 状态配置下标 |
| subStateName | 子状态机名称,默认为空,如果不是子状态机填空即可! |
### 返回值
类型 : **FSMStateNode** [详细信息](/Documentation~/ClassApi/FSMStateNode.md)
### 代码示例:
> ```none
>
>using UnityEngine;
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 获取下标为0的状态配置当前正在执行的状态的信息
> FSMStateNode info = controller.GetCurrentStateInfo(0);
> Debug.Log(info.data.name);
> }
>}
> ```
### 重载方法:
public FSMStateNode GetCurrentStateInfo(string controllerName,string subStateName = "");
### 说明:
获取当前正在执行的状态信息
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| controllerName | 状态配置文件名称 |
| subStateName | 子状态机名称,默认为空,如果不是子状态机填空即可! |

View File

@@ -0,0 +1,44 @@
# FSMController.GetCurrentTransition
### 方法:
public FSMTransition GetCurrentTransition(int index);
### 说明:
获取当前的状态切换信息
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| index | 状态配置下标 |
### 返回值
类型 : **FSMTransition** [详细信息](/Documentation~/ClassApi/FSMTransition.md)
### 代码示例:
> ```none
>
>using UnityEngine;
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 获取下表为0的状态配置的状态切换的信息
> FSMTransition info = controller.GetCurrentTransition(0);
> Debug.LogFormat("开始状态:{0} 目标状态:{1}",info.Data.fromStateName,info.Data.toStateName);
> }
>}
> ```

View File

@@ -0,0 +1,39 @@
# FSMController.GetFSM
### 方法:
public static FSMController GetFSM(string fsmName);
### 说明:
查询通过FSMController.StartupFSM启动的状态机
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| fsmName | 状态机名称 |
### 返回值
类型 : **FSMController** [详细信息](/Documentation~/ClassApi/FSMController.md)
### 代码示例:
> ```none
>
>using UnityEngine;
>
>public class TestFSMController : MonoBehaviour
>{
> void Start()
> {
> // 查询GameState
> FSMController controller = FSMController.GetFSM("GameState");
> // TODO
> }
>}
> ```

View File

@@ -0,0 +1,38 @@
# FSMController.GetFloat
### 方法:
public float GetFloat(string name );
### 说明:
获取float类型参数的值
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 获取New Float参数的值
> controller.GetFloat("New Float");
> }
>}
>
> ```

View File

@@ -0,0 +1,37 @@
# FSMController.GetInt
### 方法:
public int GetInt(string name);
### 说明:
获取Int类型参数的值
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 获取New Int参数的值
> controller.GetInt("New Int");
> }
>}
>
> ```

View File

@@ -0,0 +1,35 @@
# FSMController.GetTrigger
### 方法:
public bool GetTrigger(string name);
### 说明:
获取Trigger类型参数的值
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 获取Trigger New Trigger 的值
> controller.GetTrigger("New Trigger");
> }
>}
>
> ```

View File

@@ -0,0 +1,35 @@
# FSMController.RemoveFSM
### 方法:
public static void RemoveFSM(string fsmName);
### 说明:
移除通过FSMController.StartupFSM启动的状态机
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| fsmName | 状态机名称 |
### 代码示例:
> ```none
>
>using UnityEngine;
>
>public class TestFSMController : MonoBehaviour
>{
> void Start()
> {
> // 移除状态机GameState
> FSMController.RemoveFSM("GameState");
> }
>}
> ```

View File

@@ -0,0 +1,38 @@
# FSMController.RemoveRuntimeFSMController
### 方法:
public void RemoveRuntimeFSMController(int index);
### 说明:
运行时移除状态机配置文件
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| index | 配置文件下标 |
### 代码示例:
> ```none
>
>using UnityEngine;
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 移除状态配置
> controller.RemoveRuntimeFSMController(0);
> }
>}
> ```

View File

@@ -0,0 +1,37 @@
# FSMController.ResetTrigger
### 方法:
public void ResetTrigger(string name);
### 说明:
还原Trigger
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 还原Trigger New Trigger
> controller.ResetTrigger("New Trigger");
> }
>}
>
> ```

View File

@@ -0,0 +1,35 @@
# FSMController.RuntimeFSMController
### 属性:
public List<*RuntimeFSMController*> RuntimeFSMController;
### 说明:
当前状态机执行的状态配置文件列表
可在下图中配置:
![](textures/RuntimeFSMController.jpg)
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> // Start is called before the first frame update
> void Start()
> {
> controller = GetComponent<FSMController>();
> foreach (var item in controller.RuntimeFSMController)
> {
> Debug.LogFormat("状态配置名称:{0}", item.name);
> }
> }
>}
>
> ```

View File

@@ -0,0 +1,45 @@
# FSMController.SetBool
### 方法:
public void SetBool(string name, bool v);
### 说明:
设置bool类型参数的值
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
| v | 值 |
bool 类型参数:
![](textures/SetBool1.png)
![](textures/SetBool2.jpg)
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 设置New Bool参数为false
> controller.SetBool("New Bool", false);
> }
>}
>
> ```

View File

@@ -0,0 +1,45 @@
# FSMController.SetFloat
### 方法:
public void SetFloat(string name, float v);
### 说明:
设置float类型参数的值
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
| v | 值 |
float 类型参数:
![](textures/SetFloat1.jpg)
![](textures/SetFloat2.jpg)
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 设置New Float参数的值为1.0
> controller.SetFloat("New Float", 1.0f);
> }
>}
>
> ```

View File

@@ -0,0 +1,45 @@
# FSMController.SetInt
### 方法:
public void SetInt(string name, int v);
### 说明:
设置Int类型参数的值
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
| v | 值 |
int 类型参数:
![](textures/SetInt1.jpg)
![](textures/SetInt2.jpg)
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 设置New Int参数的值为1
> controller.SetInt("New Int", 1);
> }
>}
>
> ```

View File

@@ -0,0 +1,44 @@
# FSMController.SetTrigger
### 方法:
public void SetTrigger(string name);
### 说明:
触发Trigger
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| name | 参数名称 |
trigger 类型参数:
![](textures/SetTrigger1.jpg)
![](textures/SetTrigger2.jpg)
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> void Start()
> {
> controller = GetComponent<FSMController>();
> // 触发Trigger New Trigger
> controller.SetTrigger("New Trigger");
> }
>}
>
> ```

View File

@@ -0,0 +1,43 @@
# FSMController.StartupFSM
### 方法:
public static FSMController StartupFSM(string fsmName,RuntimeFSMController fsm,object userData = null);
### 说明:
启动状态(适用于没有具体游戏物体的状态管理,例如:游戏状态)
### 参数
| 名称 | 说明 |
| ----------- | ----------- |
| fsmName | 状态机名称 |
| fsm | 状态配置文件 |
| userData | 自定义参数或数据 |
### 返回值
类型 : **FSMController** [详细信息](/Documentation~/ClassApi/FSMController.md)
### 代码示例:
> ```none
>
>using UnityEngine;
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
> void Start()
> {
> // 加载状态配置文件(这里只是示例,请根据自己的需求使用合适的方式加载)
> RuntimeFSMController gameFSM = Resources.Load<RuntimeFSMController>("game_state");
> // 启动状态配置文件
> FSMController.StartupFSM("GameState",gameFSM);
> }
>}
> ```

View File

@@ -0,0 +1,35 @@
# FSMController.onInitFinish
### 事件:
public Action onInitFinish;
### 说明:
当状态机初始化完成的时候会触发该事件
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> private void Start()
> {
>
> if (!controller.Initialized)
> {
> // 状态机正在初始化中...
> controller.onInitFinish += () =>
> {
> Debug.Log("状态机初始化完成!");
> };
> }
>
> }
>}
>
> ```

View File

@@ -0,0 +1,29 @@
# FSMController.onStateChange
### 事件:
public Action onStateChange;
### 说明:
状态改变的回调 参数1:状态机名称 参数2:当前状态
### 代码示例:
> ```none
>
>public class TestFSMController : MonoBehaviour
>{
> private FSMController controller;
>
> private void Start()
> {
> controller.onStateChange += (stateMachineName,currentStateName) =>
> {
> Debug.LogFormat("状态改变 状态配置名称:{0} 当前状态名称:{1}",stateMachineName,currentStateName);
> };
> }
>}
>
> ```

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,75 @@
# FSMState
class in XFFSM
### 说明:
状态基类
### 字段
| 名称 | 说明 |
| ----------- | ----------- |
| controller | 状态控制器 |
| currentStateInfo | 当前状态信息 |
| userData | 自定义数据(通过FSMController.StartupFSM()启动状态机传递自定义数据) |
| lastState | 上一个状态的名称 |
| nextState | 将要切换的下一个状态的名称(该字段仅在状态将要退出(OnExit)时有值) |
### 属性
| transform | 状态控制器所在的Transform |
### 方法
| 名称 | 说明 |
| ----------- | ----------- |
| OnCreate| 当第一次进入该状态时调用,该方法仅调用一次(初始化相关操作推荐放该方法中) |
| OnEnter| 当状态进入时执行 |
| OnExit| 当状态退出时执行 |
| OnUpdate| 每帧执行 |
| OnFixedUpdate| FixedUpdate时执行 |
| OnLateUpdate| LateUpdate时执行 |
### 代码示例
> ```none
>
>using UnityEngine;
>
>public class TestState1 : FSMState
>{
> private Player player;
> public override void OnEnter()
> {
> base.OnEnter();
> // 可以通过controller访问该游戏物体身上的其他组件,执行逻辑
> // Transform transform = controller.GetComponent<Transform>();
> }
>
> public override void OnExit()
> {
> base.OnExit();
> }
>
> public override void OnUpdate()
> {
> base.OnUpdate();
> if (player == null)
> player = controller.GetComponent<Player>();
> player.Rotate();
> }
>
> public override void OnFixedUpdate()
> {
> base.OnFixedUpdate();
> }
>
> public override void OnLateUpdate()
> {
> base.OnLateUpdate();
> }
>
>}
> ```

View File

@@ -0,0 +1,12 @@
# FSMStateNode
class in XFFSM
### 说明:
状态节点
### 字段
| 名称 | 说明 |
| ----------- | ----------- |
| data | 状态数据 |

View File

@@ -0,0 +1,20 @@
# FSMTransition
class in XFFSM
### 说明:
状态之间的过渡
### 字段
| 名称 | 说明 |
| ----------- | ----------- |
| conditions | 过渡所需的条件 |
| condition_groups | 过渡所需的新增组的条件 |
### 属性
| 名称 | 说明 |
| ----------- | ----------- |
| Data | 过渡数据 |
| ToState | 过渡目标状态 |

View File

@@ -0,0 +1,11 @@
# Runtime API
| 脚本 | 说明 |
| ----------- | ----------- |
| [FSMController](ClassApi/FSMController.md) | 状态机控制器,可通过此类来修改状态机参数,获取当前状态等! |
| [FSMState](ClassApi/FSMState.md) | 状态基类 |
| [FSMStateNode](ClassApi/FSMStateNode.md) | 状态节点 |
| [FSMTransition](ClassApi/FSMTransition.md) | 状态之间的过渡 |
# 如有疑问 或 遗漏请及时联系群主,qq交流群:644685781

View File

@@ -0,0 +1,94 @@
## XFFSM快速入门
### 功能介绍
管理游戏物体状态的插件!
### 插件特点
该插件能够可视化编辑状态,参数,状态与状态之间的过渡以及过渡的条件等,开发者只需要关心某一个状态所需要执行的逻辑即可!
### 创建状态配置
1. 在Project窗口点击鼠标右键(Create/XFKT/XFFSM/FSMController),如下图:
![](textures/quick_start/1.create_runtime_controller.jpg)
2. 输入名称
![](textures/quick_start/2.input_runtime_controller_name.jpg)
### 添加状态节点
1. 打开状态配置编辑窗口(Window/XFKT/XFFSM/State Machine Window)或者双击配置文件
![](textures/quick_start/3.open_state_machine_window.jpg)
2. 界面如下:
![](textures/quick_start/4.state_machine_window.jpg)
3. 创建状态节点
3.1 在状态区域点击鼠标右键
![](textures/quick_start/5.create_state.jpg)
![](textures/quick_start/6.state_node.jpg)
### 添加状态之间的过渡
1. 在状态节点上面点击鼠标右键,选择 Make Transition此时会出现一个箭头,将鼠标放到需要过渡的状态上点击左键即可!
![](textures/quick_start/7.add_transition.jpg)
![](textures/quick_start/8.add_transitiotn.jpg)
过渡添加完成之后,需要设置过渡所需要的条件,设置条件需要用到参数,所以我们要添加参数,具体如下!
### 添加参数
![](textures/quick_start/9.add_param.jpg)
![](textures/quick_start/10.add_param2.jpg)
### 添加过渡所需的条件
1. 点击需要设置条件的过渡,在Inspector面板添加条件
![](textures/quick_start/11.add_condition.jpg)
![](textures/quick_start/12.add_condition2.jpg)
条件设置完毕后就可以正常切换状态了,那怎样在某一个状态执行具体的逻辑呢?具体如下!
### 创建状态脚本
1. 在Project窗口点击鼠标右键(Create/XFKT/XFFSM/FSMState)创建状态脚本,如下图:
![](textures/quick_start/13.create_state.jpg)
![](textures/quick_start/14.input_state_name.jpg)
[FSMState介绍](ClassApi/FSMState.md)
### 将状态脚本添加到指定节点
![](textures/quick_start/15.add_state_scripts.jpg)
状态的配置到这里基本就结束了,接下来我们需要把状态配置赋值某个游戏物体,具体如下!
### 将状态配置赋值给指定游戏物体
1. 给游戏物体添加组件FSMController
![](textures/quick_start/17.add_fsmcontroller.jpg)
2. 将配置文件设置给FSMController
![](textures/quick_start/18.set_fsmcontroller.jpg)
[FSMController详细用法](ClassApi/FSMController.md)
### 如有疑问 或 遗漏请及时联系群主,qq交流群:644685781

View File

@@ -0,0 +1,11 @@
# FAQ
## 开启代码混淆后状态无法正常执行
该插件创建状态脚本对象是通过反射的方式创建的,假如您的项目使用了代码混淆,状态脚本对象可能会创建失败,
状态逻辑可能无法执行,如果出现这种情况,请不要混淆状态脚本!
## QQ交流群:1058692748 碰到问题请及时联系群主!

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a6716a78326a1b5419d9b5fd4e284b7d
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a8a72409c0017b748a459efc3d962992
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2cac4426da733bb40a850dae7d95f06c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ceea6a6df55d26240b403484dec2606f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,76 @@
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.ProjectWindowCallback;
using UnityEngine;
namespace XFFSM
{
public class FSMStateCreator : EndNameEditAction
{
static string regex = "^[a-zA-Z][a-zA-Z0-9_]*$";
public override void Action(int instanceId, string pathName, string resourceFile)
{
CreateFSMState(pathName);
}
internal static bool CreateFSMState(string pathName,bool errorTip = true)
{
string fileName = Path.GetFileNameWithoutExtension(pathName);
// 判断文件是否已经存在 如果已经存在则不能创建
string[] guids = AssetDatabase.FindAssets(fileName);
foreach (var item in guids)
{
string asset_path = AssetDatabase.GUIDToAssetPath(item);
MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(asset_path);
if (script == null || script.GetClass() == null) continue;
if (script.GetClass().Name.Equals(fileName))
{
if (errorTip)
{
string message = string.Format("脚本名称:{0}已经存在!", fileName);
//FSMEditorWindow.ShowNotification(string.Format("脚本名称:{0}已经存在!", fileName));
EditorUtility.DisplayDialog("提示", message, "确定");
}
return false;
}
}
if (!Regex.Match(fileName, regex).Success)
{
EditorUtility.DisplayDialog("提示", string.Format("文件名:{0}不可用!", fileName), "确定");
return false;
}
TextAsset template = AssetDatabase.LoadAssetAtPath<TextAsset>("Assets/Editor/Template/FSMStateTemplate.txt");
if (template == null)
{
// Packages/com.xfkj.xffsm/Editor/Texture/logo_web.png
template = AssetDatabase.LoadAssetAtPath<TextAsset>("Packages/com.xfkj.xffsm/Editor/Template/FSMStateTemplate.txt");
}
string content = template.text;
content = content.Replace("{0}", fileName);
FileStream stream = File.Create(pathName);
stream.Write(Encoding.UTF8.GetBytes(content), 0, content.Length);
stream.Close();
//stream.Write()
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 921d2044bfcd3fa42a8426b5f080c71c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using UnityEditor;
using UnityEditor.ProjectWindowCallback;
using UnityEngine;
namespace XFFSM
{
public class RuntimeFSMControllerCreator : EndNameEditAction
{
public override void Action(int instanceId, string pathName, string resourceFile)
{
RuntimeFSMController runtimeFSM = ScriptableObject.CreateInstance<RuntimeFSMController>();
AssetDatabase.CreateAsset(runtimeFSM,pathName);
Selection.activeObject = runtimeFSM;
Rect rect = new Rect(0, 100, FSMConst.StateNodeWith, FSMConst.StateNodeHeight);
FSMStateNodeFactory.CreateStateNode(runtimeFSM, FSMConst.anyState, rect, false,false,null,true,FSMConst.anyState);
rect = new Rect(0, 300, FSMConst.StateNodeWith, FSMConst.StateNodeHeight);
FSMStateNodeFactory.CreateStateNode(runtimeFSM, FSMConst.entryState, rect, false, false,null, true, FSMConst.entryState);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fbb86492958a4b945bf86d27575d1dae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e9fb7308c8fe9b540bc65ec7cff8984e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
namespace XFFSM
{
public class FSMConditionFactory
{
public static FSMConditionData CreateCondition(RuntimeFSMController controller) {
FSMConditionData condition = new FSMConditionData();
string parameterName = string.Empty;
FSMParameterData parameter = null;
if (controller.parameters.Count > 0)
{
parameter = controller.parameters[0];
parameterName = parameter.name;
}
if (parameter != null)
{
switch (parameter.parameterType)
{
case ParameterType.Float:
condition.compareType = CompareType.Greater;
break;
case ParameterType.Int:
condition.compareType = CompareType.Greater;
break;
case ParameterType.Bool:
condition.compareType = CompareType.Equal;
break;
}
}
else
{
condition.compareType = CompareType.Greater;
}
condition.targetValue = 0;
condition.parameterName = parameterName;
return condition;
}
// 创建条件
public static void CreateCondition(RuntimeFSMController controller,FSMTransitionData transition) {
FSMConditionData condition = CreateCondition(controller);
transition.conditions.Add(condition);
UnityEditor.EditorUtility.SetDirty(controller);
UnityEditor.AssetDatabase.SaveAssets();
}
public static void DeleteCondition(RuntimeFSMController controller, FSMTransitionData transition,int index) {
if ( index < 0 || index >= transition.conditions.Count ) {
return;
}
transition.conditions.RemoveAt(index);
UnityEditor.EditorUtility.SetDirty(controller);
UnityEditor.AssetDatabase.SaveAssets();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f55b27a4a6f3fcd428fbb8944fa14f2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,149 @@
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace XFFSM
{
public class FSMParamterFactory
{
// 创建参数
public static void CreateParamter(RuntimeFSMController controller,ParameterType type) {
FSMParameterData parameter = new FSMParameterData();
parameter.name = GetDefualtName(controller, type);
parameter.parameterType = type;
parameter.value = 0;
controller.AddParameters(parameter);
}
private static string GetDefualtName(RuntimeFSMController controller,ParameterType type) {
string name = string.Format("New {0}", type.ToString());
string tempName = name;
int i = 1;
while (controller.GetParameterData(tempName) != null)
{
tempName = string.Format("{0}{1}", name, i);
i++;
}
return tempName;
}
// 删除参数
public static void RemoveParamter(RuntimeFSMController controller,int index) {
if (Application.isPlaying) return;
if (controller == null) return;
FSMParameterData parameter = controller.parameters[index];
List<FSMTransitionData> transitions = new List<FSMTransitionData>();
// 查询引用了这个参数的过渡
foreach (var item in controller.transitions)
{
foreach (var condition in item.conditions)
{
if (condition.parameterName!=null && condition.parameterName.Equals(parameter.name)) {
transitions.Add(item);
break;
}
}
}
if (transitions.Count == 0)
{
controller.RemoveParameters(parameter);
}
else {
StringBuilder content = new StringBuilder();
content.Append("确定删除参数:").Append(parameter.name).Append("吗?").Append("\n");
content.Append("有以下过渡引用此参数!\n");
foreach (var item in transitions)
{
content.Append(item.fromStateName).Append(" -> ").Append(item.toStateName);
}
if (UnityEditor.EditorUtility.DisplayDialog("删除参数",content.ToString(),"确定","取消")) {
controller.RemoveParameters(parameter);
foreach (var item in transitions)
{
for (int i = item.conditions.Count - 1; i >=0; i--)
{
FSMConditionData condition = item.conditions[i];
if (condition.parameterName != null && condition.parameterName.Equals(parameter.name))
{
item.conditions.RemoveAt(i);
}
}
}
}
}
}
// 重命名参数
public static void RenameParamter(RuntimeFSMController controller,FSMParameterData parameter,string newName) {
// 判断名称是否为空
if (string.IsNullOrEmpty(newName))
{
FSMEditorWindow.ShowNotification("参数名称不能为空!");
Debug.LogError("参数名称不能为空!");
return;
}
// 判断新的名称是否已经存在
if (controller.GetParameterData(newName) != null) {
FSMEditorWindow.ShowNotification("参数名称已经存在!");
Debug.LogError("参数名称已经存在!");
return;
}
// 查找到所有引用此参数的过渡 修改名称
foreach (var item in controller.transitions)
{
// 遍历所有的条件
foreach (var condition in item.conditions)
{
if (condition.parameterName!=null && condition.parameterName.Equals(parameter.name)) {
condition.parameterName = newName;
}
}
// 遍历其他组的条件
foreach (var group in item.group_conditions)
{
foreach (var condition in group.conditions)
{
if (condition.parameterName != null && condition.parameterName.Equals(parameter.name))
{
condition.parameterName = newName;
}
}
}
}
// 修改参数名称
parameter.name = newName;
controller.ClearCache();
controller.Save();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 897bc9b25a26839409ec026d49a9b6ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,191 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace XFFSM
{
public class FSMStateNodeFactory
{
public static FSMStateNodeData CreateStateNode(RuntimeFSMController controller, string name, Rect rect, bool defaultState,bool isSubStateMachine = false,List<string> parent = null,bool isBuildInState = false,string buildInStateName = "")
{
// 判断名称是否重复
if (controller.GetStateNodeData(name) != null)
{
string message = string.Format("创建状态节点失败,名称:{0}重复!", name);
Debug.LogError(message);
FSMEditorWindow.ShowNotification(message);
return null;
}
FSMStateNodeData node = new FSMStateNodeData();
node.name = name;
node.rect = rect;
node.isSubStateMachine = isSubStateMachine;
node.parents = parent;
node.isBuildInState = isBuildInState;
node.buildInStateName = buildInStateName;
if (defaultState)
{
foreach (var item in controller.states)
{
// 把相同层级的默认状态设置成false
if (!item.CompareParent(node)) continue;
item.defaultState = false;
}
}
node.defaultState = defaultState;
controller.AddState(node);
AssetDatabase.SaveAssets();
return node;
}
public static FSMStateNodeData CreateStateNode(RuntimeFSMController controller, Rect rect, bool defaultState, bool isSubStateMachine = false, List<string> parent = null,bool isBuildInState = false, string buildInStateName = "") {
return CreateStateNode(controller,GetStateNodeName(controller),rect,defaultState,isSubStateMachine,parent, isBuildInState, buildInStateName);
}
private static string GetStateNodeName(RuntimeFSMController controller) {
string name = null;
int i = 1;
do
{
name = string.Format("New State{0}", i);
i++;
} while (controller.GetStateNodeData(name) != null);
return name;
}
public static void DeleteState(RuntimeFSMController controller,FSMStateNodeData state) {
// 判断 删除的是不是 entry 或者 any
if (state.IsAnyState || state.IsEntryState || state.IsUpState)
{
if (state.BaseLayer())
{
string message = string.Format("状态:{0}不能删除!", state.DisplayName);
Debug.LogWarning(message);
FSMEditorWindow.ShowNotification(message);
return;
}
}
// 删除相关的过渡
for (int i = 0; i < controller.transitions.Count; i++)
{
FSMTransitionData transitionData = controller.transitions[i];
if (transitionData.fromStateName.Equals(state.name) || transitionData.toStateName.Equals(state.name))
{
controller.RemoveTransition(transitionData);
i--;
}
}
controller.RemoveState(state);
// 判断是不是默认状态
if (state.defaultState) {
foreach (var item in controller.states)
{
if (item.IsAnyState || item.IsEntryState || item.IsUpState)
continue;
if (!item.CompareParent(state)) continue;
item.defaultState = true;
break;
}
}
// 判断是不是子状态机
if (state.isSubStateMachine) {
List<FSMStateNodeData> subStates = new List<FSMStateNodeData>();
foreach (var item in controller.states)
{
if (item.BelongToParent(state.name))
subStates.Add(item);
}
foreach (var item in subStates)
{
// 删除子状态
DeleteState(controller, item);
}
}
}
public static bool Rename(RuntimeFSMController controller,FSMStateNodeData node,string newName) {
if ( node.name.Equals(FSMConst.entryState) || node.name.Equals(FSMConst.anyState) ) {
return false;
}
if (string.IsNullOrEmpty(newName))
{
FSMEditorWindow.ShowNotification("名称不能为空!");
return false;
}
if ( controller.GetStateNodeData(newName) !=null )
{
string message = string.Format("状态重命名失败,名称:{0}已经存在,请使用其他的名称!", newName);
FSMEditorWindow.ShowNotification(message);
return false;
}
// 找到 相关的过渡 进行修改
foreach (var item in controller.transitions)
{
if (item.fromStateName.Equals(node.name)) {
item.fromStateName = newName;
}
if (item.toStateName.Equals(node.name)) {
item.toStateName = newName;
}
}
string oldName = node.name;
node.name = newName;
// 修改子状态层级
if (node.isSubStateMachine) {
// 查到所有包含该状态的状态
List<FSMStateNodeData> subStates = new List<FSMStateNodeData>();
foreach (var item in controller.states)
{
if (item.ContainsParent(oldName)) subStates.Add(item);
}
foreach (var item in subStates)
{
// 重命名
item.RenameParent(oldName, newName);
if (item.isBuildInState)
{
string itemNewName = string.Format("{0}/{1}",item.ParentPath,item.buildInStateName);
Rename(controller,item, itemNewName);
}
}
}
controller.ClearCache();
EditorUtility.SetDirty(controller);
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f4b272a7b6b47be4b9f4cbc29ee45bc9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using UnityEngine;
namespace XFFSM
{
public class FSMTransitionFactory
{
public static void CreateTransition(RuntimeFSMController controller,string fromStateName,string toStateName) {
FSMStateNodeData toNode = controller.GetStateNodeData(toStateName);
if (toNode.IsAnyState || toNode.IsEntryState || toNode.IsUpState )
{
string message = string.Format("状态:{0}不能添加过渡!", toNode.DisplayName);
Debug.LogWarning(message);
FSMEditorWindow.ShowNotification(message);
return;
}
if (fromStateName.Equals(toStateName)) {
return;
}
foreach (var item in controller.transitions)
{
if (item.fromStateName.Equals(fromStateName) && item.toStateName.Equals(toStateName) )
{
string message = string.Format("过渡 {0} -> {1} 已经存在,请勿重复添加!", fromStateName, toStateName);
Debug.LogError(message);
FSMEditorWindow.ShowNotification(message);
return;
}
}
FSMTransitionData transition = new FSMTransitionData();
transition.fromStateName = fromStateName;
transition.toStateName = toStateName;
controller.AddTransition(transition);
UnityEditor.EditorUtility.SetDirty(controller);
UnityEditor.AssetDatabase.SaveAssets();
}
public static void DeleteTransition(RuntimeFSMController controller,FSMTransitionData transition) {
controller.RemoveTransition(transition);
UnityEditor.EditorUtility.SetDirty(controller);
UnityEditor.AssetDatabase.SaveAssets();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c40b3be288bcec747ab41b63ee243e59
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e24a82ed9042ee54bb850af6391d08a2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,259 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace XFFSM
{
public class Context
{
#region
private RuntimeFSMController _runtimeFSMController;
public List<FSMStateNodeData> SelectNodes = new List<FSMStateNodeData>();
public FSMTransitionData selectTransition;
public bool isPreviewTransition = false;
public FSMStateNodeData fromState = null;
public FSMStateNodeData hoverState = null;
private FSMController _FSMController;
//private LayoutMatrix4x4Info _matrixInfo;
private static Context _instance;
//private List<FSMStateNodeData> current_show_state_node_data = new List<FSMStateNodeData>();
//private List<FSMTransitionData> current_show_transition_data = new List<FSMTransitionData>();
#endregion
#region Layers
private List<string> empty_layers = new List<string>();
public List<string> Layers
{
get {
if(RuntimeFSMController != null)
return RuntimeFSMController.Layers;
return empty_layers;
}
}
public string LayerParent
{
get
{
if (Layers.Count == 0)
return string.Empty;
return Layers[Layers.Count - 1];
}
}
public void AddLayer(string layer)
{
if (RuntimeFSMController == null) return;
RuntimeFSMController.AddLayer(layer);
}
public void RemoveLayer(int index, int count)
{
if (RuntimeFSMController == null) return;
RuntimeFSMController.RemoveLayer(index,count);
}
public void RemoveLast(string name)
{
if (RuntimeFSMController == null) return;
int index = RuntimeFSMController.Layers.Count - 1;
if (index >= 0 && index < RuntimeFSMController.Layers.Count)
{
string last = RuntimeFSMController.Layers[index];
if (!last.Equals(name))
return;
RuntimeFSMController.RemoveLayer(RuntimeFSMController.Layers.Count - 1, 1);
}
}
#endregion
#region
// 这个逻辑修改为如果只有一个,逻辑不变,
// 如果有多个,判断当前显示的是不是在多个中,如果在不做处理,如果不在设置为第一个
public RuntimeFSMController RuntimeFSMController {
get
{
if ( RuntimeFSMControllers != null && RuntimeFSMControllers.Count != 0)
{
if (FSMControllerIndex < 0 || FSMControllerIndex >= RuntimeFSMControllers.Count)
FSMControllerIndex = 0;
RuntimeFSMController controller = RuntimeFSMControllers[FSMControllerIndex];
if (controller != null && !string.IsNullOrEmpty(controller.originGUID) && _runtimeFSMControllerGUID != controller.originGUID)
{
PlayerPrefs.SetString("XFFSMRuntimeFSMControllerGUID", controller.originGUID);
_runtimeFSMControllerGUID = controller.originGUID;
//Debug.LogFormat("保存的GUID:{0}", _runtimeFSMControllerGUID);
}
return controller;
}
if (_runtimeFSMController == null)
{
string path = AssetDatabase.GUIDToAssetPath(RuntimeFSMControllerGUID);
_runtimeFSMController = AssetDatabase.LoadAssetAtPath<RuntimeFSMController>(path);
}
return _runtimeFSMController;
}
internal set
{
if (_runtimeFSMController == value) return;
_runtimeFSMController = value;
}
}
public FSMController FSMController {
get {
if (_FSMController == null)
{
GameObject gameObj = EditorUtility.InstanceIDToObject(FSMControllerInstanceID) as GameObject;
if (gameObj == null) return null;
_FSMController = gameObj.GetComponent<FSMController>();
}
return _FSMController;
}
}
private List<RuntimeFSMController> currentFSMControllers = new List<RuntimeFSMController> ();
public List<RuntimeFSMController> RuntimeFSMControllers
{
get
{
if (FSMController != null)
return FSMController.RuntimeFSMController;
currentFSMControllers.Clear();
if (_runtimeFSMController == null)
{
string path = AssetDatabase.GUIDToAssetPath(RuntimeFSMControllerGUID);
_runtimeFSMController = AssetDatabase.LoadAssetAtPath<RuntimeFSMController>(path);
}
if (_runtimeFSMController != null)
currentFSMControllers.Add(_runtimeFSMController);
return currentFSMControllers;
}
}
public static Context Instance
{
get {
if(_instance == null)
_instance = new Context();
return _instance;
}
}
internal int FSMControllerInstanceID
{
get
{
return PlayerPrefs.GetInt("XFFSMControllerInstanceID",0);
}
set {
PlayerPrefs.SetInt("XFFSMControllerInstanceID", value);
Object obj = EditorUtility.InstanceIDToObject(value);
GameObject gameObj = obj as GameObject;
if (gameObj != null && gameObj.GetComponent<FSMController>() != null) {
_FSMController = gameObj.GetComponent<FSMController>();
}
else
_FSMController = null;
}
}
private string _runtimeFSMControllerGUID;
internal string RuntimeFSMControllerGUID
{
get
{
return PlayerPrefs.GetString("XFFSMRuntimeFSMControllerGUID",string.Empty);
}
set
{
//Debug.LogFormat("修改GUID:{0}",value);
PlayerPrefs.SetString("XFFSMRuntimeFSMControllerGUID", value);
string path = AssetDatabase.GUIDToAssetPath(value);
RuntimeFSMController = AssetDatabase.LoadAssetAtPath<RuntimeFSMController>(path);
}
}
internal int FSMControllerIndex
{
get {
return PlayerPrefs.GetInt("FSMControllerIndex", 0);
}
set
{
if (FSMControllerIndex == value) return;
PlayerPrefs.SetInt("FSMControllerIndex", value);
}
}
#endregion
#region
private Context() {
}
public void ClearSelections()
{
// 如果不是 InspectorWindow 此时清空 Inspector
this.SelectNodes.Clear();
selectTransition = null;
Selection.activeObject = null;
//Selection.activeObject = this.RuntimeFSMController;
}
/// <summary>
/// 获取当前显示状态节点
/// </summary>
/// <returns></returns>
public List<FSMStateNodeData> GetCurrentShowStateNodeData() {
if (RuntimeFSMController != null)
return RuntimeFSMController.GetCurrentShowStateNodeData(LayerParent);
return null;
}
public List<FSMTransitionData> GetCurrentShowTransitionData()
{
if (RuntimeFSMController != null)
return RuntimeFSMController.GetCurrentShowTransitionData(LayerParent);
return null;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc4e8d1fd5489124bbd0e11d631762fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8c7fdc94ab675d543b15b12578676825
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
using UnityEditor;
using UnityEngine;
namespace XFFSM
{
public class FSMBoolConditionInspector : FSMConditionInspector
{
public override void OnGUI(Rect rect, FSMConditionData condition, RuntimeFSMController controller)
{
string text = condition.targetValue == 1 ? "True" : "False";
if (EditorGUI.DropdownButton(rect, new GUIContent(text), FocusType.Keyboard)) {
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("True"), condition.targetValue == 1, () => {
condition.targetValue = 1;
controller.Save();
});
//tempContent.text = "False";
menu.AddItem(new GUIContent("False"), condition.targetValue == 0, () => {
condition.targetValue = 0;
controller.Save();
});
menu.ShowAsContext();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce9f38bc4f6646845b15aa6f152bb7a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using UnityEngine;
namespace XFFSM
{
public class FSMConditionInspector
{
public virtual void OnGUI(Rect rect, FSMConditionData condtion, RuntimeFSMController controller) {
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 64ef6b93dc886854a899900b205641c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System;
using UnityEditor;
using UnityEngine;
namespace XFFSM
{
public class FSMFloatConditionInspector : FSMConditionInspector
{
private Rect leftRect;
private Rect rightRect;
//private GUIContent tempContent = new GUIContent();
public override void OnGUI(Rect rect, FSMConditionData condition, RuntimeFSMController controller)
{
leftRect.Set(rect.x, rect.y, rect.width / 2, rect.height);
rightRect.Set(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
//tempContent.text = condition.compareType.ToString();
if (EditorGUI.DropdownButton(leftRect, new GUIContent(condition.compareType.ToString()), FocusType.Keyboard)) {
GenericMenu menu = new GenericMenu();
for (int i = 0; i < Enum.GetValues(typeof(CompareType)).Length; i++)
{
CompareType v = (CompareType)Enum.GetValues(typeof(CompareType)).GetValue(i);
if (v == CompareType.Equal || v == CompareType.NotEqual) continue;
menu.AddItem(new GUIContent(v.ToString()), condition.compareType == v, () => {
condition.compareType = v;
controller.Save();
});
}
menu.ShowAsContext();
}
condition.targetValue = EditorGUI.FloatField(rightRect, condition.targetValue);
EditorUtility.SetDirty(controller);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0769ca1535aa530498501eeea743587f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using System;
using UnityEditor;
using UnityEngine;
namespace XFFSM
{
public class FSMIntConditionInspector : FSMConditionInspector
{
private Rect leftRect;
private Rect rightRect;
//private GUIContent tempContent = new GUIContent();
public override void OnGUI(Rect rect, FSMConditionData condition, RuntimeFSMController controller)
{
leftRect.Set(rect.x, rect.y, rect.width / 2, rect.height);
rightRect.Set(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
//tempContent.text = condition.compareType.ToString();
if (EditorGUI.DropdownButton(leftRect, new GUIContent(condition.compareType.ToString()), FocusType.Keyboard))
{
GenericMenu menu = new GenericMenu();
for (int i = 0; i < Enum.GetValues(typeof(CompareType)).Length; i++)
{
CompareType v = (CompareType)Enum.GetValues(typeof(CompareType)).GetValue(i);
//if (v == CompareType.Equal || v == CompareType.NotEqual) continue;
//tempContent.text = v.ToString();
menu.AddItem(new GUIContent(v.ToString()), condition.compareType == v, () => {
condition.compareType = v;
controller.Save();
});
}
menu.ShowAsContext();
}
condition.targetValue = EditorGUI.IntField(rightRect, (int)condition.targetValue);
EditorUtility.SetDirty(controller);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a227da536ccef04d96788e51bad55fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,266 @@
using System;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace XFFSM
{
[CustomEditor(typeof(FSMStateInspectorHelper))]
public class FSMStateInspector : Editor
{
//private string stateName;
private ReorderableList reorderableList;
private Rect popupRect;
private GUIContent btn_add_state_script = new GUIContent("Add State Script");
private GUIStyle ProjectBrowserHeaderBgMiddle = null;
private GUIStyle DD_HeaderStyle = null;
private GUIStyle PrefixLabel = null;
private GUIContent script_gui_content = new GUIContent();
private Vector2 scroll;
private void OnEnable()
{
FSMStateInspectorHelper helper = target as FSMStateInspectorHelper;
if (helper == null) { return; }
//reorderableList = new ReorderableList(helper.node.StateScripts, typeof(FSMStateScriptInfo), true, true, true, true);
//reorderableList.drawHeaderCallback += OnDrawHeaderCallback;
//reorderableList.onAddCallback += this.OnAddCallback;
//reorderableList.onRemoveCallback += this.OnRemoveCallback;
//reorderableList.drawElementCallback += this.DrawItem;
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
if (ProjectBrowserHeaderBgMiddle == null)
ProjectBrowserHeaderBgMiddle = new GUIStyle("AC BoldHeader");
if (DD_HeaderStyle == null)
{
DD_HeaderStyle = new GUIStyle("IconButton");
}
if (PrefixLabel == null)
{
PrefixLabel = new GUIStyle("PrefixLabel");
PrefixLabel.richText = true;
}
FSMStateInspectorHelper helper = target as FSMStateInspectorHelper;
if (helper == null) return;
bool disabled = EditorApplication.isPlaying || helper.node.IsAnyState || helper.node.IsEntryState || helper.node.IsUpState;
EditorGUI.BeginDisabledGroup(disabled);
//GUILayout.Space(-10);
//scroll = GUILayout.BeginScrollView(scroll);
Vector2 mousePosition = Event.current.mousePosition;
foreach (var item in helper.node.StateScripts)
{
// 刷新一下
if (string.IsNullOrEmpty(item.guid) && !string.IsNullOrEmpty(item.className))
helper.node.RefreshStateScripts(helper.controller);
// 根据guid加载到脚本信息
string path = AssetDatabase.GUIDToAssetPath(item.guid);
MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
if (script == null) continue;
Type type = script.GetClass();
if (type == null) continue;
var r = EditorGUILayout.BeginHorizontal(GUILayout.Height(25));
r.x = 0;
r.width += 30;
GUI.Box(r, string.Empty, ProjectBrowserHeaderBgMiddle);
GUILayout.Space(-10);
GUILayout.Label(EditorGUIUtility.IconContent("d_cs Script Icon"), GUILayout.Width(20), GUILayout.Height(20));
string displayName = string.Empty;
if (type.IsSubclassOf(typeof(FSMState)))
{
displayName = type.Name;
script_gui_content.tooltip = string.Empty;
}
else
{
displayName = string.Format("{0}<color=yellow>(Script Missing)</color>", type.Name);
script_gui_content.tooltip = "脚本丢失,请检查该脚本是否继承自FSMState!";
}
script_gui_content.text = displayName;
GUILayout.Label(script_gui_content, PrefixLabel, GUILayout.Height(20));
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
GUILayout.Space(5);
if ( GUILayout.Button(EditorGUIUtility.IconContent("d__Menu"), DD_HeaderStyle, GUILayout.Width(25), GUILayout.Height(25)))
{
ShowMenu(script);
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
if ( Event.current.type == EventType.MouseUp && Event.current.button == 1 && r.Contains(mousePosition))
{
ShowMenu(script);
Event.current.Use();
}
if ( Event.current.type == EventType.MouseUp && Event.current.button == 0 && r.Contains(mousePosition)) {
EditorGUIUtility.PingObject(script);
Event.current.Use();
}
}
GUILayout.Space(30);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(btn_add_state_script, GUILayout.Width(260), GUILayout.Height(25)))
{
popupRect.height = 300;
popupRect.y -= popupRect.height;
if (popupRect.y > 300)
popupRect.y -= popupRect.height + 25;
PopupWindow.Show(popupRect, new FSMSelectStateWindow(popupRect, helper.controller, helper.node));
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
var rect = GUILayoutUtility.GetRect(255, 0);
if (rect.width > 10)
popupRect = rect;
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
if(helper.controller != null)
helper.controller.Save();
//GUILayout.EndScrollView();
EditorGUI.EndDisabledGroup();
}
protected override void OnHeaderGUI()
{
//base.OnHeaderGUI();
FSMStateInspectorHelper helper = target as FSMStateInspectorHelper;
if (helper == null) return;
string name = null;
EditorGUI.BeginChangeCheck();
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
GUILayout.Label(EditorGUIUtility.IconContent("icons/processed/unityeditor/animations/animatorstate icon.asset"), GUILayout.Width(30), GUILayout.Height(30));
EditorGUILayout.LabelField("Name:", GUILayout.Width(60));
bool disabled = EditorApplication.isPlaying
|| helper.node.IsAnyState || helper.node.IsEntryState || helper.node.IsUpState;
EditorGUI.BeginDisabledGroup(disabled);
name = EditorGUILayout.DelayedTextField(helper.node.DisplayName);
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndHorizontal();
}
if (EditorGUI.EndChangeCheck())
{
string oldName = helper.node.name;
bool success = FSMStateNodeFactory.Rename(helper.controller, helper.node, name);
if (success)
helper.grap.RenameState(oldName, name);
}
EditorGUILayout.Space();
var rect = EditorGUILayout.BeginHorizontal();
Handles.color = Color.black;
Handles.DrawLine(new Vector2(rect.x, rect.y), new Vector3(rect.x + rect.width, rect.y));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
private void ShowMenu(MonoScript script)
{
FSMStateInspectorHelper helper = target as FSMStateInspectorHelper;
if (helper == null) return;
var genricMenu = new GenericMenu();
genricMenu.AddItem(new GUIContent("Remove Script"), false, () =>
{
helper.node.RemoveStateScript(script);
helper.controller.Save();
});
genricMenu.AddItem(new GUIContent("Edit Script"), false, () =>
{
AssetDatabase.OpenAsset(script);
});
genricMenu.ShowAsContext();
}
//private void DrawItem(Rect rect, int index, bool isActive, bool isFocused)
//{
// if (index < 0 || index >= reorderableList.list.Count) return;
// FSMStateScriptInfo info = reorderableList.list[index] as FSMStateScriptInfo;
// if (info == null) return;
// info.className = GUI.TextField(rect, info.className);
// if (string.IsNullOrEmpty(info.className))
// {
// EditorGUI.BeginDisabledGroup(true);
// GUI.Label(rect, "请输入类的全名(含命名空间)!");
// EditorGUI.EndDisabledGroup();
// }
//}
//private void OnDrawHeaderCallback(Rect rect)
//{
// GUI.Label(rect, "State Scripts");
//}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2304151d2a6c07d429a8183e483319c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
using UnityEditor;
namespace XFFSM
{
public class FSMStateInspectorHelper : ScriptableObjectSingleton<FSMStateInspectorHelper>
{
//private static FSMStateInspectorHelper _instance;
//public static FSMStateInspectorHelper Instance {
// get {
// if (_instance == null) {
// _instance = ScriptableObject.CreateInstance<FSMStateInspectorHelper>();
// }
// return _instance;
// }
//}
public FSMStateNodeData node;
public RuntimeFSMController controller;
public FSMStateGraphView grap;
public void Inspect(RuntimeFSMController controller, FSMStateNodeData node, FSMStateGraphView grap) {
if (node == null)
{
Selection.activeObject = null;
return;
}
this.node = node;
this.controller = controller;
this.grap = grap;
Selection.activeObject = this;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9bff4756b18720a4c92ff5f539a260f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,304 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace XFFSM
{
[CustomEditor(typeof(FSMTransitionInspectorHelper))]
public class FSMTransitionInspector : Editor
{
private ReorderableList reorderableList;
private Rect condition_left_rect;
private Rect condition_right_rect;
private Rect popRect;
private GUIContent add_a_sets_of_conditions = null;
private GUIContent auto_switch = null;
private static Dictionary<ParameterType, FSMConditionInspector> conditionInspector = new Dictionary<ParameterType, FSMConditionInspector>();
private Dictionary<int, ReorderableList> reorderables = new Dictionary<int, ReorderableList>();
private bool autoSwitch;
private void OnEnable()
{
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null) { return; }
reorderableList = new ReorderableList(helper.transition.conditions, typeof(FSMConditionData), true, true, true, true);
reorderableList.drawHeaderCallback += OnDrawHeaderCallback;
reorderableList.onAddCallback += this.OnAddCallback;
reorderableList.onRemoveCallback += this.OnRemoveCallback;
reorderableList.drawElementCallback += this.DrawItem;
autoSwitch = helper.transition.AutoSwtich;
}
public override void OnInspectorGUI()
{
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null) { return; }
EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying);
if (auto_switch == null)
auto_switch = new GUIContent("AutoSwitch", "当条件为空时,是否自动切换?当前过渡的条件为空时可用!");
EditorGUI.BeginDisabledGroup(!helper.transition.Empty);
Rect rect = GUILayoutUtility.GetRect(0f, 20, GUILayout.ExpandWidth(expand: true));
GUI.Label(rect, auto_switch);
rect.Set(rect.width - 20, rect.y, 20, 20);
if (helper.transition.Empty)
{
helper.transition.AutoSwtich = GUI.Toggle(rect, helper.transition.AutoSwtich, string.Empty);
}
else {
GUI.Toggle(rect,false, string.Empty);
}
if (autoSwitch != helper.transition.AutoSwtich)
{
helper.controller.Save();
autoSwitch = helper.transition.AutoSwtich;
}
GUILayout.Space(10);
EditorGUI.EndDisabledGroup();
reorderableList.list = helper.transition.conditions;
reorderableList.DoLayoutList();
GUILayout.Space(10);
for (int i = 0; i < helper.transition.group_conditions.Count; i++)
{
GroupCondition condition = helper.transition.group_conditions[i];
if (condition == null) continue;
ReorderableList list = GetReorderableList(condition);
if (list != null)
{
list.list = condition.conditions;
list.DoLayoutList();
GUILayout.Space(10);
}
}
if (add_a_sets_of_conditions == null)
add_a_sets_of_conditions = new GUIContent("Add a set of conditions","添加一组条件");
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (helper.transition.group_conditions.Count > 0)
{
GUILayout.Label("注:当有多组条件时,其中一组满足,状态就会切换!", "CN StatusWarn");
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(add_a_sets_of_conditions, GUILayout.Width(260), GUILayout.Height(25)))
{
helper.transition.group_conditions.Add(new GroupCondition());
helper.controller.Save();
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
}
protected override void OnHeaderGUI()
{
//base.OnHeaderGUI();
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null) { return; }
GUILayout.BeginHorizontal();
GUILayout.Label(EditorGUIUtility.IconContent("icons/processed/unityeditor/animations/animatorstatetransition icon.asset"),GUILayout.Width(30),GUILayout.Height(30));
GUILayout.Label(string.Format("{0} -> {1}", helper.transition.fromStateName, helper.transition.toStateName));
GUILayout.EndHorizontal();
// 画一条 分隔的线
var rect = EditorGUILayout.BeginHorizontal();
Handles.color = Color.black;
Handles.DrawLine(new Vector2(rect.x, rect.y), new Vector3(rect.x + rect.width, rect.y));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
private static void InitConditionInspectors() {
if (conditionInspector.Count == 0) {
conditionInspector.Add(ParameterType.Bool, new FSMBoolConditionInspector());
conditionInspector.Add(ParameterType.Float, new FSMFloatConditionInspector());
conditionInspector.Add(ParameterType.Int, new FSMIntConditionInspector());
conditionInspector.Add(ParameterType.Trigger, new FSMConditionInspector()); // 不需要做任何绘制
}
}
private void OnAddCallback(ReorderableList list) {
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null) { return; }
//Debug.Log("添加条件");
FSMConditionFactory.CreateCondition(helper.controller, helper.transition);
}
private void OnRemoveCallback(ReorderableList list)
{
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null) return;
FSMConditionFactory.DeleteCondition(helper.controller, helper.transition,list.index);
}
private void DrawItem(Rect rect, int index, bool isActive, bool isFocused) {
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null) { return; }
var conditon = helper.transition.conditions[index];
DrawItemExcute(rect, conditon);
}
private void DrawItemExcute(Rect rect, FSMConditionData condition)
{
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null) return;
condition_left_rect.Set(rect.x, rect.y, rect.width / 2, rect.height);
condition_right_rect.Set(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
if (helper.controller.parameters.Count > 0)
{
//tempContent.text = conditon.parameterName;
if (EditorGUI.DropdownButton(condition_left_rect, new GUIContent(condition.parameterName), FocusType.Keyboard))
{
// 弹出选择参数的弹框 TODO
popRect.Set(rect.x, rect.y + 2, rect.width / 2, rect.height);
PopupWindow.Show(popRect, new FSMSelectParamWindow(rect.width / 2, condition, helper.controller));
}
}
InitConditionInspectors();
FSMParameterData parameter = helper.controller.GetParameterData(condition.parameterName);
if (parameter == null)
{
EditorGUI.LabelField(condition_right_rect, "缺少参数!");
}
else
{
// 根据不同的参数类型绘制不同的内容
if (conditionInspector.ContainsKey(parameter.parameterType))
// 进行绘制
if (conditionInspector[parameter.parameterType] != null)
conditionInspector[parameter.parameterType].OnGUI(condition_right_rect, condition, helper.controller);
else
Debug.LogErrorFormat("未查询到对应的绘制方式:{0}", parameter.parameterType);
}
}
private void OnDrawHeaderCallback(Rect rect)
{
GUI.Label(rect, "Conditions");
}
public ReorderableList GetReorderableList(GroupCondition condition)
{
if (condition == null)
return null;
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null)
return null;
int key = condition.GetHashCode();
if (!reorderables.ContainsKey(key))
{
ReorderableList list = new ReorderableList(condition.conditions, typeof(FSMConditionData), true, true, true, true);
list.drawHeaderCallback += (rect)=> {
GUI.Label(rect, "Conditions");
rect.Set(rect.width - 10, rect.y + 1, 25, 25);
if (GUI.Button(rect,EditorGUIUtility.IconContent("d__Menu"), "IconButton"))
{
ShowConditionMenu(condition);
}
};
list.onAddCallback += (a) => {
FSMConditionData data = FSMConditionFactory.CreateCondition(helper.controller);
condition.conditions.Add(data);
helper.controller.Save();
};
list.onRemoveCallback += (a) =>
{
if(a.index >= 0 && a.index < condition.conditions.Count)
condition.conditions.RemoveAt(a.index);
helper.controller.Save();
};
list.drawElementCallback += (rect,index,c,d) =>
{
DrawItemExcute(rect, condition.conditions[index]);
};
reorderables.Add(key, list);
}
return reorderables[key];
}
private void ShowConditionMenu(GroupCondition condition)
{
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
if (helper == null) { return; }
var genricMenu = new GenericMenu();
genricMenu.AddItem(new GUIContent("Remove"), false, () =>
{
// 移除一组条件
helper.transition.group_conditions.Remove(condition);
helper.controller.Save();
});
genricMenu.ShowAsContext();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35e608d082dfb634986b77bdcb9a59cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
using UnityEditor;
namespace XFFSM
{
public class FSMTransitionInspectorHelper : ScriptableObjectSingleton<FSMTransitionInspectorHelper>
{
public FSMTransitionData transition;
public RuntimeFSMController controller;
public void Inspect(RuntimeFSMController controller, FSMTransitionData transition)
{
if (transition == null)
{
Selection.activeObject = null;
return;
}
this.transition = transition;
this.controller = controller;
Selection.activeObject = this;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d83e4df9515f6834289221614fd2fd62
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using UnityEngine;
namespace XFFSM
{
public class ScriptableObjectSingleton<T> : ScriptableObject where T : ScriptableObjectSingleton<T>
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
_instance = CreateInstance<T>();
}
return _instance;
}
}
private void OnDisable()
{
_instance = null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f161a7ba0c72a7249971e4d18ab4a249
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4f1e1e098fe279240872b9a9e6cf099c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
using UnityEditor;
using UnityEngine;
namespace XFFSM
{
public class GraphLayer
{
#region
protected Rect position;
#endregion
#region
public EditorWindow EditorWindow { get; private set; }
#endregion
#region
public GraphLayer(EditorWindow editorWindow) {
this.EditorWindow = editorWindow;
}
public virtual void OnGUI(Rect rect) {
position = rect;
UpdateTransformationMatrix();
}
public virtual void ProcessEvents() {
}
public virtual void Update() { }
private void UpdateTransformationMatrix()
{
//this.transormMatrix = Matrix4x4.TRS(position.center + this.Context.DragOffset , Quaternion.identity,Vector3.one * this.Context.ZoomFactor);
}
public virtual void OnLostFocus()
{
if (UnityEditor.EditorWindow.mouseOverWindow != null && UnityEditor.EditorWindow.mouseOverWindow.GetType().ToString().Equals("UnityEditor.InspectorWindow"))
return;
Context.Instance.ClearSelections();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0afc6de6e08d3ee479093608ac1302e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,350 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace XFFSM
{
public class ParamLayer : GraphLayer
{
#region
private ReorderableList reorderableList;
private ReorderableList reorderableListStates;
private Vector2 scrollView;
// 参数区域
//private Rect paramLabelRect;
//private Rect paramValueRect;
//const float param_value_width = 50f;
//const float param_value_padding = 2f;
private bool isRenaming = false;
private string newName;
private List<FSMParameterData> EmptyList = new List<FSMParameterData>();
private Rect headerRect = new Rect();
private string[] toolbars = new string[] { "Controllers", "Paramters" };
private int select = 1;
private int index = 0;
//private Vector3[] lines = new Vector3[2];
#endregion
#region
public override void OnGUI(Rect rect)
{
rect.Set(rect.x + 2,rect.y,rect.width,rect.height);
base.OnGUI(rect);
headerRect.Set(rect.x, rect.y, rect.width, 20);
//EditorGUI.DrawRect(headerRect, ColorConst.ParaBackground);
headerRect.Set(headerRect.x + 5, headerRect.y, 150, headerRect.height);
select = GUI.Toolbar(headerRect, select, toolbars, "toolbarbuttonLeft");
//Handles.color = Color.black;
//Handles.DrawLine(new Vector2(rect.x, rect.y + headerRect.height), new Vector2(rect.x + rect.width, rect.y + headerRect.height));
//Handles.DrawLine(new Vector2(rect.x + rect.width, rect.y), new Vector2(rect.x + rect.width, rect.y + rect.height));
// 偏移20, 用来绘制 States 和 Parmaters
rect.Set(rect.x, rect.y + 20, rect.width, rect.height);
//EditorGUI.DrawRect(rect, ColorConst.ParaBackground);
//GUI.Box(rect, string.Empty, GUI.skin.GetStyle("CN Box"));
if (select == 1)
{
DrawParamters(rect);
}
else {
DrawStates(rect);
}
}
private void DrawParamters(Rect rect)
{
if (reorderableList == null)
{
if (Context.Instance.RuntimeFSMController != null)
{
reorderableList = new ReorderableList(Context.Instance.RuntimeFSMController.parameters, typeof(FSMParameterData), true, true, true, true);
}
else
{
reorderableList = new ReorderableList(EmptyList, typeof(FSMParameterData), true, true, true, true);
}
reorderableList.drawHeaderCallback += HeaderCallbackDelegate;
reorderableList.onAddDropdownCallback += OnAddDropdownCallback;
reorderableList.onRemoveCallback += OnRemoveCallback;
reorderableList.drawElementCallback += DrawElementCallback;
reorderableList.onCanRemoveCallback += onCanRemoveCallback;
reorderableList.onCanAddCallback += onCanRemoveCallback;
}
if (Context.Instance.RuntimeFSMController != null)
reorderableList.list = Context.Instance.RuntimeFSMController.parameters;
else
reorderableList.list = EmptyList;
EditorGUI.BeginDisabledGroup(Context.Instance.RuntimeFSMController == null);
GUILayout.BeginArea(rect);
scrollView = GUILayout.BeginScrollView(scrollView);
reorderableList.DoLayoutList();
GUILayout.EndScrollView();
GUILayout.EndArea();
EditorGUI.EndDisabledGroup();
}
private void DrawStates(Rect rect)
{
if (reorderableListStates == null)
{
reorderableListStates = new ReorderableList(Context.Instance.RuntimeFSMControllers, typeof(RuntimeFSMController), false, true, false, false);
reorderableListStates.headerHeight = 0;
reorderableListStates.drawElementCallback += DrawStateElementCallback;
reorderableListStates.onSelectCallback += OnStateChanged;
}
for (int i = 0; i < Context.Instance.RuntimeFSMControllers.Count; i++)
{
if (Context.Instance.RuntimeFSMControllers[i] == null) {
Context.Instance.RuntimeFSMControllers.RemoveAt(i);
i--;
}
}
reorderableListStates.list = Context.Instance.RuntimeFSMControllers;
GUILayout.BeginArea(rect);
scrollView = GUILayout.BeginScrollView(scrollView);
reorderableListStates.DoLayoutList();
GUILayout.EndScrollView();
GUILayout.EndArea();
}
public override void ProcessEvents()
{
base.ProcessEvents();
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 &&
position.Contains(Event.current.mousePosition)
)
{
Context.Instance.ClearSelections();
}
}
public override void OnLostFocus()
{
base.OnLostFocus();
isRenaming = false;
}
#endregion
#region
public ParamLayer(EditorWindow editorWindow) : base(editorWindow)
{
}
// 添加
private void OnAddDropdownCallback(Rect buttonRect, ReorderableList list)
{
GenericMenu menu = new GenericMenu();
for (int i = 0; i < Enum.GetValues(typeof(ParameterType)).Length; i++)
{
ParameterType v = (ParameterType)Enum.GetValues(typeof(ParameterType)).GetValue(i);
menu.AddItem(new GUIContent(v.ToString()), false, () =>
{
FSMParamterFactory.CreateParamter(Context.Instance.RuntimeFSMController, v);
});
}
menu.ShowAsContext();
}
// 移除
private void OnRemoveCallback(ReorderableList list)
{
FSMParamterFactory.RemoveParamter(Context.Instance.RuntimeFSMController, list.index);
}
// 绘制每一条数据
private void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused)
{
if (Context.Instance.RuntimeFSMController == null) return;
if (index < 0 || index >= Context.Instance.RuntimeFSMController.parameters.Count) return;
FSMParameterData parameter = Context.Instance.RuntimeFSMController.parameters[index];
if (parameter == null) return;
rect.width *= 0.6f;
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition) && isFocused )
{
isRenaming = true;
this.index = index;
}
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 &&
!rect.Contains(Event.current.mousePosition) && index == reorderableList.index)
{
EditorApplication.delayCall += () =>
{
isRenaming = false;
};
GUI.FocusControl(null);
}
// 按下回车键的时候 也需要取消重命名
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
{
EditorApplication.delayCall += () =>
{
isRenaming = false;
};
}
if (isRenaming && index == reorderableList.index)
{
// 绘制输入框
EditorGUI.BeginChangeCheck();
newName = EditorGUI.DelayedTextField(rect, parameter.name);
if (EditorGUI.EndChangeCheck() && this.index == index)
{
isRenaming = false;
if (parameter.name.Equals(newName)) return;
FSMParamterFactory.RenameParamter(Context.Instance.RuntimeFSMController, parameter, newName);
}
}
else
{
GUI.Label(rect, parameter.name);
}
rect.x += rect.width;
rect.width /= 3;
EditorGUI.BeginDisabledGroup(true);
GUI.Label(rect, GetParameterType(parameter));
EditorGUI.EndDisabledGroup();
rect.x += rect.width;
switch (parameter.parameterType)
{
case ParameterType.Float:
parameter.Value = EditorGUI.FloatField(rect, parameter.Value);
break;
case ParameterType.Int:
parameter.Value = EditorGUI.IntField(rect, (int)parameter.Value);
break;
case ParameterType.Bool:
parameter.Value = EditorGUI.Toggle(rect, parameter.Value == 1) ? 1 : 0;
break;
case ParameterType.Trigger:
parameter.Value = EditorGUI.Toggle(rect, parameter.Value == 1, GUI.skin.GetStyle("Radio")) ? 1 : 0;
break;
}
}
private string GetParameterType(FSMParameterData parameter)
{
switch (parameter.parameterType)
{
case ParameterType.Float:
return "Float";
case ParameterType.Int:
return "Int";
case ParameterType.Bool:
return "Bool";
case ParameterType.Trigger:
return "Trigger";
}
return string.Empty;
}
private void DrawStateElementCallback(Rect rect, int index, bool isActive, bool isFocused)
{
if (index < 0 || index >= Context.Instance.RuntimeFSMControllers.Count) return;
RuntimeFSMController controller = Context.Instance.RuntimeFSMControllers[index];
//if (controller == null) return;
try
{
GUIContent content = EditorGUIUtility.IconContent("AnimatorController Icon");
if (controller == Context.Instance.RuntimeFSMController)
GUI.Label(new Rect(rect.x, rect.y, rect.height, rect.height), "✓");
GUI.Label(new Rect(rect.x + rect.height, rect.y, rect.height, rect.height), content);
rect.Set(rect.x + rect.height * 2,rect.y,rect.width - rect.height,rect.height);
GUI.Label(rect,controller == null ? "None" : controller.name);
}
catch (Exception)
{
}
}
private bool onCanRemoveCallback(ReorderableList list)
{
return Application.isPlaying == false;
}
private void OnStateChanged(ReorderableList list)
{
if (Context.Instance.RuntimeFSMControllers == null || Context.Instance.RuntimeFSMControllers.Count == 0) return;
if (Context.Instance.RuntimeFSMController == Context.Instance.RuntimeFSMControllers[list.index]) return;
Context.Instance.FSMControllerIndex = list.index;
}
private void HeaderCallbackDelegate(Rect rect)
{
rect.width *= 0.6f;
rect.x += 15;
GUI.Label(rect,"名称");
rect.x += rect.width;
rect.width /= 3;
rect.x -= 10;
GUI.Label(rect, "类型");
rect.x += rect.width;
rect.x -= 5;
GUI.Label(rect, "值");
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5e4cbef482e9bd428dc3d594927279e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e334cab2f8105f94a8bde697f6a370de
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More