From cd974df054b37b5234a6dcef395ec31998fc662f Mon Sep 17 00:00:00 2001 From: Karim Kimsanbaev Date: Thu, 24 Aug 2023 15:05:38 +0300 Subject: [PATCH 1/7] Add validate to remove nullable nodes --- Core/BehaviourTree.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Core/BehaviourTree.cs b/Core/BehaviourTree.cs index 0bd004b..af97b67 100644 --- a/Core/BehaviourTree.cs +++ b/Core/BehaviourTree.cs @@ -421,6 +421,15 @@ void AddBlackboardAsset() } } + public void OnValidate() + { + var hasNullable = allNodes.Any(node => node == null); + if (hasNullable) + { + allNodes = allNodes.Where(node => node != null).ToArray(); + } + } + [HideInInspector] public Vector2 panPosition = Vector2.zero; From ec22796c746c8d8d8ca02e22e64f42c03d4a37f2 Mon Sep 17 00:00:00 2001 From: Karim Kimsanbaev Date: Thu, 24 Aug 2023 16:19:22 +0300 Subject: [PATCH 2/7] Add comparator & getter nodes --- Core/BehaviourNode.cs | 7 ++++- Core/BehaviourTree.cs | 19 ++++++++++++- Core/Comparator.cs | 63 +++++++++++++++++++++++++++++++++++++++++ Core/Comparator.cs.meta | 3 ++ Core/GetterNode.cs | 30 ++++++++++++++++++++ Core/GetterNode.cs.meta | 3 ++ Editor/BonsaiEditor.cs | 5 ++++ Editor/BonsaiNode.cs | 15 ++++++++++ Editor/BonsaiSaver.cs | 15 ++++++++++ 9 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 Core/Comparator.cs create mode 100644 Core/Comparator.cs.meta create mode 100644 Core/GetterNode.cs create mode 100644 Core/GetterNode.cs.meta diff --git a/Core/BehaviourNode.cs b/Core/BehaviourNode.cs index dc654d3..216bc7a 100644 --- a/Core/BehaviourNode.cs +++ b/Core/BehaviourNode.cs @@ -162,7 +162,7 @@ protected GameObject Actor public bool IsComposite() { - return MaxChildCount() > 1; + return MaxChildCount() == int.MaxValue; } public bool IsDecorator() @@ -174,6 +174,11 @@ public bool IsTask() { return MaxChildCount() == 0; } + + public bool IsComparator() + { + return MaxChildCount() == 2; + } /// /// A summary description of the node. diff --git a/Core/BehaviourTree.cs b/Core/BehaviourTree.cs index af97b67..99ea382 100644 --- a/Core/BehaviourTree.cs +++ b/Core/BehaviourTree.cs @@ -358,7 +358,17 @@ public static BehaviourTree Clone(BehaviourTree sourceTree) else if (copyNode.IsDecorator() && nodeSource.ChildCount() == 1) { var copyDecorator = copyNode as Decorator; - copyDecorator.SetChild(GetInstanceVersion(cloneBt, nodeSource.GetChildAt(0))); ; + copyDecorator.SetChild(GetInstanceVersion(cloneBt, nodeSource.GetChildAt(0))); + } + + else if (copyNode.IsComparator() && nodeSource.ChildCount() > 0) + { + var copyComparator = copyNode as Comparator; + copyComparator.SetChildX(GetInstanceVersion(cloneBt, nodeSource.GetChildAt(0))); + if (nodeSource.ChildCount() == 2) + { + copyComparator.SetChildY(GetInstanceVersion(cloneBt, nodeSource.GetChildAt(1))); + } } } @@ -406,6 +416,13 @@ private void ClearChildrenStructure(BehaviourNode node) var decorator = node as Decorator; decorator.SetChild(null); } + + else if (node.IsComparator()) + { + var decorator = node as Comparator; + decorator.SetChildX(null); + decorator.SetChildY(null); + } } #if UNITY_EDITOR diff --git a/Core/Comparator.cs b/Core/Comparator.cs new file mode 100644 index 0000000..833a584 --- /dev/null +++ b/Core/Comparator.cs @@ -0,0 +1,63 @@ +using System; +using UnityEngine; + +namespace Bonsai.Core +{ + /// + /// The base class for two getter nodes. + /// + public abstract class Comparator : BehaviourNode + { + public sealed override int MaxChildCount() + { + return 2; + } + + public abstract void SetChildX(object childrenX); + public abstract void SetChildY(object childrenY); + } + + public abstract class Comparator : Comparator + { + [SerializeField, HideInInspector] private GetterNode childrenX; + [SerializeField, HideInInspector] private GetterNode childrenY; + + protected abstract bool Compare(T x, T y); + + public sealed override Status Run() + { + var x = childrenX.Get(); + var y = childrenY.Get(); + return Compare(x, y) ? Status.Success : Status.Failure; + } + + public sealed override void SetChildX(object childX) + { + childrenX = childX as GetterNode; + } + + public sealed override void SetChildY(object childY) + { + childrenY = childY as GetterNode; + } + + public sealed override int ChildCount() + { + var count = 0; + if (childrenX) count++; + if (childrenY) count++; + + return count; + } + + public sealed override BehaviourNode GetChildAt(int index) + { + return index switch + { + 0 => childrenX, + 1 => childrenY, + _ => throw new ArgumentOutOfRangeException(nameof(index), index, null) + }; + } + } +} \ No newline at end of file diff --git a/Core/Comparator.cs.meta b/Core/Comparator.cs.meta new file mode 100644 index 0000000..7a1367a --- /dev/null +++ b/Core/Comparator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5e521880d62d4489bc10a586280bab8d +timeCreated: 1692880278 \ No newline at end of file diff --git a/Core/GetterNode.cs b/Core/GetterNode.cs new file mode 100644 index 0000000..e8573ab --- /dev/null +++ b/Core/GetterNode.cs @@ -0,0 +1,30 @@ +namespace Bonsai.Core +{ + /// + /// Base class for a node that takes some values. + /// + public abstract class GetterNode : BehaviourNode + { + public abstract T Get(); + + public sealed override Status Run() + { + return Status.Success; + } + + public sealed override BehaviourNode GetChildAt(int index) + { + return null; + } + + public sealed override int ChildCount() + { + return 0; + } + + public sealed override int MaxChildCount() + { + return 0; + } + } +} \ No newline at end of file diff --git a/Core/GetterNode.cs.meta b/Core/GetterNode.cs.meta new file mode 100644 index 0000000..ceaf8e5 --- /dev/null +++ b/Core/GetterNode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e30ed613a3874862a1b97824e52e3073 +timeCreated: 1692880268 \ No newline at end of file diff --git a/Editor/BonsaiEditor.cs b/Editor/BonsaiEditor.cs index 6554f37..8f89fd3 100644 --- a/Editor/BonsaiEditor.cs +++ b/Editor/BonsaiEditor.cs @@ -478,6 +478,11 @@ public static Type CoreType(BehaviourNode behaviour) { return typeof(Decorator); } + + if (behaviour is Comparator) + { + return typeof(Comparator); + } return typeof(Task); } diff --git a/Editor/BonsaiNode.cs b/Editor/BonsaiNode.cs index c867086..74c0a62 100644 --- a/Editor/BonsaiNode.cs +++ b/Editor/BonsaiNode.cs @@ -177,6 +177,21 @@ public void SetParent(BonsaiNode newParent) newParent.OrphanChildren(); newParent.children.Add(this); } + + else if (newParent.behaviour is Comparator) + { + if (newParent.children.Count == 2) + { + var firstChildren = newParent.children[0]; + newParent.OrphanChildren(); + newParent.children.Add(firstChildren); + newParent.children.Add(this); + } + else + { + newParent.children.Add(this); + } + } // else: Tasks cannot have children added. } diff --git a/Editor/BonsaiSaver.cs b/Editor/BonsaiSaver.cs index b11b192..635f003 100644 --- a/Editor/BonsaiSaver.cs +++ b/Editor/BonsaiSaver.cs @@ -162,6 +162,7 @@ private void SaveTree(TreeMetaData meta, BonsaiCanvas canvas) // Set parent-child connections matching those in the canvas. Only consider decorators and composites. SetCompositeChildren(canvas); SetDecoratorChildren(canvas); + SetComparatorChildren(canvas); // Re-add nodes to tree. if (canvas.Root != null) @@ -200,6 +201,20 @@ private void SetDecoratorChildren(BonsaiCanvas canvas) decoratorBehaviour.SetChild(node.GetChildAt(0).Behaviour); } } + + private void SetComparatorChildren(BonsaiCanvas canvas) + { + IEnumerable comparatorNodes = canvas.Nodes + .Where(n => n.Behaviour.IsComparator() && n.ChildCount() > 0); + + foreach (BonsaiNode node in comparatorNodes) + { + var comparatorBehaviour = node.Behaviour as Comparator; + comparatorBehaviour.SetChildX(node.GetChildAt(0).Behaviour); + if (node.Children.Count == 2) + comparatorBehaviour.SetChildY(node.GetChildAt(1).Behaviour); + } + } private void AddNewNodeAssets( BehaviourTree treeAsset, From 6f161672e608101497f1498c5bff80e45fa1cebe Mon Sep 17 00:00:00 2001 From: Karim Kimsanbaev Date: Thu, 24 Aug 2023 16:24:57 +0300 Subject: [PATCH 3/7] Add comparator color --- Editor/BonsaiPreferences.cs | 1 + Editor/Drawer.cs | 5 +++++ Editor/Resources/DefaultBonsaiPreferences.asset | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Editor/BonsaiPreferences.cs b/Editor/BonsaiPreferences.cs index 208d051..a7a75b3 100644 --- a/Editor/BonsaiPreferences.cs +++ b/Editor/BonsaiPreferences.cs @@ -32,6 +32,7 @@ public class BonsaiPreferences : ScriptableObject [Header("Node Colors")] public Color compositeColor; public Color decoratorColor; + public Color comparatorColor; public Color conditionalColor; public Color serviceColor; public Color taskColor; diff --git a/Editor/Drawer.cs b/Editor/Drawer.cs index 5a6e727..d7cf550 100644 --- a/Editor/Drawer.cs +++ b/Editor/Drawer.cs @@ -145,6 +145,11 @@ private static Color NodeTypeColor(BonsaiNode node) { return BonsaiPreferences.Instance.decoratorColor; } + + else if (node.Behaviour is Core.Comparator) + { + return BonsaiPreferences.Instance.comparatorColor; + } return BonsaiPreferences.Instance.compositeColor; } diff --git a/Editor/Resources/DefaultBonsaiPreferences.asset b/Editor/Resources/DefaultBonsaiPreferences.asset index 6e2cb6f..8371ab5 100644 --- a/Editor/Resources/DefaultBonsaiPreferences.asset +++ b/Editor/Resources/DefaultBonsaiPreferences.asset @@ -20,12 +20,12 @@ MonoBehaviour: gridTexture: {fileID: 2800000, guid: 4ab4e605fe422164da65569c46a161d2, type: 3} failureSymbol: {fileID: 2800000, guid: 8db2b847ab6b6434c9da1e0cf721fd8e, type: 3} successSymbol: {fileID: 2800000, guid: 39cfff267dd954642b26f231c7a997ad, type: 3} - nodeBackgroundTexture: {fileID: 2800000, guid: 6c5508e99038cdf46bb29a177e477d6d, - type: 3} + nodeBackgroundTexture: {fileID: 2800000, guid: 6c5508e99038cdf46bb29a177e477d6d, type: 3} nodeGradient: {fileID: 2800000, guid: c453f51bbb9d4134d86679c771689df6, type: 3} portTexture: {fileID: 2800000, guid: dfda76d4e735e7a47bcf756d5d90c44d, type: 3} compositeColor: {r: 0.3773585, g: 0.3773585, b: 0.3773585, a: 1} decoratorColor: {r: 0.4553469, g: 0.38719293, b: 0.7264151, a: 1} + comparatorColor: {r: 0.33397114, g: 0.4972861, b: 0.745283, a: 1} conditionalColor: {r: 0.36663404, g: 0.7264151, b: 0.72088, a: 1} serviceColor: {r: 0.990566, g: 0.6200383, b: 0.13550198, a: 1} taskColor: {r: 0.59607846, g: 0.6431373, b: 0.5372549, a: 1} From fe66d2970a3571d4f7c785b7d3d9c776991720ee Mon Sep 17 00:00:00 2001 From: Karim Kimsanbaev Date: Fri, 25 Aug 2023 11:59:24 +0300 Subject: [PATCH 4/7] Add color for getter nodes --- Core/GetterNode.cs | 15 +++++++++------ Editor/BonsaiPreferences.cs | 1 + Editor/Drawer.cs | 5 +++++ Editor/Resources/DefaultBonsaiPreferences.asset | 1 + 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Core/GetterNode.cs b/Core/GetterNode.cs index e8573ab..f75c6aa 100644 --- a/Core/GetterNode.cs +++ b/Core/GetterNode.cs @@ -1,12 +1,7 @@ namespace Bonsai.Core { - /// - /// Base class for a node that takes some values. - /// - public abstract class GetterNode : BehaviourNode + public abstract class GetterNode : BehaviourNode { - public abstract T Get(); - public sealed override Status Run() { return Status.Success; @@ -27,4 +22,12 @@ public sealed override int MaxChildCount() return 0; } } + + /// + /// Base class for a node that takes some values. + /// + public abstract class GetterNode : GetterNode + { + public abstract T Get(); + } } \ No newline at end of file diff --git a/Editor/BonsaiPreferences.cs b/Editor/BonsaiPreferences.cs index a7a75b3..a203806 100644 --- a/Editor/BonsaiPreferences.cs +++ b/Editor/BonsaiPreferences.cs @@ -36,6 +36,7 @@ public class BonsaiPreferences : ScriptableObject public Color conditionalColor; public Color serviceColor; public Color taskColor; + public Color getterColor; [Header("Status Colors")] public Color defaultNodeBackgroundColor; diff --git a/Editor/Drawer.cs b/Editor/Drawer.cs index d7cf550..75b0cb3 100644 --- a/Editor/Drawer.cs +++ b/Editor/Drawer.cs @@ -150,6 +150,11 @@ private static Color NodeTypeColor(BonsaiNode node) { return BonsaiPreferences.Instance.comparatorColor; } + + else if (node.Behaviour is Core.GetterNode) + { + return BonsaiPreferences.Instance.getterColor; + } return BonsaiPreferences.Instance.compositeColor; } diff --git a/Editor/Resources/DefaultBonsaiPreferences.asset b/Editor/Resources/DefaultBonsaiPreferences.asset index 8371ab5..b9a0183 100644 --- a/Editor/Resources/DefaultBonsaiPreferences.asset +++ b/Editor/Resources/DefaultBonsaiPreferences.asset @@ -29,6 +29,7 @@ MonoBehaviour: conditionalColor: {r: 0.36663404, g: 0.7264151, b: 0.72088, a: 1} serviceColor: {r: 0.990566, g: 0.6200383, b: 0.13550198, a: 1} taskColor: {r: 0.59607846, g: 0.6431373, b: 0.5372549, a: 1} + getterColor: {r: 0.631871, g: 0.745283, b: 0.2988163, a: 1} defaultNodeBackgroundColor: {r: 0.2924528, g: 0.2924528, b: 0.2924528, a: 1} selectedColor: {r: 0.3443396, g: 0.62303853, b: 1, a: 1} runningColor: {r: 0.2971698, g: 1, b: 0.61701846, a: 1} From f0c87edd49dbdb0a4a06e50daf0bf00691088e02 Mon Sep 17 00:00:00 2001 From: Karim Kimsanbaev Date: Fri, 25 Aug 2023 12:13:05 +0300 Subject: [PATCH 5/7] Add 'distance between' node --- Standard/Comparators.meta | 3 ++ Standard/Comparators/DistanceBetween.cs | 38 ++++++++++++++++++++ Standard/Comparators/DistanceBetween.cs.meta | 3 ++ 3 files changed, 44 insertions(+) create mode 100644 Standard/Comparators.meta create mode 100644 Standard/Comparators/DistanceBetween.cs create mode 100644 Standard/Comparators/DistanceBetween.cs.meta diff --git a/Standard/Comparators.meta b/Standard/Comparators.meta new file mode 100644 index 0000000..0403f67 --- /dev/null +++ b/Standard/Comparators.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9c0609dbc7ff4aef88a6029f4080a542 +timeCreated: 1692954683 \ No newline at end of file diff --git a/Standard/Comparators/DistanceBetween.cs b/Standard/Comparators/DistanceBetween.cs new file mode 100644 index 0000000..6e43b20 --- /dev/null +++ b/Standard/Comparators/DistanceBetween.cs @@ -0,0 +1,38 @@ +using System; +using Bonsai; +using Bonsai.Core; +using UnityEngine; + +namespace Bonsai.Standard +{ + /// + /// Compares the distance between two Vector3 points. + /// + [BonsaiNode("Comparator/", "DistanceBetween")] + public class DistanceBetween : Comparator + { + private enum Type + { + Equal, + Less, + Greater + }; + + [SerializeField] private Type _type = Type.Less; + [SerializeField] private float _value = 2; + + protected override bool Compare(Vector3 x, Vector3 y) + { + var distance = Vector3.Distance(x, y); + var isSuccess = _type switch + { + Type.Equal => Math.Abs(distance - _value) < 0.01f, + Type.Less => distance < _value, + Type.Greater => distance > _value, + _ => throw new ArgumentOutOfRangeException() + }; + + return isSuccess; + } + } +} \ No newline at end of file diff --git a/Standard/Comparators/DistanceBetween.cs.meta b/Standard/Comparators/DistanceBetween.cs.meta new file mode 100644 index 0000000..8d57bb9 --- /dev/null +++ b/Standard/Comparators/DistanceBetween.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 529fe917f03d4a098ab64f0062c696cb +timeCreated: 1692832217 \ No newline at end of file From 52fed2e2f63f91e55854918c59bc238e2e713af3 Mon Sep 17 00:00:00 2001 From: Karim Kimsanbaev Date: Fri, 25 Aug 2023 12:29:06 +0300 Subject: [PATCH 6/7] Fix reorder childs after remove child X --- Core/BehaviourTree.cs | 16 +++++++++++----- Core/Comparator.cs | 19 +++++++++++++------ Editor/BonsaiSaver.cs | 15 ++++++++++++--- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/Core/BehaviourTree.cs b/Core/BehaviourTree.cs index 99ea382..1bd3ddf 100644 --- a/Core/BehaviourTree.cs +++ b/Core/BehaviourTree.cs @@ -364,10 +364,17 @@ public static BehaviourTree Clone(BehaviourTree sourceTree) else if (copyNode.IsComparator() && nodeSource.ChildCount() > 0) { var copyComparator = copyNode as Comparator; - copyComparator.SetChildX(GetInstanceVersion(cloneBt, nodeSource.GetChildAt(0))); - if (nodeSource.ChildCount() == 2) + switch (nodeSource.ChildCount()) { - copyComparator.SetChildY(GetInstanceVersion(cloneBt, nodeSource.GetChildAt(1))); + case 1: + copyComparator.SetChild(GetInstanceVersion(cloneBt, nodeSource.GetChildAt(0))); + break; + case 2: + copyComparator.SetChilds( + GetInstanceVersion(cloneBt, nodeSource.GetChildAt(0)), + GetInstanceVersion(cloneBt, nodeSource.GetChildAt(1)) + ); + break; } } } @@ -420,8 +427,7 @@ private void ClearChildrenStructure(BehaviourNode node) else if (node.IsComparator()) { var decorator = node as Comparator; - decorator.SetChildX(null); - decorator.SetChildY(null); + decorator.SetChilds(null, null); } } diff --git a/Core/Comparator.cs b/Core/Comparator.cs index 833a584..bb42a38 100644 --- a/Core/Comparator.cs +++ b/Core/Comparator.cs @@ -13,8 +13,8 @@ public sealed override int MaxChildCount() return 2; } - public abstract void SetChildX(object childrenX); - public abstract void SetChildY(object childrenY); + public abstract void SetChild(object children); + public abstract void SetChilds(object childrenX, object childrenY); } public abstract class Comparator : Comparator @@ -31,13 +31,20 @@ public sealed override Status Run() return Compare(x, y) ? Status.Success : Status.Failure; } - public sealed override void SetChildX(object childX) + public sealed override void SetChild(object child) { - childrenX = childX as GetterNode; + if (childrenX == null) + { + childrenX = child as GetterNode; + return; + } + + childrenY = child as GetterNode; } - public sealed override void SetChildY(object childY) + public sealed override void SetChilds(object childX, object childY) { + childrenX = childX as GetterNode; childrenY = childY as GetterNode; } @@ -54,7 +61,7 @@ public sealed override BehaviourNode GetChildAt(int index) { return index switch { - 0 => childrenX, + 0 => childrenX ? childrenX : childrenY, 1 => childrenY, _ => throw new ArgumentOutOfRangeException(nameof(index), index, null) }; diff --git a/Editor/BonsaiSaver.cs b/Editor/BonsaiSaver.cs index 635f003..d262120 100644 --- a/Editor/BonsaiSaver.cs +++ b/Editor/BonsaiSaver.cs @@ -210,9 +210,18 @@ private void SetComparatorChildren(BonsaiCanvas canvas) foreach (BonsaiNode node in comparatorNodes) { var comparatorBehaviour = node.Behaviour as Comparator; - comparatorBehaviour.SetChildX(node.GetChildAt(0).Behaviour); - if (node.Children.Count == 2) - comparatorBehaviour.SetChildY(node.GetChildAt(1).Behaviour); + switch (node.Children.Count) + { + case 1: + comparatorBehaviour.SetChild(node.GetChildAt(0).Behaviour); + break; + case 2: + comparatorBehaviour.SetChilds( + node.GetChildAt(0).Behaviour, + node.GetChildAt(1).Behaviour + ); + break; + } } } From 852aa9e60c6b1dec995a47614c143aa547aa6a2f Mon Sep 17 00:00:00 2001 From: Karim Kimsanbaev Date: Fri, 25 Aug 2023 13:35:36 +0300 Subject: [PATCH 7/7] Rename for pattern naming --- Standard/Comparators/DistanceBetween.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Standard/Comparators/DistanceBetween.cs b/Standard/Comparators/DistanceBetween.cs index 6e43b20..c770af1 100644 --- a/Standard/Comparators/DistanceBetween.cs +++ b/Standard/Comparators/DistanceBetween.cs @@ -8,7 +8,7 @@ namespace Bonsai.Standard /// /// Compares the distance between two Vector3 points. /// - [BonsaiNode("Comparator/", "DistanceBetween")] + [BonsaiNode("Comparators/", "DistanceBetween")] public class DistanceBetween : Comparator { private enum Type