Unity节点编辑器制作的流程记录

2024-04-05

# Unity 节点编辑器

前言: 狂怒传说 项目中 因为想要做一个泛用的对话树 对话树还要被大行为树包起来? 好好好 小小 NPC 必须好好捣鼓一下 这下好了 不得不手搓行为树了

image-20240405034841452

如何让 Assets 跟 Hierarchy 之间进行序列化的存储可难倒我了 这里 就要弄清楚 GameObject 在 Scene Assets 中的区别了

考虑到效率 我打算先临时用一个 Hash 值来 表示数据 Data

节点保存 int 的哈希值,然后每次选中节点的时候 根据哈希值和当前树的拥有者来通过 Linq 查找 List 集合中的数据 Data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

//****************** 代码文件申明 ************************
//* 文件:NodeDataInOwner
//* 作者:Koo
//* 创建时间:2024/04/04 00:08:02 星期四
//* 功能:nothing
//*****************************************************

using System;
using System.Collections.Generic;
using System.Reflection;
using KooFrame;
using UnityEngine;
using UnityEngine.Events;

namespace SubSystem
{
[Serializable]
public class NodeDataInOwner
{
//public Dictionary<string, MethodInfo> methodsDic = new();

[SerializeField, HideInInspector] public List<string> methodsTypeNames = new();

[SerializeField] public UnityEvent Event = new UnityEvent();

/// <summary>
/// 运行时 会根据这个名字 对Event加委托监听
/// </summary>
[SerializeField] public string CurRegisterMethodName;


[SerializeField] public int OwnerDataID;

[SerializeField] public TreeOwner Owner;


[SerializeField] public GameObject EventTrigger;

/// <summary>
/// 绑定的Node
/// </summary>
[SerializeField] public Node BindNode;

public NodeDataInOwner(Node node, TreeOwner owner)
{
BindNode = node;
Owner = owner;
OwnerDataID = Animator.StringToHash(owner.name +
(owner.NodeDatas.Count)
.ToString());
}


public void EventAddListener(MethodInfo methodInfo, Type invokeObj)
{
UnityAction methodDelegate = () =>
methodInfo.Invoke(
EventTrigger.GetComponent(invokeObj), null);
CurRegisterMethodName = invokeObj.FullName + "/" + methodInfo.Name;
Event.AddListener(methodDelegate);
}
}
}

那么接下来一个问题就出现了 我序列化了要触发方法的名字 这里我如何通过名字拿到方法的委托来用来注册到事件里面去呢。

通过对象 和对象的 Component 名称 和当中的方法名称 在 OnEnable 的时候 反射拿到方法 并注册进事件当中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void OnEnable()
{
//注册事件
foreach (var nodeDataInOwner in NodeDatas)
{
string[] typeAndmethod = nodeDataInOwner.CurRegisterMethodName.Split("/", StringSplitOptions.None);
//得到方法
Type type = nodeDataInOwner.EventTrigger.GetComponent(Type.GetType(typeAndmethod[0])).GetType();
//获得所有方法 这里限制了方法必须是公共的 实例方法 而非静态或者字段
MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
MethodInfo method = null;
foreach (var methodInfo in methods)
{
//如果是Void返回值 并且无参 名称相同
if (methodInfo.ReturnType == typeof(void) && methodInfo.GetParameters().Length == 0 &&
methodInfo.Name == typeAndmethod[1])
{
method = methodInfo;
}
}

UnityAction action = () =>
{
if (method != null) method.Invoke(nodeDataInOwner.EventTrigger.GetComponent(type), null);
};

nodeDataInOwner.Event.AddListener(action);
}
}

这里的流程应该还能优化一些 让 OnEnable 可以执行的更快

0406

今天制作节点编辑器遇到了很多困难 比较触碰到了那个实验性的基类的属性 而且又是我知识盲区。

第一个是 节点没办法根据鼠标位置来创建

​ 第一个完全是坐标系的转换很出问题 就仿佛是之前在 Scene 窗口 右键移动 GO 一样

转换不同坐标系是很麻烦的一件事

第二个则是 节点的复制和黏贴

这里则是 Node 的数据层没有很好的剥离开来。导致需要序列化的时候。 没办法转换成能方便复制黏贴的 json 格式。

这里我想只有两种解决办法

  1. 自己定义 string 化的格式和内容,只抽离出必要的数据。来完成复制和黏贴
  2. 新写出可序列化的数据类 NodeModel,复制的时候 json 化 Model 类 同理 黏贴的时候反序列化一下

不过我也在油管上找到了合适的教程 ,可是 demo 时间紧任务重 。 果然还是先快点完成比较好。