沈阳网站专业加盟网站建设的内容

当前位置: 首页 > news >正文

沈阳网站专业,加盟网站建设的内容,中小企业公司,天津特定网站建设推广【分析】 行为树是否足够灵活强大依赖于足够丰富的各类条件节点和动作节点#xff0c;在实现这些节点时#xff0c;不可避免的#xff0c;节点本身需要有一些参数供配置。 这些参数可以分为静态的固定值的参数以及动态读取设置的参数。 静态参数直接设置为Public即可在实现这些节点时不可避免的节点本身需要有一些参数供配置。 这些参数可以分为静态的固定值的参数以及动态读取设置的参数。 静态参数直接设置为Public即可动态参数的难点在于可能需要获取不同模块的数据。这里认为数据是这些模块的某个属性或字段 节点是通用的不可能在获取模块A的数据时在Update中调用A方法在获取模块B的数据时调用B方法这里需要一个统一的调用方式。 获取属性值的关键在于获取实例类和属性名在代码上就可以通过实例类.属性名获取到属性值 因此动态参数的配置内容为类名和字段名需要通过代码自动生成[实例类.属性名]的调用这必须要依赖反射。 理论上我们可以通过类A.类B.类C这样生成嵌套的调用。但这样通过程序去实现是成本较高出现Bug容易导致错乱。 需要通过流程做规范最多通过某个类就可以获取到值。 例如在Unity中脚本挂在GameObject上可以通过统一的GetComponet获取有个属性系统可以通过一个的单例获取等等这于具体的项目相关。这里我们用GetComponet 为了实现动态的设置和获取值我们需要将这些动态值做封装设置Getter和Setter 【行为树数据初始化】 参数配置是整个行为树配置的一部分在此之前我们需要补充实现前文缺省的初始化数据部分 我们需要有一个结构来描述行为树的配置数据这个结构是用于运行时初始化数据的可以将编辑数据和运行时数据结构分隔开来。 数据显而易见的可以分为三个层次行为树的数据、节点的数据、参数的数据 比较难解决的是最下层的参数数据这在很多配置数据中是通用的其主要问题是参数类型、数量、值等各不相同如何做一个统一的描述 方式一是采用Json描述不同节点的参数每个参数各自解析便于任意扩展可以用Unity提供的JsonUtility.ToJson和JsonUtility.FromJson做编码和解析。更进一步的可以看到这和网络协议的编码和解码类似在追求性能和效率时可以考虑用ProtoBuffer 方式二是使用固定的结构来存储所有类型数据每个参数各自解析。 行为树节点基本参数是可以做明确的这里采用方式二 【生命周期】 行为树需要初始化读取配置数据节点也需要有初始化 行为树在Update中执行轮询所有节点节点需要有Update方法。行为树本身也不一定需要每帧都执行可以根据实际情况定期执行等 大多数节点只在Update中执行即可有些节点自身包含参数数据需要重置。 有些特殊的节点在首次执行时有特殊的逻辑处理需要有特殊的Start方法 行为树销毁时节点数据也需要销毁 【代码实现】 配置数据结构 #region BTConfigDatapublic class BehaviourTreeData:ScriptableObject{public string btName;public UpdateType updateType;public float updateTime;public ListNodeInfo nodes new ListNodeInfo();}[Serializable]public class NodeInfo{public int nodeId;public string nodeName;public string nodeType;public ListNodeStaticParams staticParams new ListNodeStaticParams();public ListNodeDynamicParams dynamicParams new ListNodeDynamicParams();public Listint subNodes new Listint();}[Serializable]public class NodeStaticParams{public string paramType;public string paramName;public Listint intParams;public Liststring strParams;//节点类型是有限的直接对每个类型做编码解码public void Encode(string name,bool value){paramType bool;paramName name;intParams new Listint();intParams.Add(value ? 1 : 0);}public void Encode(string name,int value){paramType int;paramName name;intParams new Listint();intParams.Add(value);}public void Encode(string name, float value){paramType float;paramName name;intParams new Listint();intParams.Add((int)value*1000);}public void Encode(string name, string value){paramType string;paramName name;strParams new Liststring();strParams.Add(value);}public void Encode(string name, Comparison value){paramType comparison;paramName name;intParams new Listint();intParams.Add((int)value);}public void Encode(string name, Listint value){paramType listint;paramName name;intParams new Listint();intParams.AddRange(value);}public void Encode(string name, Listfloat value){paramType listfloat;paramName name;intParams value.Select(x(int)(x*1000)).ToList();}public void Encode(string name, Liststring value){paramType liststring;paramName name;strParams new Liststring();strParams.AddRange(value);}public object Decode(){object value null;switch (paramType){case bool: value intParams[0] 1;break;case float: value ((float)intParams[0]) / 1000; break;case string: value strParams[0]; break;case int: value intParams[0]; break;case listint: value new Listint(intParams); break;case listfloat:value intParams.Select(x((float)x)/1000).ToList(); break;case liststring:value new Liststring(strParams); break;case comparison:value (Comparison)intParams[0];break;default:break;}return value;}}[Serializable]public class NodeDynamicParams{public string paramType;public string paramName;public string classType;public string fieldName;public bool isProperty;//最好字段都是属性}public class DynamicParams{public string paramName;public virtual void Init(BehaviorTree bt, NodeDynamicParams param){}}public class DynamicParamsT: DynamicParams{protected FuncT getter;protected ActionT setter;public T Value {get {if(getter ! null){return getter();}return default(T);}set {if(setter ! null){setter.Invoke(value);}}}public override void Init(BehaviorTree bt, NodeDynamicParams param){var classType Type.GetType(param.classType);var classIntance bt.owner.GetComponent(classType);if(param.isProperty){var property classIntance.GetType().GetProperty(param.fieldName);var getMethod property.GetGetMethod(true);var getter (FuncT)Delegate.CreateDelegate(typeof(FuncT), classIntance, getMethod);this.getter getter;var setMethod property.GetSetMethod(true);var setter (ActionT)Delegate.CreateDelegate(typeof(ActionT), classIntance, setMethod);this.setter setter;}else{var fieldInfo classIntance.GetType().GetField(param.fieldName);FuncT getter () {return (T)fieldInfo.GetValue(classIntance);};this.getter getter;ActionT setter (value) {fieldInfo.SetValue(classIntance, value);};this.setter setter;}}}#endregion 行为树 public class BehaviorTree{public string btName;public int btId;public UpdateType updateType;public float updateTime;private float curTime;public GameObject owner;private Node rootNode;private ListNode allNodes new ListNode();private Dictionaryint,Node id2Node new Dictionaryint,Node();private Dictionaryint,NodeInfo id2NodeInfo new Dictionaryint,NodeInfo();public static Funcstring, BehaviourTreeData loadFunc null;public void Init(GameObject owner, string path, Funcstring, BehaviourTreeData loadFunc){this.owner owner;//这里省略部分边界情况检查的逻辑if (!string.IsNullOrEmpty(path)){var data loadFunc?.Invoke(path);btId owner.GetInstanceID();updateType data.updateType; updateTime data.updateTime;//通常初始化有两种写法一种是在管理者中完成管理者自身以及被管理者的数据初始化另一种是管理者完成自身的初始化后将被数据传递给被管理者被管理者自身完成初始化//第二种方式更加灵活可变且不需要关注行为树结构是怎么样的只需每个节点做好自身的初始化//在第二种方式下涉及如何将数据递归传递的问题为统一获取将数据记录在管理者上被管理者根据Id从管理者中获取初始化后可选择将数据释放foreach (var item in data.nodes){id2NodeInfo[item.nodeId] item;}var rootData data.nodes[0];//默认第一个是rootNode数据rootNode new RootNode();rootNode.Init(rootData, this);id2NodeInfo.Clear();//也可以保留}}public void Update(float time){if(updateType UpdateType.EveryFrame){Update();}else if(updateType UpdateType.FixedTime){curTime time;if(curTimeupdateTime){curTime 0;Update();}}}public NodeStatus Update(){var status rootNode.Update();if(status ! NodeStatus.Running){rootNode.End();}return status;}public void Destroy(){foreach (var item in allNodes){item.Destroy();}allNodes.Clear();id2Node.Clear();id2NodeInfo.Clear();}public NodeInfo GetNodeInfo(int nodeId){return id2NodeInfo[nodeId];}public void AddNode(Node node){allNodes.Add(node);id2Node[node.nodeId] node;}} 部分节点 public class RootNode:Node {public Node subNode;protected override void OnInit(NodeInfo nodeInfo){if(nodeInfo.subNodes.Count 0){var subNodeInfo owner.GetNodeInfo(nodeInfo.subNodes[0]);Type type Type.GetType(subNodeInfo.nodeType);//根据完整类名例如反射创建类及其实例subNode (Node)Activator.CreateInstance(type);subNode.Init(subNodeInfo, owner);}}protected override NodeStatus OnUpdate(){return subNode.Update();}}public class ControlNode:Node { public ListNode subNodes;public int curSubIndex;protected override void OnInit(NodeInfo nodeInfo){foreach (var item in nodeInfo.subNodes){var subNodeInfo owner.GetNodeInfo(item);Type type Type.GetType(subNodeInfo.nodeType);//根据完整类名例如反射创建类及其实例var node (Node)Activator.CreateInstance(type);subNodes.Add(node);node.Init(subNodeInfo, owner);}foreach (var item in nodeInfo.staticParams){var field this.GetType().GetField(item.paramName);//这里类型有限直接用对每个类型做判断实现类型转换field.SetValue(this, item.Decode());}foreach (var item in nodeInfo.dynamicParams){var paramtype Type.GetType(item.paramType);var paramInstance (DynamicParams)Activator.CreateInstance(paramtype);paramInstance.Init(owner, item);var fieldInfo GetType().GetField(item.paramName);fieldInfo.SetValue(this, paramInstance);}}public class BoolCompareSelectorNode:ControlNode{public bool targetValue;public DynamicParamsbool curValue;public bool targetValues { get; set; }public bool GetBool(bool a){return a;}protected override NodeStatus OnUpdate(){curSubIndex targetValue curValue.Value ? 0 : 1;var status subNodes[curSubIndex].Update();return status;}}public class IntCompareSelectorNode : ControlNode{public int targetValue;public Comparison comparison;public DynamicParamsint curValue;protected override NodeStatus OnUpdate(){bool res true;switch(comparison){case Comparison.SmallerThan: res curValue.ValuetargetValue; break;case Comparison.GreaterThan: res curValue.Value targetValue; break;case Comparison.Equal: res curValue.Value targetValue;break;case Comparison.SmallerThanOrEqual: res curValue.Value targetValue; break;case Comparison.GreaterThanOrEqual: res curValue.Value targetValue; break;}curSubIndex res?0:1;var status subNodes[curSubIndex].Update();return status;}}public class FloatCompareSelectorNode : ControlNode{public float targetValue;public Comparison comparison;public DynamicParamsfloat curValue;protected override NodeStatus OnUpdate(){bool res true;switch (comparison){case Comparison.SmallerThan: res curValue.Value targetValue; break;case Comparison.GreaterThan: res curValue.Value targetValue; break;case Comparison.Equal: res curValue.Value targetValue; break;case Comparison.SmallerThanOrEqual: res curValue.Value targetValue; break;case Comparison.GreaterThanOrEqual: res curValue.Value targetValue; break;}curSubIndex res ? 0 : 1;var status subNodes[curSubIndex].Update();return status;}}public class IntRangeSelectorNode:ControlNode{public Listint intRange;public DynamicParamsint curValue;protected override NodeStatus OnUpdate(){for (int i 0;iintRange.Count;i){if (curValue.Value intRange[i]){curSubIndex i;break;}}var status subNodes[curSubIndex].Update();return status;}}public class FloatRangeSelectorNode : ControlNode{public Listfloat intRange;public DynamicParamsfloat curValue;protected override NodeStatus OnUpdate(){for (int i 0; i intRange.Count; i){if (curValue.Value intRange[i]){curSubIndex i;break;}}var status subNodes[curSubIndex].Update();return status;}}public class ActionNode:Node{private bool start;protected override NodeStatus OnUpdate(){if(!start){Start();start true; }return base.OnUpdate();}private void Start(){OnStart();}protected virtual void OnStart(){}protected override void OnEnd(){start false;}}