Monobehaviour tree
This commit is contained in:
parent
7613269fa5
commit
c76b364ab7
8
Assets/MonoBehaviourTree.meta
Normal file
8
Assets/MonoBehaviourTree.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 646d077994822874f8e542df122777f7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/MonoBehaviourTree/Source.meta
Normal file
8
Assets/MonoBehaviourTree/Source.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba1db740a296d7c47b95f11808bad3a9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/MonoBehaviourTree/Source/Editor.meta
Normal file
8
Assets/MonoBehaviourTree/Source/Editor.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59e9dc397161a6b44991bc327eb108a6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
771
Assets/MonoBehaviourTree/Source/Editor/BehaviourTreeWindow.cs
Normal file
771
Assets/MonoBehaviourTree/Source/Editor/BehaviourTreeWindow.cs
Normal file
@ -0,0 +1,771 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
public class BehaviourTreeWindow : EditorWindow, IHasCustomMenu
|
||||
{
|
||||
private MonoBehaviourTree currentMBT;
|
||||
private Editor currentMBTEditor;
|
||||
private Node[] currentNodes;
|
||||
private Node selectedNode;
|
||||
private bool nodeMoved = false;
|
||||
private Vector2 workspaceOffset;
|
||||
private NodeHandle currentHandle;
|
||||
private NodeHandle dropdownHandleCache;
|
||||
private bool snapNodesToGrid;
|
||||
private bool locked = false;
|
||||
|
||||
private Rect nodeFinderActivatorRect;
|
||||
private NodeDropdown nodeDropdown;
|
||||
private Vector2 nodeDropdownTargetPosition;
|
||||
|
||||
private readonly float _handleDetectionDistance = 8f;
|
||||
private readonly Color _editorBackgroundColor = new Color(0.16f, 0.19f, 0.25f, 1);
|
||||
private GUIStyle _lockButtonStyle;
|
||||
private GUIStyle _defaultNodeStyle;
|
||||
private GUIStyle _selectedNodeStyle;
|
||||
private GUIStyle _successNodeStyle;
|
||||
private GUIStyle _failureNodeStyle;
|
||||
private GUIStyle _runningNodeStyle;
|
||||
private GUIStyle _nodeContentBoxStyle;
|
||||
private GUIStyle _nodeLabelStyle;
|
||||
private GUIStyle _nodeBreakpointLabelStyle;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Read snap option
|
||||
snapNodesToGrid = EditorPrefs.GetBool("snapNodesToGrid", true);
|
||||
// Setup events
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
Undo.undoRedoPerformed -= UpdateSelection;
|
||||
Undo.undoRedoPerformed += UpdateSelection;
|
||||
// Node finder
|
||||
nodeDropdown = new NodeDropdown(new AdvancedDropdownState(), AddNode);
|
||||
// Standard node
|
||||
_defaultNodeStyle = new GUIStyle();
|
||||
_defaultNodeStyle.border = new RectOffset(10,10,10,10);
|
||||
_defaultNodeStyle.normal.background = Resources.Load("mbt_node_default", typeof(Texture2D)) as Texture2D;
|
||||
// Selected node
|
||||
_selectedNodeStyle = new GUIStyle();
|
||||
_selectedNodeStyle.border = new RectOffset(10,10,10,10);
|
||||
_selectedNodeStyle.normal.background = Resources.Load("mbt_node_selected", typeof(Texture2D)) as Texture2D;
|
||||
// Success node
|
||||
_successNodeStyle = new GUIStyle();
|
||||
_successNodeStyle.border = new RectOffset(10,10,10,10);
|
||||
_successNodeStyle.normal.background = Resources.Load("mbt_node_success", typeof(Texture2D)) as Texture2D;
|
||||
// Failure node
|
||||
_failureNodeStyle = new GUIStyle();
|
||||
_failureNodeStyle.border = new RectOffset(10,10,10,10);
|
||||
_failureNodeStyle.normal.background = Resources.Load("mbt_node_failure", typeof(Texture2D)) as Texture2D;
|
||||
// Running node
|
||||
_runningNodeStyle = new GUIStyle();
|
||||
_runningNodeStyle.border = new RectOffset(10,10,10,10);
|
||||
_runningNodeStyle.normal.background = Resources.Load("mbt_node_running", typeof(Texture2D)) as Texture2D;
|
||||
// Node content box
|
||||
_nodeContentBoxStyle = new GUIStyle();
|
||||
_nodeContentBoxStyle.padding = new RectOffset(0,0,15,15);
|
||||
// Node label
|
||||
_nodeLabelStyle = new GUIStyle();
|
||||
_nodeLabelStyle.normal.textColor = Color.white;
|
||||
_nodeLabelStyle.alignment = TextAnchor.MiddleCenter;
|
||||
_nodeLabelStyle.wordWrap = true;
|
||||
_nodeLabelStyle.margin = new RectOffset(10,10,10,10);
|
||||
_nodeLabelStyle.font = Resources.Load("mbt_Lato-Regular", typeof(Font)) as Font;
|
||||
_nodeLabelStyle.fontSize = 12;
|
||||
// Node label when breakpoint is set to true
|
||||
_nodeBreakpointLabelStyle = new GUIStyle(_nodeLabelStyle);
|
||||
_nodeBreakpointLabelStyle.normal.textColor = new Color(1f, 0.35f, 0.18f);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
Undo.undoRedoPerformed -= UpdateSelection;
|
||||
}
|
||||
|
||||
[MenuItem("Window/Mono Behaviour Tree")]
|
||||
public static void OpenEditor()
|
||||
{
|
||||
BehaviourTreeWindow window = GetWindow<BehaviourTreeWindow>();
|
||||
window.titleContent = new GUIContent(
|
||||
"Behaviour Tree",
|
||||
Resources.Load("mbt_window_icon", typeof(Texture2D)) as Texture2D
|
||||
);
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
// Draw grid in background first
|
||||
PaintBackground();
|
||||
|
||||
// If there is no tree to render just skip rest
|
||||
if (currentMBT == null) {
|
||||
// Keep toolbar rendered
|
||||
PaintWindowToolbar();
|
||||
return;
|
||||
}
|
||||
|
||||
PaintConnections(Event.current);
|
||||
|
||||
// Repaint nodes
|
||||
PaintNodes();
|
||||
|
||||
PaintWindowToolbar();
|
||||
|
||||
// Update selection and drag
|
||||
ProcessEvents(Event.current);
|
||||
|
||||
if (GUI.changed) Repaint();
|
||||
}
|
||||
|
||||
private void PaintConnections(Event e)
|
||||
{
|
||||
// Paint line when dragging connection
|
||||
if (currentHandle != null) {
|
||||
Handles.BeginGUI();
|
||||
Vector3 p1 = new Vector3(currentHandle.position.x, currentHandle.position.y, 0f);
|
||||
Vector3 p2 = new Vector3(e.mousePosition.x, e.mousePosition.y, 0f);
|
||||
Handles.DrawBezier(p1, p2, p1, p2, new Color(0.3f, 0.36f, 0.5f), null, 4f);
|
||||
Handles.EndGUI();
|
||||
}
|
||||
// Paint all current connections
|
||||
for (int i = 0; i < currentNodes.Length; i++)
|
||||
{
|
||||
Node n = currentNodes[i];
|
||||
Vector3 p1 = GetBottomHandlePosition(n.rect) + workspaceOffset;
|
||||
for (int j = 0; j < n.children.Count; j++)
|
||||
{
|
||||
Handles.BeginGUI();
|
||||
Vector3 p2 = GetTopHandlePosition(n.children[j].rect) + workspaceOffset;
|
||||
Handles.DrawBezier(p1, p2, p1, p2, new Color(0.3f, 0.36f, 0.5f), null, 4f);
|
||||
Handles.EndGUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PaintWindowToolbar()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
EditorGUI.BeginDisabledGroup(currentMBT == null);
|
||||
if (GUILayout.Toggle(snapNodesToGrid, "Snap Nodes", EditorStyles.toolbarButton) != snapNodesToGrid)
|
||||
{
|
||||
snapNodesToGrid = !snapNodesToGrid;
|
||||
// Store this setting
|
||||
EditorPrefs.SetBool("snapNodesToGrid", snapNodesToGrid);
|
||||
}
|
||||
// TODO: Autolayout
|
||||
// if (GUILayout.Button("Auto Layout", EditorStyles.toolbarButton)){
|
||||
// Debug.Log("Auto layout is not implemented.");
|
||||
// }
|
||||
EditorGUILayout.Space();
|
||||
if (GUILayout.Button("Focus Root", EditorStyles.toolbarButton)){
|
||||
FocusRoot();
|
||||
}
|
||||
if (GUILayout.Button("Select In Hierarchy", EditorStyles.toolbarButton)){
|
||||
if (currentMBT != null)
|
||||
{
|
||||
Selection.activeGameObject = currentMBT.gameObject;
|
||||
EditorGUIUtility.PingObject(currentMBT.gameObject);
|
||||
}
|
||||
}
|
||||
if (GUILayout.Button("Add Node", EditorStyles.toolbarDropDown)){
|
||||
OpenNodeFinder(nodeFinderActivatorRect, false);
|
||||
}
|
||||
if (Event.current.type == EventType.Repaint) nodeFinderActivatorRect = GUILayoutUtility.GetLastRect();
|
||||
EditorGUI.EndDisabledGroup();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (currentMBT != null)
|
||||
{
|
||||
GUILayout.Label(string.Format("{0} {1}", currentMBT.name, -workspaceOffset));
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
void FocusRoot()
|
||||
{
|
||||
Root rootNode = null;
|
||||
for (int i = 0; i < currentNodes.Length; i++)
|
||||
{
|
||||
if (currentNodes[i] is Root) {
|
||||
rootNode = currentNodes[i] as Root;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rootNode != null) {
|
||||
workspaceOffset = -rootNode.rect.center + new Vector2(this.position.width/2, this.position.height/2);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
// Disable lock when changing state
|
||||
this.locked = false;
|
||||
UpdateSelection();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
// OPTIMIZE: This can be optimized to call repaint once per second
|
||||
Repaint();
|
||||
}
|
||||
|
||||
void OnSelectionChange()
|
||||
{
|
||||
MonoBehaviourTree previous = currentMBT;
|
||||
UpdateSelection();
|
||||
// Reset workspace position only when selection changed
|
||||
if (previous != currentMBT)
|
||||
{
|
||||
workspaceOffset = Vector2.zero;
|
||||
}
|
||||
Repaint();
|
||||
}
|
||||
|
||||
void OnFocus()
|
||||
{
|
||||
UpdateSelection();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
void IHasCustomMenu.AddItemsToMenu(GenericMenu menu)
|
||||
{
|
||||
menu.AddItem(new GUIContent("Lock"), this.locked, () => {
|
||||
this.locked = !this.locked;
|
||||
UpdateSelection();
|
||||
});
|
||||
}
|
||||
|
||||
// http://leahayes.co.uk/2013/04/30/adding-the-little-padlock-button-to-your-editorwindow.html
|
||||
private void ShowButton(Rect position)
|
||||
{
|
||||
// Cache style
|
||||
if (_lockButtonStyle == null) {
|
||||
_lockButtonStyle = "IN LockButton";
|
||||
}
|
||||
// Generic menu button
|
||||
GUI.enabled = currentMBT != null;
|
||||
this.locked = GUI.Toggle(position, this.locked, GUIContent.none, _lockButtonStyle);
|
||||
GUI.enabled = true;
|
||||
}
|
||||
|
||||
// DeselectNode cannot be called here
|
||||
// void OnLostFocus()
|
||||
// {
|
||||
// // DeselectNode();
|
||||
// }
|
||||
|
||||
private void UpdateSelection()
|
||||
{
|
||||
MonoBehaviourTree prevMBT = currentMBT;
|
||||
if (!this.locked && Selection.activeGameObject != null)
|
||||
{
|
||||
currentMBT = Selection.activeGameObject.GetComponent<MonoBehaviourTree>();
|
||||
// If new selection is null then restore previous one
|
||||
if (currentMBT == null)
|
||||
{
|
||||
currentMBT = prevMBT;
|
||||
}
|
||||
}
|
||||
if (currentMBT != prevMBT)
|
||||
{
|
||||
// Get new editor for new MBT
|
||||
Editor.CreateCachedEditor(currentMBT, null, ref currentMBTEditor);
|
||||
}
|
||||
if (currentMBT != null) {
|
||||
currentNodes = currentMBT.GetComponents<Node>();
|
||||
// Ensure there is no error when node script is missing
|
||||
for (int i = 0; i < currentNodes.Length; i++)
|
||||
{
|
||||
currentNodes[i].children.RemoveAll(item => item == null);
|
||||
}
|
||||
} else {
|
||||
currentNodes = new Node[0];
|
||||
// Unlock when there is nothing to display
|
||||
this.locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessEvents(Event e)
|
||||
{
|
||||
switch (e.type)
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
if (e.button == 0) {
|
||||
// Reset flag
|
||||
nodeMoved = false;
|
||||
// Frist check if any node handle was clicked
|
||||
NodeHandle handle = FindHandle(e.mousePosition);
|
||||
if (handle != null)
|
||||
{
|
||||
currentHandle = handle;
|
||||
e.Use();
|
||||
break;
|
||||
}
|
||||
Node node = FindNode(e.mousePosition);
|
||||
// Select node if contains point
|
||||
if (node != null) {
|
||||
DeselectNode();
|
||||
SelectNode(node);
|
||||
if (e.clickCount == 2 && node is SubTree) {
|
||||
SubTree subTree = node as SubTree;
|
||||
if (subTree.tree != null) {
|
||||
Selection.activeGameObject = subTree.tree.gameObject;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DeselectNode();
|
||||
}
|
||||
e.Use();
|
||||
} else if (e.button == 1) {
|
||||
Node node = FindNode(e.mousePosition);
|
||||
// Open proper context menu
|
||||
if (node != null) {
|
||||
OpenNodeMenu(e.mousePosition, node);
|
||||
} else {
|
||||
DeselectNode();
|
||||
OpenNodeFinder(new Rect(e.mousePosition.x, e.mousePosition.y, 1, 1));
|
||||
}
|
||||
e.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
// Drag node, workspace or connection
|
||||
if (e.button == 0) {
|
||||
if (currentHandle != null) {
|
||||
// Let PaintConnections draw lines
|
||||
} else if (selectedNode != null) {
|
||||
Undo.RecordObject(selectedNode, "Move Node");
|
||||
selectedNode.rect.position += Event.current.delta;
|
||||
// Move whole branch when Ctrl is pressed
|
||||
if (e.control) {
|
||||
List<Node> movedNodes = selectedNode.GetAllSuccessors();
|
||||
for (int i = 0; i < movedNodes.Count; i++)
|
||||
{
|
||||
Undo.RecordObject(movedNodes[i], "Move Node");
|
||||
movedNodes[i].rect.position += Event.current.delta;
|
||||
}
|
||||
}
|
||||
nodeMoved = true;
|
||||
} else {
|
||||
workspaceOffset += Event.current.delta;
|
||||
}
|
||||
GUI.changed = true;
|
||||
e.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (currentHandle != null) {
|
||||
TryConnectNodes(currentHandle, e.mousePosition);
|
||||
}
|
||||
// Reorder or snap nodes in case any of them was moved
|
||||
if (nodeMoved && selectedNode != null) {
|
||||
// Snap nodes if option is enabled
|
||||
if (snapNodesToGrid)
|
||||
{
|
||||
Undo.RecordObject(selectedNode, "Move Node");
|
||||
selectedNode.rect.position = SnapPositionToGrid(selectedNode.rect.position);
|
||||
// When control is pressed snap successors too
|
||||
if (e.control) {
|
||||
List<Node> movedNodes = selectedNode.GetAllSuccessors();
|
||||
for (int i = 0; i < movedNodes.Count; i++)
|
||||
{
|
||||
Undo.RecordObject(movedNodes[i], "Move Node");
|
||||
movedNodes[i].rect.position = SnapPositionToGrid(movedNodes[i].rect.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reorder siblings if selected node has parent
|
||||
if (selectedNode.parent != null)
|
||||
{
|
||||
Undo.RecordObject(selectedNode.parent, "Move Node");
|
||||
selectedNode.parent.SortChildren();
|
||||
}
|
||||
}
|
||||
nodeMoved = false;
|
||||
currentHandle = null;
|
||||
GUI.changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 SnapPositionToGrid(Vector2 position)
|
||||
{
|
||||
return new Vector2(
|
||||
Mathf.Round(position.x / 20f) * 20f,
|
||||
Mathf.Round(position.y / 20f) * 20f
|
||||
);
|
||||
}
|
||||
|
||||
private void TryConnectNodes(NodeHandle handle, Vector2 mousePosition)
|
||||
{
|
||||
// Find hovered node and connect or open dropdown
|
||||
Node targetNode = FindNode(mousePosition);
|
||||
if (targetNode == null) {
|
||||
OpenNodeFinder(new Rect(mousePosition.x, mousePosition.y, 1, 1), true, handle);
|
||||
return;
|
||||
}
|
||||
// Check if they are not the same node
|
||||
if (targetNode == handle.node) {
|
||||
return;
|
||||
}
|
||||
Undo.RecordObject(targetNode, "Connect Nodes");
|
||||
Undo.RecordObject(handle.node, "Connect Nodes");
|
||||
// There is node, try to connect if this is possible
|
||||
if (handle.type == HandleType.Input && targetNode is IParentNode) {
|
||||
// Do not allow connecting descendants as parents
|
||||
if (targetNode.IsDescendantOf(handle.node)) {
|
||||
return;
|
||||
}
|
||||
// Then add as child to new parent
|
||||
targetNode.AddChild(handle.node);
|
||||
// Update order of nodes
|
||||
targetNode.SortChildren();
|
||||
} else if (handle.type == HandleType.Output && targetNode is IChildrenNode) {
|
||||
// Do not allow connecting descendants as parents
|
||||
if (handle.node.IsDescendantOf(targetNode)) {
|
||||
return;
|
||||
}
|
||||
// Then add as child to new parent
|
||||
handle.node.AddChild(targetNode);
|
||||
// Update order of nodes
|
||||
handle.node.SortChildren();
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectNode(Node node)
|
||||
{
|
||||
currentMBT.selectedEditorNode = node;
|
||||
currentMBTEditor.Repaint();
|
||||
node.selected = true;
|
||||
selectedNode = node;
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
private void DeselectNode(Node node)
|
||||
{
|
||||
currentMBT.selectedEditorNode = null;
|
||||
currentMBTEditor.Repaint();
|
||||
node.selected = false;
|
||||
selectedNode = null;
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
private void DeselectNode()
|
||||
{
|
||||
currentMBT.selectedEditorNode = null;
|
||||
currentMBTEditor.Repaint();
|
||||
for (int i = 0; i < currentNodes.Length; i++)
|
||||
{
|
||||
currentNodes[i].selected = false;
|
||||
}
|
||||
selectedNode = null;
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
private Node FindNode(Vector2 mousePosition)
|
||||
{
|
||||
for (int i = 0; i < currentNodes.Length; i++)
|
||||
{
|
||||
// Get correct position of node with offset
|
||||
Rect target = currentNodes[i].rect;
|
||||
target.position += workspaceOffset;
|
||||
if (target.Contains(mousePosition)) {
|
||||
return currentNodes[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private NodeHandle FindHandle(Vector2 mousePosition)
|
||||
{
|
||||
for (int i = 0; i < currentNodes.Length; i++)
|
||||
{
|
||||
Node node = currentNodes[i];
|
||||
// Get correct position of node with offset
|
||||
Rect targetRect = node.rect;
|
||||
targetRect.position += workspaceOffset;
|
||||
|
||||
if (node is IChildrenNode) {
|
||||
Vector2 handlePoint = GetTopHandlePosition(targetRect);
|
||||
if (Vector2.Distance(handlePoint, mousePosition) < _handleDetectionDistance) {
|
||||
return new NodeHandle(node, handlePoint, HandleType.Input);
|
||||
}
|
||||
}
|
||||
if (node is IParentNode) {
|
||||
Vector2 handlePoint = GetBottomHandlePosition(targetRect);
|
||||
if (Vector2.Distance(handlePoint, mousePosition) < _handleDetectionDistance) {
|
||||
return new NodeHandle(node, handlePoint, HandleType.Output);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void PaintNodes()
|
||||
{
|
||||
for (int i = currentNodes.Length - 1; i >= 0 ; i--)
|
||||
{
|
||||
Node node = currentNodes[i];
|
||||
Rect targetRect = node.rect;
|
||||
targetRect.position += workspaceOffset;
|
||||
// Draw node content
|
||||
GUILayout.BeginArea(targetRect, GetNodeStyle(node));
|
||||
GUILayout.BeginVertical(_nodeContentBoxStyle);
|
||||
if (node.breakpoint)
|
||||
{
|
||||
GUILayout.Label(node.title, _nodeBreakpointLabelStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label(node.title, _nodeLabelStyle);
|
||||
}
|
||||
GUILayout.EndVertical();
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
node.rect.height = GUILayoutUtility.GetLastRect().height;
|
||||
}
|
||||
GUILayout.EndArea();
|
||||
|
||||
// Paint warning icon
|
||||
if (!Application.isPlaying && !node.IsValid())
|
||||
{
|
||||
GUI.Label(GetWarningIconRect(targetRect), EditorGUIUtility.IconContent("CollabConflict Icon"));
|
||||
}
|
||||
|
||||
// Draw connection handles if needed
|
||||
if (node is IChildrenNode)
|
||||
{
|
||||
Vector2 top = GetTopHandlePosition(targetRect);
|
||||
GUI.DrawTexture(
|
||||
new Rect(top.x-8, top.y-5, 16, 16),
|
||||
Resources.Load("mbt_node_handle", typeof(Texture2D)) as Texture2D
|
||||
);
|
||||
}
|
||||
if (node is IParentNode)
|
||||
{
|
||||
Vector2 bottom = GetBottomHandlePosition(targetRect);
|
||||
GUI.DrawTexture(
|
||||
new Rect(bottom.x-8, bottom.y-11, 16, 16),
|
||||
Resources.Load("mbt_node_handle", typeof(Texture2D)) as Texture2D
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GUIStyle GetNodeStyle(Node node)
|
||||
{
|
||||
if (node.selected) {
|
||||
return _selectedNodeStyle;
|
||||
}
|
||||
switch (node.status)
|
||||
{
|
||||
case Status.Success:
|
||||
return _successNodeStyle;
|
||||
case Status.Failure:
|
||||
return _failureNodeStyle;
|
||||
case Status.Running:
|
||||
return _runningNodeStyle;
|
||||
}
|
||||
return _defaultNodeStyle;
|
||||
}
|
||||
|
||||
private Vector2 GetTopHandlePosition(Rect rect)
|
||||
{
|
||||
return new Vector2(rect.x + rect.width/2, rect.y);
|
||||
}
|
||||
|
||||
private Vector2 GetBottomHandlePosition(Rect rect)
|
||||
{
|
||||
return new Vector2(rect.x + rect.width/2, rect.y + rect.height);
|
||||
}
|
||||
|
||||
private Rect GetWarningIconRect(Rect rect)
|
||||
{
|
||||
// return new Rect(rect.x - 10, rect.y + rect.height/2 - 10 , 20, 20);
|
||||
return new Rect(rect.x + rect.width - 2, rect.y - 1, 20, 20);
|
||||
}
|
||||
|
||||
private void OpenNodeFinder(Rect rect, bool useRectPosition = true, NodeHandle handle = null)
|
||||
{
|
||||
// Store handle to connect later (null by default)
|
||||
dropdownHandleCache = handle;
|
||||
// Store real clicked position including workspace offset
|
||||
if (useRectPosition) {
|
||||
nodeDropdownTargetPosition = rect.position - workspaceOffset;
|
||||
} else {
|
||||
nodeDropdownTargetPosition = new Vector2(this.position.width/2, this.position.height/2) - workspaceOffset;
|
||||
}
|
||||
// Open dropdown
|
||||
nodeDropdown.Show(rect);
|
||||
}
|
||||
|
||||
private void OpenNodeMenu(Vector2 mousePosition, Node node)
|
||||
{
|
||||
GenericMenu genericMenu = new GenericMenu();
|
||||
genericMenu.AddItem(new GUIContent("Breakpoint"), node.breakpoint, () => ToggleNodeBreakpoint(node));
|
||||
genericMenu.AddItem(new GUIContent("Duplicate"), false, () => DuplicateNode(node));
|
||||
genericMenu.AddItem(new GUIContent("Disconnect Children"), false, () => DisconnectNodeChildren(node));
|
||||
genericMenu.AddItem(new GUIContent("Disconnect Parent"), false, () => DisconnectNodeParent(node));
|
||||
genericMenu.AddItem(new GUIContent("Delete Node"), false, () => DeleteNode(node));
|
||||
genericMenu.ShowAsContext();
|
||||
}
|
||||
|
||||
void AddNode(ClassTypeDropdownItem item)
|
||||
{
|
||||
// In case there is nothing to add
|
||||
if (currentMBT == null || item.classType == null) {
|
||||
return;
|
||||
}
|
||||
// Allow only one root
|
||||
if (item.classType.IsAssignableFrom(typeof(Root)) && currentMBT.GetComponent<Root>() != null) {
|
||||
Debug.LogWarning("You can not add more than one Root node.");
|
||||
return;
|
||||
}
|
||||
Undo.SetCurrentGroupName("Create Node");
|
||||
Node node = (Node)Undo.AddComponent(currentMBT.gameObject, item.classType);
|
||||
node.title = item.name;
|
||||
node.hideFlags = HideFlags.HideInInspector;
|
||||
node.rect.position = nodeDropdownTargetPosition - new Vector2(node.rect.width/2, 0);
|
||||
UpdateSelection();
|
||||
if (dropdownHandleCache != null) {
|
||||
// Add additonal offset (3,3) to be sure that point is inside rect
|
||||
TryConnectNodes(dropdownHandleCache, nodeDropdownTargetPosition + workspaceOffset + new Vector2(3,3));
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleNodeBreakpoint(Node node)
|
||||
{
|
||||
// Toggle breakpoint flag
|
||||
// Undo.RecordObject(node, "Toggle Breakpoint");
|
||||
node.breakpoint = !node.breakpoint;
|
||||
}
|
||||
|
||||
private void DeleteNode(Node node)
|
||||
{
|
||||
if (currentMBT == null) {
|
||||
return;
|
||||
}
|
||||
DeselectNode();
|
||||
// Disconnect all children and parent
|
||||
Undo.SetCurrentGroupName("Delete Node");
|
||||
DisconnectNodeChildren(node);
|
||||
DisconnectNodeParent(node);
|
||||
Undo.DestroyObjectImmediate(node);
|
||||
// DestroyImmediate(node, true);
|
||||
UpdateSelection();
|
||||
}
|
||||
|
||||
private void DisconnectNodeParent(Node node)
|
||||
{
|
||||
if (node.parent != null) {
|
||||
Undo.RecordObject(node, "Disconnect Parent");
|
||||
Undo.RecordObject(node.parent, "Disconnect Parent");
|
||||
node.parent.RemoveChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void DisconnectNodeChildren(Node node)
|
||||
{
|
||||
Undo.RecordObject(node, "Disconnect Children");
|
||||
for (int i = node.children.Count - 1; i >= 0 ; i--)
|
||||
{
|
||||
Undo.RecordObject(node.children[i], "Disconnect Children");
|
||||
node.RemoveChild(node.children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void DuplicateNode(Node contextNode)
|
||||
{
|
||||
// NOTE: This code is mostly copied from AddNode()
|
||||
// Check if there is MBT
|
||||
if (currentMBT == null) {
|
||||
return;
|
||||
}
|
||||
System.Type classType = contextNode.GetType();
|
||||
// Allow only one root
|
||||
if (classType.IsAssignableFrom(typeof(Root)) && currentMBT.GetComponent<Root>() != null) {
|
||||
Debug.LogWarning("You can not add more than one Root node.");
|
||||
return;
|
||||
}
|
||||
Undo.SetCurrentGroupName("Duplicate Node");
|
||||
Node node = (Node)Undo.AddComponent(currentMBT.gameObject, classType);
|
||||
// Copy values
|
||||
EditorUtility.CopySerialized(contextNode, node);
|
||||
// Set flags anyway to ensure it is not visible in inspector
|
||||
node.hideFlags = HideFlags.HideInInspector;
|
||||
node.rect.position = contextNode.rect.position + new Vector2(20, 20);
|
||||
// Remove all connections or graph gonna break
|
||||
node.parent = null;
|
||||
node.children.Clear();
|
||||
UpdateSelection();
|
||||
}
|
||||
|
||||
/// It is quite unique, but https://stackoverflow.com/questions/2920696/how-generate-unique-integers-based-on-guids
|
||||
private int GenerateId()
|
||||
{
|
||||
return System.Guid.NewGuid().GetHashCode();
|
||||
}
|
||||
|
||||
private void PaintBackground()
|
||||
{
|
||||
// Background
|
||||
Handles.BeginGUI();
|
||||
Handles.DrawSolidRectangleWithOutline(new Rect(0, 0, position.width, position.height), _editorBackgroundColor, Color.gray);
|
||||
Handles.EndGUI();
|
||||
// Grid lines
|
||||
DrawBackgroundGrid(20, 0.1f, new Color(0.3f, 0.36f, 0.5f));
|
||||
DrawBackgroundGrid(100, 0.2f, new Color(0.3f, 0.36f, 0.5f));
|
||||
}
|
||||
|
||||
/// Method copied from https://gram.gs/gramlog/creating-node-based-editor-unity/
|
||||
private void DrawBackgroundGrid(float gridSpacing, float gridOpacity, Color gridColor)
|
||||
{
|
||||
int widthDivs = Mathf.CeilToInt(position.width / gridSpacing);
|
||||
int heightDivs = Mathf.CeilToInt(position.height / gridSpacing);
|
||||
|
||||
Handles.BeginGUI();
|
||||
|
||||
Handles.color = new Color(gridColor.r, gridColor.g, gridColor.b, gridOpacity);
|
||||
|
||||
Vector3 newOffset = new Vector3(workspaceOffset.x % gridSpacing, workspaceOffset.y % gridSpacing, 0);
|
||||
|
||||
for (int i = 0; i <= widthDivs; i++)
|
||||
{
|
||||
Handles.DrawLine(new Vector3(gridSpacing * i, -gridSpacing, 0) + newOffset, new Vector3(gridSpacing * i, position.height+gridSpacing, 0f) + newOffset);
|
||||
}
|
||||
|
||||
for (int j = 0; j <= heightDivs; j++)
|
||||
{
|
||||
Handles.DrawLine(new Vector3(-gridSpacing, gridSpacing * j, 0) + newOffset, new Vector3(position.width+gridSpacing, gridSpacing * j, 0f) + newOffset);
|
||||
}
|
||||
|
||||
Handles.color = Color.white;
|
||||
Handles.EndGUI();
|
||||
}
|
||||
|
||||
private class NodeHandle
|
||||
{
|
||||
public Node node;
|
||||
public Vector2 position;
|
||||
public HandleType type;
|
||||
|
||||
public NodeHandle(Node node, Vector2 position, HandleType type)
|
||||
{
|
||||
this.node = node;
|
||||
this.position = position;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
private enum HandleType
|
||||
{
|
||||
Input, Output
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 238d35a99c7b4b14da156f1c64f9e342
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
210
Assets/MonoBehaviourTree/Source/Editor/BlackboardEditor.cs
Normal file
210
Assets/MonoBehaviourTree/Source/Editor/BlackboardEditor.cs
Normal file
@ -0,0 +1,210 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomEditor(typeof(Blackboard))]
|
||||
public class BlackboardEditor : Editor
|
||||
{
|
||||
const double CONSTANT_REPAINT_INTERVAL = 0.5d;
|
||||
|
||||
readonly string[] varOptions = new string[]{"Delete"};
|
||||
SerializedProperty variables;
|
||||
SerializedProperty masterBlackboardProperty;
|
||||
GUIStyle popupStyle;
|
||||
string newVariableKey = "";
|
||||
string[] variableTypesNames = new string[0];
|
||||
Type[] variableTypes = new Type[0];
|
||||
int selectedVariableType = 0;
|
||||
Blackboard blackboard;
|
||||
GameObject blackboardGameObject;
|
||||
bool showVariables = true;
|
||||
private double lastRepaint;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
// Set hide flags in case object was duplicated or turned into prefab
|
||||
if (target != null)
|
||||
{
|
||||
Blackboard bb = (Blackboard) target;
|
||||
// Sample one variable and check if its hidden. Hide all varialbes if sample is visible.
|
||||
if (bb.TryGetComponent<BlackboardVariable>(out BlackboardVariable bv) && bv.hideFlags != HideFlags.HideInInspector)
|
||||
{
|
||||
BlackboardVariable[] vars = bb.GetComponents<BlackboardVariable>();
|
||||
for (int i = 0; i < vars.Length; i++)
|
||||
{
|
||||
vars[i].hideFlags = HideFlags.HideInInspector;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Init
|
||||
variables = serializedObject.FindProperty("variables");
|
||||
masterBlackboardProperty = serializedObject.FindProperty("masterBlackboard");
|
||||
blackboard = target as Blackboard;
|
||||
blackboardGameObject = blackboard.gameObject;
|
||||
SetupVariableTypes();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
// Remove all variables in case Blackboard was removed
|
||||
if (Application.isEditor && (Blackboard)target == null && blackboardGameObject != null)
|
||||
{
|
||||
// Additional check to avoid errors when exiting playmode
|
||||
if (Application.IsPlaying(blackboardGameObject) || blackboardGameObject.GetComponent<Blackboard>() != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BlackboardVariable[] blackboardVariables = blackboardGameObject.GetComponents<BlackboardVariable>();
|
||||
for (int i = 0; i < blackboardVariables.Length; i++)
|
||||
{
|
||||
Undo.DestroyObjectImmediate(blackboardVariables[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RequiresConstantRepaint()
|
||||
{
|
||||
return Application.isPlaying && EditorApplication.timeSinceStartup > lastRepaint + CONSTANT_REPAINT_INTERVAL;
|
||||
}
|
||||
|
||||
private void SetupVariableTypes()
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
List<string> names = new List<string>();
|
||||
|
||||
// Find all types
|
||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
IEnumerable<Type> enumerable = assembly.GetTypes()
|
||||
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(BlackboardVariable)));
|
||||
foreach (Type type in enumerable)
|
||||
{
|
||||
names.Add(type.Name);
|
||||
types.Add(type);
|
||||
}
|
||||
}
|
||||
variableTypesNames = names.ToArray();
|
||||
variableTypes = types.ToArray();
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
// Update repaint timer
|
||||
lastRepaint = EditorApplication.timeSinceStartup;
|
||||
// Init styles
|
||||
if (popupStyle == null) {
|
||||
popupStyle = new GUIStyle(GUI.skin.GetStyle("PaneOptions"));
|
||||
popupStyle.imagePosition = ImagePosition.ImageOnly;
|
||||
popupStyle.margin.top += 3;
|
||||
}
|
||||
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(masterBlackboardProperty);
|
||||
EditorGUILayout.Space();
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
// Fields used to add variables
|
||||
EditorGUILayout.LabelField("Create Variable", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Key", GUILayout.MaxWidth(80));
|
||||
newVariableKey = EditorGUILayout.TextField(newVariableKey);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Type", GUILayout.MaxWidth(80));
|
||||
selectedVariableType = EditorGUILayout.Popup(selectedVariableType, variableTypesNames);
|
||||
GUI.SetNextControlName("AddButton");
|
||||
if (GUILayout.Button("Add", EditorStyles.miniButton)) {
|
||||
CreateVariableAndResetInput();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// DrawDefaultInspector();
|
||||
// EditorGUILayout.Space();
|
||||
|
||||
// serializedObject.Update();
|
||||
// EditorGUI.BeginChangeCheck();
|
||||
showVariables = EditorGUILayout.BeginFoldoutHeaderGroup(showVariables, "Variables");
|
||||
if(showVariables){
|
||||
SerializedProperty vars = variables.Copy();
|
||||
if (vars.isArray) {
|
||||
// xxx: Why this line existed? Why EventType.DragPerform is not allowed here? (maybe BeginChangeCheck)
|
||||
// if (vars.isArray && Event.current.type != EventType.DragPerform) {
|
||||
for (int i = 0; i < vars.arraySize; i++)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int popupOption = -1;
|
||||
SerializedProperty serializedV = vars.GetArrayElementAtIndex(i);
|
||||
SerializedObject serializedVariable = new SerializedObject(serializedV.objectReferenceValue);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel(
|
||||
new GUIContent(serializedVariable.FindProperty("key").stringValue,
|
||||
serializedVariable.targetObject.GetType().Name)
|
||||
);
|
||||
int v = EditorGUILayout.Popup(popupOption, varOptions, popupStyle, GUILayout.MaxWidth(20));
|
||||
EditorGUILayout.PropertyField(serializedVariable.FindProperty("val"), GUIContent.none);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
serializedVariable.ApplyModifiedProperties();
|
||||
}
|
||||
// Delete on change
|
||||
if (v != popupOption) {
|
||||
DeleteVariabe(serializedV.objectReferenceValue as BlackboardVariable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// if (EditorGUI.EndChangeCheck()) {
|
||||
// serializedObject.ApplyModifiedProperties();
|
||||
// }
|
||||
}
|
||||
|
||||
private void DeleteVariabe(BlackboardVariable blackboardVariable)
|
||||
{
|
||||
Undo.RecordObject(blackboard, "Delete Blackboard Variable");
|
||||
blackboard.variables.Remove(blackboardVariable);
|
||||
Undo.DestroyObjectImmediate(blackboardVariable);
|
||||
}
|
||||
|
||||
private void CreateVariableAndResetInput()
|
||||
{
|
||||
// Validate field. Key "None" is not allowed.
|
||||
if (string.IsNullOrEmpty(newVariableKey) || newVariableKey.Equals("None")) {
|
||||
return;
|
||||
}
|
||||
string k = new string( newVariableKey.ToCharArray().Where(c => !Char.IsWhiteSpace(c)).ToArray() );
|
||||
// Check for key duplicates
|
||||
for (int i = 0; i < blackboard.variables.Count; i++)
|
||||
{
|
||||
if (blackboard.variables[i].key == k) {
|
||||
Debug.LogWarning("Variable '"+k+"' already exists.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Add variable
|
||||
Undo.RecordObject(blackboard, "Create Blackboard Variable");
|
||||
BlackboardVariable var = Undo.AddComponent(blackboard.gameObject, variableTypes[selectedVariableType]) as BlackboardVariable;
|
||||
var.hideFlags = HideFlags.HideInInspector;
|
||||
var.key = k;
|
||||
blackboard.variables.Add(var);
|
||||
// Reset field
|
||||
newVariableKey = "";
|
||||
GUI.FocusControl("Clear");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2b8d2392660b6e46bcbb8bd44a1be30
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
126
Assets/MonoBehaviourTree/Source/Editor/InvokeUnityEventEditor.cs
Normal file
126
Assets/MonoBehaviourTree/Source/Editor/InvokeUnityEventEditor.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using MBT;
|
||||
using System;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomEditor(typeof(InvokeUnityEvent))]
|
||||
public class InvokeUnityEventEditor : Editor
|
||||
{
|
||||
SerializedProperty titleProp;
|
||||
SerializedProperty typeProp;
|
||||
|
||||
private SerializedProperty transformEventProp;
|
||||
private SerializedProperty gameObjectEventProp;
|
||||
private SerializedProperty floatEventProp;
|
||||
private SerializedProperty intEventProp;
|
||||
private SerializedProperty boolEventProp;
|
||||
private SerializedProperty stringEventProp;
|
||||
private SerializedProperty vector3EventProp;
|
||||
private SerializedProperty vector2EventProp;
|
||||
|
||||
private SerializedProperty transformReferenceProp;
|
||||
private SerializedProperty gameObjectReferenceProp;
|
||||
private SerializedProperty floatReferenceProp;
|
||||
private SerializedProperty intReferenceProp;
|
||||
private SerializedProperty boolReferenceProp;
|
||||
private SerializedProperty stringReferenceProp;
|
||||
private SerializedProperty vector3ReferenceProp;
|
||||
private SerializedProperty vector2ReferenceProp;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
titleProp = serializedObject.FindProperty("title");
|
||||
typeProp = serializedObject.FindProperty("type");
|
||||
|
||||
transformEventProp = serializedObject.FindProperty("transformEvent");
|
||||
gameObjectEventProp = serializedObject.FindProperty("gameObjectEvent");
|
||||
floatEventProp = serializedObject.FindProperty("floatEvent");
|
||||
intEventProp = serializedObject.FindProperty("intEvent");
|
||||
boolEventProp = serializedObject.FindProperty("boolEvent");
|
||||
stringEventProp = serializedObject.FindProperty("stringEvent");
|
||||
vector3EventProp = serializedObject.FindProperty("vector3Event");
|
||||
vector2EventProp = serializedObject.FindProperty("vector2Event");
|
||||
|
||||
transformReferenceProp = serializedObject.FindProperty("transformReference");
|
||||
gameObjectReferenceProp = serializedObject.FindProperty("gameObjectReference");
|
||||
floatReferenceProp = serializedObject.FindProperty("floatReference");
|
||||
intReferenceProp = serializedObject.FindProperty("intReference");
|
||||
boolReferenceProp = serializedObject.FindProperty("boolReference");
|
||||
stringReferenceProp = serializedObject.FindProperty("stringReference");
|
||||
vector3ReferenceProp = serializedObject.FindProperty("vector3Reference");
|
||||
vector2ReferenceProp = serializedObject.FindProperty("vector2Reference");
|
||||
}
|
||||
|
||||
private static readonly GUIContent variableLabel = new GUIContent("Parameter");
|
||||
private static readonly GUIContent eventLabel = new GUIContent("Event");
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(titleProp);
|
||||
EditorGUILayout.PropertyField(typeProp);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (GetSerializedProperties(out SerializedProperty eventProp, out SerializedProperty variableProp))
|
||||
{
|
||||
EditorGUILayout.PropertyField(variableProp, variableLabel);
|
||||
EditorGUILayout.PropertyField(eventProp, eventLabel);
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetSerializedProperties(out SerializedProperty eventProp, out SerializedProperty referenceProp)
|
||||
{
|
||||
InvokeUnityEvent.EventType eventType = (InvokeUnityEvent.EventType)typeProp.enumValueIndex;
|
||||
switch (eventType)
|
||||
{
|
||||
case InvokeUnityEvent.EventType.Transform:
|
||||
eventProp = transformEventProp;
|
||||
referenceProp = transformReferenceProp;
|
||||
return true;
|
||||
case InvokeUnityEvent.EventType.Float:
|
||||
eventProp = floatEventProp;
|
||||
referenceProp = floatReferenceProp;
|
||||
return true;
|
||||
case InvokeUnityEvent.EventType.GameObject:
|
||||
eventProp = gameObjectEventProp;
|
||||
referenceProp = gameObjectReferenceProp;
|
||||
return true;
|
||||
case InvokeUnityEvent.EventType.Int:
|
||||
eventProp = intEventProp;
|
||||
referenceProp = intReferenceProp;
|
||||
return true;
|
||||
case InvokeUnityEvent.EventType.String:
|
||||
eventProp = stringEventProp;
|
||||
referenceProp = stringReferenceProp;
|
||||
return true;
|
||||
case InvokeUnityEvent.EventType.Vector3:
|
||||
eventProp = vector3EventProp;
|
||||
referenceProp = vector3ReferenceProp;
|
||||
return true;
|
||||
case InvokeUnityEvent.EventType.Vector2:
|
||||
eventProp = vector2EventProp;
|
||||
referenceProp = vector2ReferenceProp;
|
||||
return true;
|
||||
case InvokeUnityEvent.EventType.Bool:
|
||||
eventProp = boolEventProp;
|
||||
referenceProp = boolReferenceProp;
|
||||
return true;
|
||||
default:
|
||||
eventProp = null;
|
||||
referenceProp = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 007923a936f7a7d4a9e8b8bc59f97592
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,60 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomEditor(typeof(IsSetCondition))]
|
||||
public class IsSetConditionEditor : Editor
|
||||
{
|
||||
SerializedProperty titleProp;
|
||||
SerializedProperty abortProp;
|
||||
SerializedProperty boolReferenceProp;
|
||||
SerializedProperty objectReferenceProp;
|
||||
SerializedProperty transformReferenceProp;
|
||||
SerializedProperty typeProp;
|
||||
SerializedProperty invertProp;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
titleProp = serializedObject.FindProperty("title");
|
||||
boolReferenceProp = serializedObject.FindProperty("boolReference");
|
||||
objectReferenceProp = serializedObject.FindProperty("objectReference");
|
||||
transformReferenceProp = serializedObject.FindProperty("transformReference");
|
||||
abortProp = serializedObject.FindProperty("abort");
|
||||
typeProp = serializedObject.FindProperty("type");
|
||||
invertProp = serializedObject.FindProperty("invert");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(titleProp);
|
||||
EditorGUILayout.PropertyField(abortProp);
|
||||
EditorGUILayout.PropertyField(invertProp);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(typeProp);
|
||||
if (typeProp.enumValueIndex == (int)IsSetCondition.Type.Boolean)
|
||||
{
|
||||
EditorGUILayout.PropertyField(boolReferenceProp, new GUIContent("Variable"));
|
||||
}
|
||||
else if (typeProp.enumValueIndex == (int)IsSetCondition.Type.GameObject)
|
||||
{
|
||||
EditorGUILayout.PropertyField(objectReferenceProp, new GUIContent("Variable"));
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.PropertyField(transformReferenceProp, new GUIContent("Variable"));
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45cfc361407c0f4448f74c8395194f6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,87 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomEditor(typeof(MonoBehaviourTree))]
|
||||
public class MonoBehaviourTreeEditor : Editor
|
||||
{
|
||||
private GUIStyle boxStyle;
|
||||
private GUIStyle foldStyle;
|
||||
private Editor nodeEditor;
|
||||
|
||||
void InitStyle()
|
||||
{
|
||||
if (foldStyle == null)
|
||||
{
|
||||
boxStyle = new GUIStyle(EditorStyles.helpBox);
|
||||
foldStyle = new GUIStyle(EditorStyles.foldoutHeader);
|
||||
foldStyle.onNormal = foldStyle.onFocused;
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
// Set hide flags in case object was duplicated or turned into prefab
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MonoBehaviourTree mbt = (MonoBehaviourTree) target;
|
||||
// Sample one component and check if its hidden. Hide all nodes if sample is visible.
|
||||
if (mbt.TryGetComponent<Node>(out Node n) && n.hideFlags != HideFlags.HideInInspector)
|
||||
{
|
||||
Node[] nodes = mbt.GetComponents<Node>();
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
nodes[i].hideFlags = HideFlags.HideInInspector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
// Destroy editor if there is any
|
||||
if (nodeEditor != null)
|
||||
{
|
||||
DestroyImmediate(nodeEditor);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
InitStyle();
|
||||
|
||||
DrawDefaultInspector();
|
||||
GUILayout.Space(5);
|
||||
|
||||
if (GUILayout.Button("Open editor")) {
|
||||
BehaviourTreeWindow.OpenEditor();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
MonoBehaviourTree mbt = ((MonoBehaviourTree) target);
|
||||
bool renderNodeInspector = mbt.selectedEditorNode != null;
|
||||
|
||||
EditorGUILayout.Foldout(renderNodeInspector, "Node inspector", foldStyle);
|
||||
EditorGUILayout.Space(1);
|
||||
if (renderNodeInspector)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(boxStyle);
|
||||
GUILayout.Space(3);
|
||||
EditorGUILayout.BeginVertical();
|
||||
GUILayout.Space(5);
|
||||
Editor.CreateCachedEditor(mbt.selectedEditorNode, null, ref nodeEditor);
|
||||
nodeEditor.OnInspectorGUI();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f78abb7d7a304aa41a91ef451cc8cb6e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
143
Assets/MonoBehaviourTree/Source/Editor/NodeDropdown.cs
Normal file
143
Assets/MonoBehaviourTree/Source/Editor/NodeDropdown.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
public class ClassTypeDropdownItem : AdvancedDropdownItem
|
||||
{
|
||||
public Type classType;
|
||||
public int order;
|
||||
public string path;
|
||||
|
||||
public ClassTypeDropdownItem(string name, Type type = null, int order = 1000, string path = "") : base(name)
|
||||
{
|
||||
this.classType = type;
|
||||
this.order = order;
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
|
||||
public class NodeDropdown : AdvancedDropdown
|
||||
{
|
||||
protected Action<ClassTypeDropdownItem> Callback;
|
||||
|
||||
public NodeDropdown(AdvancedDropdownState state, Action<ClassTypeDropdownItem> callback) : base(state)
|
||||
{
|
||||
this.Callback = callback;
|
||||
minimumSize = new Vector2(230,320);
|
||||
}
|
||||
|
||||
protected override AdvancedDropdownItem BuildRoot()
|
||||
{
|
||||
var root = new ClassTypeDropdownItem("Nodes");
|
||||
|
||||
// List for all found subclasses
|
||||
List<Type> results = new List<Type>();
|
||||
|
||||
// Search all assemblies
|
||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
// Find all subclasses of Node
|
||||
IEnumerable<Type> enumerable = assembly.GetTypes()
|
||||
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(Node)));
|
||||
results.AddRange(enumerable);
|
||||
}
|
||||
|
||||
// Keep track of all paths to correctly build tree later
|
||||
Dictionary<string, ClassTypeDropdownItem> nodePathsDictionary = new Dictionary<string, ClassTypeDropdownItem>();
|
||||
nodePathsDictionary.Add("", root);
|
||||
// Create list of items
|
||||
List<ClassTypeDropdownItem> items = new List<ClassTypeDropdownItem>();
|
||||
foreach (Type type in results)
|
||||
{
|
||||
if(type.IsDefined(typeof(MBTNode), false))
|
||||
{
|
||||
MBTNode nodeMeta = type.GetCustomAttribute<MBTNode>();
|
||||
string itemName;
|
||||
string nodePath = "";
|
||||
if (String.IsNullOrEmpty(nodeMeta.name))
|
||||
{
|
||||
itemName = type.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] path = nodeMeta.name.Split('/');
|
||||
itemName = path[path.Length-1];
|
||||
nodePath = BuildPathIfNotExists(path, ref nodePathsDictionary);
|
||||
}
|
||||
ClassTypeDropdownItem classTypeDropdownItem = new ClassTypeDropdownItem(itemName, type, nodeMeta.order, nodePath);
|
||||
if (nodeMeta.icon != null)
|
||||
{
|
||||
classTypeDropdownItem.icon = Resources.Load(nodeMeta.icon, typeof(Texture2D)) as Texture2D;
|
||||
}
|
||||
items.Add(classTypeDropdownItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort items
|
||||
items.Sort((x, y) => {
|
||||
int result = x.order.CompareTo(y.order);
|
||||
return result != 0 ? result : x.name.CompareTo(y.name);
|
||||
});
|
||||
|
||||
// Add all nodes to menu
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
nodePathsDictionary[items[i].path].AddChild(items[i]);
|
||||
}
|
||||
|
||||
// Remove root to avoid infinite root folder loop
|
||||
nodePathsDictionary.Remove("");
|
||||
List<ClassTypeDropdownItem> parentNodes = nodePathsDictionary.Values.ToList();
|
||||
parentNodes.Sort((x, y) => {
|
||||
return x.name.CompareTo(y.name);
|
||||
});
|
||||
|
||||
// Add folders
|
||||
for (int i = 0; i < parentNodes.Count(); i++)
|
||||
{
|
||||
root.AddChild(parentNodes[i]);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void ItemSelected(AdvancedDropdownItem item)
|
||||
{
|
||||
Callback(item as ClassTypeDropdownItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates nodes if path does not exists. Supports only signle level folders.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to build. Last element should be actual node name.</param>
|
||||
/// <param name="dictionary">Reference to dictionary to store references to items</param>
|
||||
/// <returns>Path to provided node in path</returns>
|
||||
protected string BuildPathIfNotExists(string[] path, ref Dictionary<string, ClassTypeDropdownItem> dictionary)
|
||||
{
|
||||
// IMPORTANT: This code supports only single level folders. Nodes can't be nested more than one level.
|
||||
if (path.Length != 2)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
AdvancedDropdownItem root = dictionary[""];
|
||||
// // This code assumes the last element of path is actual name of node
|
||||
// string nodePath = String.Join("/", path, 0, path.Length-1);
|
||||
string nodePath = path[0];
|
||||
// Create path nodes if does not exists
|
||||
if(!dictionary.ContainsKey(nodePath))
|
||||
{
|
||||
ClassTypeDropdownItem node = new ClassTypeDropdownItem(nodePath);
|
||||
dictionary.Add(nodePath, node);
|
||||
}
|
||||
return nodePath;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/MonoBehaviourTree/Source/Editor/NodeDropdown.cs.meta
Normal file
11
Assets/MonoBehaviourTree/Source/Editor/NodeDropdown.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adc3d2af9116c5b418b63f5da3dc6feb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,66 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomEditor(typeof(NumberCondition))]
|
||||
public class NumberConditionEditor : Editor
|
||||
{
|
||||
SerializedProperty titleProp;
|
||||
SerializedProperty abortProp;
|
||||
SerializedProperty floatReferenceProp;
|
||||
SerializedProperty intReferenceProp;
|
||||
SerializedProperty floatReference2Prop;
|
||||
SerializedProperty intReference2Prop;
|
||||
SerializedProperty typeProp;
|
||||
SerializedProperty comparatorProp;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
titleProp = serializedObject.FindProperty("title");
|
||||
floatReferenceProp = serializedObject.FindProperty("floatReference");
|
||||
intReferenceProp = serializedObject.FindProperty("intReference");
|
||||
floatReference2Prop = serializedObject.FindProperty("floatReference2");
|
||||
intReference2Prop = serializedObject.FindProperty("intReference2");
|
||||
abortProp = serializedObject.FindProperty("abort");
|
||||
typeProp = serializedObject.FindProperty("type");
|
||||
comparatorProp = serializedObject.FindProperty("comparator");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(titleProp);
|
||||
EditorGUILayout.PropertyField(abortProp);
|
||||
EditorGUILayout.PropertyField(typeProp);
|
||||
EditorGUILayout.Space();
|
||||
// GUILayout.Label("Condition");
|
||||
if (typeProp.enumValueIndex == (int)NumberCondition.Type.Float)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(floatReferenceProp, GUIContent.none);
|
||||
EditorGUILayout.PropertyField(comparatorProp, GUIContent.none, GUILayout.MaxWidth(60f));
|
||||
EditorGUILayout.PropertyField(floatReference2Prop, GUIContent.none);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(intReferenceProp, GUIContent.none);
|
||||
EditorGUILayout.PropertyField(comparatorProp, GUIContent.none, GUILayout.MaxWidth(60f));
|
||||
EditorGUILayout.PropertyField(intReference2Prop, GUIContent.none);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b9e3631627d49b4f8349f9efdf7d65a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Qriva.MonoBehaviourTree.Editor",
|
||||
"references": [
|
||||
"GUID:b94eb722e14bb88498f76d28e55be3fa"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2da7737aab07f248add22487334a422
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/MonoBehaviourTree/Source/Editor/Resources.meta
Normal file
8
Assets/MonoBehaviourTree/Source/Editor/Resources.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af68a1f59256fd94f95d09ca43a95485
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
93
Assets/MonoBehaviourTree/Source/Editor/Resources/OFL.txt
Normal file
93
Assets/MonoBehaviourTree/Source/Editor/Resources/OFL.txt
Normal file
@ -0,0 +1,93 @@
|
||||
Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato"
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 217896aaf46e40f4bb3a0d03862bac36
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,23 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4230cef5e36d34240987d46954f779ad
|
||||
TrueTypeFontImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
fontSize: 16
|
||||
forceTextureCase: -2
|
||||
characterSpacing: 0
|
||||
characterPadding: 1
|
||||
includeFontData: 1
|
||||
fontName: Lato
|
||||
fontNames:
|
||||
- Lato
|
||||
fallbackFontReferences:
|
||||
- {fileID: 12800000, guid: 7dbc24fe8e0f7e34ebaaf3637032d72e, type: 3}
|
||||
customCharacters:
|
||||
fontRenderingMode: 0
|
||||
ascentCalculationMode: 1
|
||||
useLegacyBoundsCalculation: 0
|
||||
shouldRoundAdvanceValue: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,22 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7dbc24fe8e0f7e34ebaaf3637032d72e
|
||||
TrueTypeFontImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
fontSize: 16
|
||||
forceTextureCase: -2
|
||||
characterSpacing: 0
|
||||
characterPadding: 1
|
||||
includeFontData: 1
|
||||
fontName: Lato
|
||||
fontNames:
|
||||
- Lato
|
||||
fallbackFontReferences: []
|
||||
customCharacters:
|
||||
fontRenderingMode: 0
|
||||
ascentCalculationMode: 1
|
||||
useLegacyBoundsCalculation: 0
|
||||
shouldRoundAdvanceValue: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_blackboard.png
(Stored with Git LFS)
Normal file
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_blackboard.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf1e8cb5def7ab848bce542b344bd1aa
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_default.png
(Stored with Git LFS)
Normal file
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_default.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
fileFormatVersion: 2
|
||||
guid: befeed4cb9f7e5244a25b4a259043d62
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_failure.png
(Stored with Git LFS)
Normal file
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_failure.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5dae9a45f93952469a039fd15b3734c
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_handle.png
(Stored with Git LFS)
Normal file
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_handle.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fc36e2d5dcad514c94db17908f0f6ed
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_running.png
(Stored with Git LFS)
Normal file
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_running.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7731c28d00532743aa0d1a873443d46
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_selected.png
(Stored with Git LFS)
Normal file
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_selected.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9a060d6ca9704e42914638f91bda04e
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_success.png
(Stored with Git LFS)
Normal file
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_node_success.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c5d9dfbf21fcf746854f7ccbe787ebb
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_window_icon.png
(Stored with Git LFS)
Normal file
BIN
Assets/MonoBehaviourTree/Source/Editor/Resources/mbt_window_icon.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e319fb1e46299e4bb368bf700097d39
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 8192
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
66
Assets/MonoBehaviourTree/Source/Editor/SetNumberEditor.cs
Normal file
66
Assets/MonoBehaviourTree/Source/Editor/SetNumberEditor.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomEditor(typeof(SetNumber))]
|
||||
public class SetNumberEditor : Editor
|
||||
{
|
||||
SerializedProperty titleProp;
|
||||
SerializedProperty typeProp;
|
||||
SerializedProperty operationProp;
|
||||
SerializedProperty sourceIntProp;
|
||||
SerializedProperty sourceFloatProp;
|
||||
SerializedProperty destinationFloatProp;
|
||||
SerializedProperty destinationIntProp;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
titleProp = serializedObject.FindProperty("title");
|
||||
typeProp = serializedObject.FindProperty("type");
|
||||
operationProp = serializedObject.FindProperty("operation");
|
||||
sourceFloatProp = serializedObject.FindProperty("sourceFloat");
|
||||
sourceIntProp = serializedObject.FindProperty("sourceInt");
|
||||
destinationFloatProp = serializedObject.FindProperty("destinationFloat");
|
||||
destinationIntProp = serializedObject.FindProperty("destinationInt");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(titleProp);
|
||||
EditorGUILayout.PropertyField(typeProp);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
const int floatIndex = 0;
|
||||
if (typeProp.enumValueIndex == floatIndex)
|
||||
{
|
||||
// Float
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(destinationFloatProp, GUIContent.none);
|
||||
EditorGUILayout.PropertyField(operationProp, GUIContent.none, GUILayout.MaxWidth(60f));
|
||||
EditorGUILayout.PropertyField(sourceFloatProp, GUIContent.none);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Int
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(destinationIntProp, GUIContent.none);
|
||||
EditorGUILayout.PropertyField(operationProp, GUIContent.none, GUILayout.MaxWidth(60f));
|
||||
EditorGUILayout.PropertyField(sourceIntProp, GUIContent.none);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ef8aa66218976b48a2b3fe367e420c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
61
Assets/MonoBehaviourTree/Source/Editor/SetObjectEditor.cs
Normal file
61
Assets/MonoBehaviourTree/Source/Editor/SetObjectEditor.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomEditor(typeof(SetObject))]
|
||||
public class SetObjectEditor : Editor
|
||||
{
|
||||
SerializedProperty titleProp;
|
||||
SerializedProperty typeProp;
|
||||
SerializedProperty sourceTransformProp;
|
||||
SerializedProperty sourceGameObjectProp;
|
||||
SerializedProperty destinationTransformProp;
|
||||
SerializedProperty destinationGameObjectProp;
|
||||
|
||||
private static readonly GUIContent destinationLabel = new GUIContent("Destination");
|
||||
private static readonly GUIContent sourceLabel = new GUIContent("Source");
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
titleProp = serializedObject.FindProperty("title");
|
||||
typeProp = serializedObject.FindProperty("type");
|
||||
sourceGameObjectProp = serializedObject.FindProperty("sourceGameObject");
|
||||
sourceTransformProp = serializedObject.FindProperty("sourceTransform");
|
||||
destinationGameObjectProp = serializedObject.FindProperty("destinationGameObject");
|
||||
destinationTransformProp = serializedObject.FindProperty("destinationTransform");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(titleProp);
|
||||
EditorGUILayout.PropertyField(typeProp);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
const int transformIndex = 0;
|
||||
if (typeProp.enumValueIndex == transformIndex)
|
||||
{
|
||||
// Transform
|
||||
EditorGUILayout.PropertyField(destinationTransformProp, destinationLabel);
|
||||
EditorGUILayout.PropertyField(sourceTransformProp, sourceLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// GameObject
|
||||
EditorGUILayout.PropertyField(destinationGameObjectProp, destinationLabel);
|
||||
EditorGUILayout.PropertyField(sourceGameObjectProp, sourceLabel);
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2d4187215e1e7f48b43d3034eebc775
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
65
Assets/MonoBehaviourTree/Source/Editor/SetVectorEditor.cs
Normal file
65
Assets/MonoBehaviourTree/Source/Editor/SetVectorEditor.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomEditor(typeof(SetVector))]
|
||||
public class SetVectorEditor : Editor
|
||||
{
|
||||
SerializedProperty titleProp;
|
||||
SerializedProperty typeProp;
|
||||
SerializedProperty sourceVector2Prop;
|
||||
SerializedProperty sourceVector3Prop;
|
||||
SerializedProperty destinationVector2Prop;
|
||||
SerializedProperty destinationVector3Prop;
|
||||
|
||||
private static readonly GUIContent destinationLabel = new GUIContent("Destination");
|
||||
private static readonly GUIContent sourceLabel = new GUIContent("Source");
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
titleProp = serializedObject.FindProperty("title");
|
||||
typeProp = serializedObject.FindProperty("type");
|
||||
sourceVector3Prop = serializedObject.FindProperty("sourceVector3");
|
||||
sourceVector2Prop = serializedObject.FindProperty("sourceVector2");
|
||||
destinationVector3Prop = serializedObject.FindProperty("destinationVector3");
|
||||
destinationVector2Prop = serializedObject.FindProperty("destinationVector2");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(titleProp);
|
||||
EditorGUILayout.PropertyField(typeProp);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
const int vector3Index = 1;
|
||||
if (typeProp.enumValueIndex == vector3Index)
|
||||
{
|
||||
// Vector3
|
||||
// EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(destinationVector3Prop, destinationLabel);
|
||||
EditorGUILayout.PropertyField(sourceVector3Prop, sourceLabel);
|
||||
// EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vector2
|
||||
// EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(destinationVector2Prop, destinationLabel);
|
||||
EditorGUILayout.PropertyField(sourceVector2Prop, sourceLabel);
|
||||
// EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8a7915226b0ecd4b91fb6fcd415772a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,133 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using MBT;
|
||||
|
||||
namespace MBTEditor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(BaseVariableReference), true)]
|
||||
public class VariableReferenceDrawer : PropertyDrawer
|
||||
{
|
||||
private GUIStyle constVarGUIStyle = new GUIStyle("MiniButton");
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
position.height = 18f;
|
||||
SerializedProperty keyProperty = property.FindPropertyRelative("key");
|
||||
SerializedProperty blackboardProperty = property.FindPropertyRelative("blackboard");
|
||||
SerializedProperty useConstProperty = property.FindPropertyRelative("useConstant");
|
||||
|
||||
MonoBehaviour inspectedComponent = property.serializedObject.targetObject as MonoBehaviour;
|
||||
// search only in the same game object
|
||||
if (inspectedComponent != null)
|
||||
{
|
||||
// Blackboard blackboard = inspectedComponent.GetComponent<Blackboard>();
|
||||
Blackboard blackboard = GetBlackboardInParent(inspectedComponent);
|
||||
if (blackboard != null)
|
||||
{
|
||||
// Draw mode toggle if not disabled
|
||||
if (property.FindPropertyRelative("mode").enumValueIndex == 0)
|
||||
{
|
||||
Rect togglePosition = position;
|
||||
togglePosition.width = 8;
|
||||
togglePosition.height = 16;
|
||||
useConstProperty.boolValue = EditorGUI.Toggle(togglePosition, useConstProperty.boolValue, constVarGUIStyle);
|
||||
position.xMin += 10;
|
||||
}
|
||||
|
||||
// Draw constant or dropdown
|
||||
if (useConstProperty.boolValue)
|
||||
{
|
||||
// Use constant variable
|
||||
EditorGUI.PropertyField(position, property.FindPropertyRelative("constantValue"), label);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Type desiredVariableType = fieldInfo.FieldType.BaseType.GetGenericArguments()[0];
|
||||
BlackboardVariable[] variables = blackboard.GetAllVariables();
|
||||
List<string> keys = new List<string>();
|
||||
keys.Add("None");
|
||||
for (int i = 0; i < variables.Length; i++)
|
||||
{
|
||||
BlackboardVariable bv = variables[i];
|
||||
if (bv.GetType() == desiredVariableType) {
|
||||
keys.Add(bv.key);
|
||||
}
|
||||
}
|
||||
// Setup dropdown
|
||||
// INFO: "None" can not be used as key
|
||||
int selected = keys.IndexOf(keyProperty.stringValue);
|
||||
if (selected < 0) {
|
||||
selected = 0;
|
||||
// If key is not empty it means variable was deleted and missing
|
||||
if (!System.String.IsNullOrEmpty(keyProperty.stringValue))
|
||||
{
|
||||
keys[0] = "Missing";
|
||||
}
|
||||
}
|
||||
int result = EditorGUI.Popup(position, label.text, selected, keys.ToArray());
|
||||
if (result > 0) {
|
||||
keyProperty.stringValue = keys[result];
|
||||
blackboardProperty.objectReferenceValue = blackboard;
|
||||
} else {
|
||||
keyProperty.stringValue = "";
|
||||
blackboardProperty.objectReferenceValue = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.LabelField(position, property.displayName);
|
||||
int indent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 1;
|
||||
position.y += EditorGUI.GetPropertyHeight(keyProperty);// + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(position, keyProperty);
|
||||
position.y += EditorGUI.GetPropertyHeight(blackboardProperty);// + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(position, blackboardProperty);
|
||||
EditorGUI.indentLevel = indent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
MonoBehaviour monoBehaviour = property.serializedObject.targetObject as MonoBehaviour;
|
||||
if (monoBehaviour != null && GetBlackboardInParent(monoBehaviour) == null) {
|
||||
return 3 * (EditorGUIUtility.standardVerticalSpacing + 16);
|
||||
}
|
||||
return 16 + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find Blackboard in parent including inactive game objects
|
||||
/// </summary>
|
||||
/// <param name="component">Component to search</param>
|
||||
/// <returns>Blackboard if found, otherwise null</returns>
|
||||
protected Blackboard GetBlackboardInParent(Component component)
|
||||
{
|
||||
Transform current = component.transform;
|
||||
Blackboard result = null;
|
||||
while (current != null && result == null)
|
||||
{
|
||||
if (current.TryGetComponent<Blackboard>(out Blackboard b))
|
||||
{
|
||||
result = b;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab4047381b703f94a844906084b35480
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
21
Assets/MonoBehaviourTree/Source/LICENSE
Normal file
21
Assets/MonoBehaviourTree/Source/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Qriva
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
7
Assets/MonoBehaviourTree/Source/LICENSE.meta
Normal file
7
Assets/MonoBehaviourTree/Source/LICENSE.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 253fb677ccf902d4c83f13ae18604c9f
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
341
Assets/MonoBehaviourTree/Source/README.md
Normal file
341
Assets/MonoBehaviourTree/Source/README.md
Normal file
@ -0,0 +1,341 @@
|
||||
# MonoBehaviourTree — Simple behaviour tree for Unity
|
||||
|
||||
This project is simple event driven behaviour tree based on Unity engine component system. This asset comes with minimal node library and tree visual editor. [Online version of this readme](https://github.com/Qriva/MonoBehaviourTree/blob/master/README.md)
|
||||
|
||||
**Important:** This is not fully fledged visual scripting tool. Package has its own visual editor, however requires you to implement your own nodes.
|
||||
|
||||
## Contribute
|
||||
Contribution in any form is very welcome. Bugs, feature requests or feedback can be reported in form of Issues.
|
||||
|
||||
## Getting started
|
||||
The latest version can be installed via [package manager](https://docs.unity3d.com/Manual/upm-ui-giturl.html) using following git URL:<br> ```https://github.com/Qriva/MonoBehaviourTree.git#upm```
|
||||
<br>Alternatively you can copy the `Assets/MonoBehaviourTree` folder to your project or download from [Unity Asset Store](https://assetstore.unity.com/packages/slug/213452).
|
||||
|
||||
Examples of usage are available in package manager or in folder **Samples** containing demo scenes. If you copy assets manually you might want to delete `Samples` directory to get rid of redundant files.
|
||||
|
||||
This documentation assumes you have basic knowledge about behaviour trees. If you don't have it, you should check some online resources like this
|
||||
[Game Developer article](https://www.gamedeveloper.com/programming/behavior-trees-for-ai-how-they-work)
|
||||
or [Unreal Engine documentation](https://docs.unrealengine.com/en-US/Engine/ArtificialIntelligence/BehaviorTrees/BehaviorTreesOverview/index.html).
|
||||
|
||||
## Event Driven Behaviour Tree Overview
|
||||
Standard behaviour tree design assumes three types of nodes: composites, decorators and leafs. Composites are used to define the flow in the tree, decorators can modify node results and leafs perform tasks or check conditions. This design has one major flaw - tree must be traversed from the beginning every single tick, otherwise it will not be possible to react to changes in state or data. Event driven tree is the fix to that problem. When tree gets update it continues from the last executed running node. Normally it would mean, that executed higher priority nodes will not be reevaluated immediately, but event driven BT introduces **abort system** to give possibility to reset tree to previous state, when certain event occur. Implementation used in this project is very similar to the one used in Unreal engine - leaf nodes are not used as conditions, instead they are in form of decorators. Additionally it is possible to create Service nodes which can perform task periodically.
|
||||
|
||||
### Node Abort
|
||||
When the tree is updated, the first evaluated thing are aborting nodes. If there is any aborting node, the tree will be reset to that position and execution will be continued from this node.
|
||||
In case there are multiple aborting nodes, the one closest to root will be selected.
|
||||
There are four abort types:
|
||||
- **None** - don't do anything when change occurs
|
||||
- **Self** - abort children running below
|
||||
- **Lower Priority** - abort running nodes with lower priority (nodes to the right)
|
||||
- **Both** - abort self and lower priority nodes
|
||||
|
||||
>Execution order (priority) of nodes with common ancestor is defined by position on X axis, nodes to the left has higher priority.
|
||||
|
||||
Aborts can be performed only by ```Decorator``` nodes. See example abort implementation in [Decorator](#custom-decorator--condition) section.
|
||||
|
||||
## Basic Usage
|
||||
The main core of behaviour tree is **MonoBehaviourTree** component. It contains most of tree state during runtime. It is important to note, that tree does not run automatically and must be updated by other script. This design gives you possibility to tick the tree in Update, FixedUpdate or custom interval. However, most of the time Update event will be used, so you can use component **MBT Executor** to do that.
|
||||
|
||||
In most of cases you will need some place to store shared data for nodes. You could implement your own component to do that, but instead it's better to use built in **Blackboard** component. Blackboard allows you to create observable variables of predefined types that are compatible with default nodes.
|
||||
|
||||
## Node editor
|
||||
To open tree editor window click "Open editor" button of MonoBehaviourTree component or click Unity menu: Window / Mono Behaviour Tree. In visual editor you can create, connect, delete and setup nodes.
|
||||
|
||||
Every behaviour tree needs an entry point called **Root**. To add it right click on empty canvas to open node popup, then select Root. Execution of BT starts here and goes from top to down, left to right.
|
||||
|
||||
> **Implementation note:** All nodes and variables are in fact components, but they are invisible in inspector window.
|
||||
> It is recommended to use separate empty game object to build the tree - this make it easier to create prefabs and avoid unnecessary unknown problems.
|
||||
|
||||
Most of nodes has additional properties that you can change. To do this select the node and list of options will show up in **MonoBehaviourTree component inspector** section (standard component inspector). When node setup is not valid, error icon will be shown next to the node. By default error is displayed if one of variable references is set to "None". You can create custom validation rules in your own nodes, see [Node API section](#node-api).
|
||||
|
||||
### Editor Window Features
|
||||
Right click on empty space to create new node. To connect nodes click on in/out handler (black dot on top and bottom of node), then drag and drop it above another node. In case node cannot have more than one child (decorator) the connection will be overridden by the new one.
|
||||
To delete, duplicate or disconnect nodes right click on node to open context menu and select the appropriate option.
|
||||
Use left mouse button to drag workspace or nodes. You can drag whole branch of nodes when CTRL key is pressed.
|
||||
|
||||
## Component Reference
|
||||
|
||||
### MonoBehaviourTree component
|
||||
Main component used as hub of behaviour tree.
|
||||
|
||||
**Properties**
|
||||
- **Description** - optional user description.
|
||||
- **Repeat OnFinish** - whether the tree should be executed again when finished.
|
||||
- **Max Executions Per Tick** - how many nodes should be executed during single update.
|
||||
- **Parent** - parent reference if this tree is subtree. Read more in [Subtree node section](#subtree).
|
||||
|
||||
**Node Inspector** - Inspector of node selected in Behaviour Tree editor window.
|
||||
|
||||
### Blackboard component
|
||||
Component used to provide and manage observable variables.
|
||||
To add variable fill the **Key** text field, select it's type and press "Add" button. Key is used as identifier to get or set variable value, this can be done by VariableReference or blackboard method: ```public T GetVariable<T>(string key)```.
|
||||
Blackboard component displays all available variables in list and allows to set initial value for each of them.
|
||||
> **Implementation note:** Changing blackboard variables during playmode triggers change listeners, however old and new value will be the same in this event, because it's called from `OnValidate`. Displayed values are refreshed every half second.
|
||||
|
||||
**Built In variable types:** Bool, Float, Int, Object, Quaternion, String, Transform, Vector2, Vector3. If you need to add your own custom type read [Custom Variable section](#custom-variable).
|
||||
|
||||
**Master Blackboard** option allows to make synchronisation effect between variables. When set, blackboard will replace variables during initialization, but only when their keys match. Replacement is not hierarhical and only variables from given parent blackboard are matched. It is recommended to create one master blackboard on top of the tree and keep all other subtrees blackboards synchronized with the top one.
|
||||
|
||||
## Variables and Events
|
||||
In most of situations nodes need to share some state data between each other, it can be done by Blackboard, Variable and VariableReference system. Variables are observale data containers, that can be accesed via Blackboard. To get variable you need to know its key, but inputting key manually to every node is not handy and very error prone.
|
||||
|
||||
To avoid this you can use helper class VariableReference.
|
||||
This class allows you to automaticaly get and cache reference to blackboard variable.
|
||||
VariableReference has also constant value mode in case you don't need to retrive values from blackboard. You can toggle VarRef mode in editor by clicking small button to the left. Keys displayed in dropdown will be loaded from blackboard on the same object or if there is none it will look upwards the hierarchy to find one.
|
||||
```csharp
|
||||
// Get variable from blackboard by key
|
||||
FloatVariable floatVar = blackboard.GetVariable<FloatVariable>("myKey");
|
||||
|
||||
// Attach listener to variable
|
||||
floatVar.AddListener(MyVariableChangeListener);
|
||||
|
||||
// Create float reference property with default constant value
|
||||
public FloatReference floatRef = new FloatReference(1f);
|
||||
|
||||
// Create int reference, but do not allow constant values
|
||||
public IntReference intRef = new IntReference(VarRefMode.DisableConstant);
|
||||
|
||||
// Check if its in constant or reference mode
|
||||
bool constant = floatRef.isConstant;
|
||||
|
||||
// Attach listener to variable reference
|
||||
if (!constant)
|
||||
{
|
||||
// GetVariable will return null if floatRef is constant
|
||||
floatRef.GetVariable().AddListener(MyVariableChangeListener);
|
||||
}
|
||||
|
||||
// Get or set value of variable reference
|
||||
floatRef.Value = 1.5f;
|
||||
float value = floatRef.Value;
|
||||
```
|
||||
> **Important:** TransformVariable change listener will be called only when reference to object changes. Position or rotation changes do not trigger change listener.
|
||||
|
||||
### Custom Variable
|
||||
If built in variables are not enough, you can create your own.
|
||||
To create new Variable and VariableReference you must extend Variable class and VariableReference class. Variable inheriths MonoBehaviour, so to work properly it must by placed in file of the same name as your custom type. VariableReference is normal serializable class and can be placed in the same file. Add ```[AddComponentMenu("")]``` attribute to disallow adding variable component manually.
|
||||
|
||||
Any variable must implement ValueEquals method which is used to detect change of value. This mechanism allows to correctly compare Unity objects in generic class, avoid boxing, plus gives the way to implement your own change detection logic when needed.
|
||||
```csharp
|
||||
[AddComponentMenu("")]
|
||||
public class CustomVariable : Variable<CustomType>
|
||||
{
|
||||
protected override bool ValueEquals(CustomType val1, CustomType val2)
|
||||
{
|
||||
return val1 == val2;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class CustomReference : VariableReference<CustomVariable, CustomType>
|
||||
{
|
||||
// You can create additional constructors and Value getter/setter
|
||||
// See FloatVariable.cs as example
|
||||
|
||||
// If your variable is reference type you might want constant validation
|
||||
// protected override bool isConstantValid
|
||||
// {
|
||||
// get { return constantValue != null; }
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
## Node Reference
|
||||
|
||||
### Root
|
||||
Entry node of behaviour tree.
|
||||
### Sequence
|
||||
Executes children from left to right as long as each subsequent child returns success. Returns success when all children succeeded. Failure if one of them failed. When Random option is enabled, then execution goes in random order.
|
||||
### Selector
|
||||
Executes children from left to right until one of them return failure. Returns success if any children succeed. Failure if all of them failed. When Random option is enabled, then execution goes in random order.
|
||||
### Is Set Condition
|
||||
Checks if blackboard variable is set. Node supports Bollean, Object and Transform variables. Selecting Invert option will produce "If not set" effect.
|
||||
### Number Condition
|
||||
Checks if blackboard number variable meets requirement. Node supports Float and Int variables.
|
||||
### Distance Condition
|
||||
Checks distance between two transforms and returns success when given distance condition is met.
|
||||
### Cooldown
|
||||
Blocks execition until the specified amount of time has elapsed.
|
||||
Time starts counting after branch is exited. If abort is enabled, the execution will be moved back to this node after time has elapsed.
|
||||
### Force Result
|
||||
Forces success or failure.
|
||||
### Inverter
|
||||
Inverts node result. Failure becomes Success and vice versa.
|
||||
### Invoke Unity Event
|
||||
Triggers Unity Event with one parameter of selected type
|
||||
### Random Chance
|
||||
There is fixed chance that node will be executed. Returns Failure if roll is not favorable.
|
||||
### Random Float
|
||||
Generates random float in provided range.
|
||||
### Random Integer
|
||||
Generates random integer in provided range.
|
||||
### Repeat Until Fail
|
||||
Repeats branch as long as Success is returned from the child. Use Loop node to create a more advanced flow.
|
||||
### Repeater
|
||||
Repeats branch specified amount of times or infinitely. Use Loop node to create a more advanced flow.
|
||||
### Loop
|
||||
Repeats branch specified amount of times, infinitely or until selected result is returned. Results and flow can be fully customized.
|
||||
### Set Boolean
|
||||
Sets blackboard bool variable
|
||||
### Set Number
|
||||
Sets blackboard int or float variable
|
||||
### Set Object
|
||||
Sets blackboard Transform or GameObject variable
|
||||
### Set Vector
|
||||
Sets blackboard Vector3 or Vector2 variable
|
||||
### Succeeder
|
||||
Always returns Success.
|
||||
### Time Limit
|
||||
Determines how long branch can be executed. After given time elapses branch is aborted and Failure is returned.
|
||||
### Calculate Distance Service
|
||||
Calculates distance between two transforms and updates blackboard float variable with the result.
|
||||
### Update Position Service
|
||||
Updates blackboard Vector3 variable with position of given source transform.
|
||||
### Set Number
|
||||
Sets blackboard Float or Int variable.
|
||||
### Wait
|
||||
Waits specific amount of time, then returns Success.
|
||||
### Subtree
|
||||
Subtree node allows connection of other behaviour tree as child, this gives you possibility to create reusable blocks of nodes. Such a tree must be created in separate game object and attached as children. Child tree is updated by its parent. **Parent of subtree must be specified in MonoBehaviourTree component to work properly.**
|
||||
|
||||
## Creating custom nodes
|
||||
It is possible to create custom nodes by extending one of base classes provided by library. Each node **must** be in separate file with the name corresponding to class name. MBTNode attribute is required to register node in editor finder, it accepts two parameters: name and order. Name allows use of up to one folder, so "Custom Node" and "Example/Custom Node" is valid, but "Fruits/Bananas/Custom Node" is not.
|
||||
Order parameter is used to position node higher in finder. Nodes are sorted first by order and then by name. Default order is 1000 and lower values get higher priority. For example Selector, Sequence, Root and SubTree nodes have following values: 100, 150, 200, 250.
|
||||
|
||||
### Node API
|
||||
There are several event methods that can be implemented to control Node execution flow, but the only required one is ```Execute``` used to return state when node is running.
|
||||
```OnEnter``` and ```OnExit``` primary function is to setup before or cleanup after execution.
|
||||
```OnAllowInterrupt``` and ```OnDisallowInterrupt``` can be used to detect when its allowed to abort or listen to some events.
|
||||
Additionally there is ```IsValid``` method used in editor window to determine if Node setup is correct. By default it uses reflection to find variable references with empty keys and in most of cases there is no need to override this method, unless you want to include other fields during validation or custom setup requires it.
|
||||
|
||||
### Execution Flow
|
||||
During runtime node can be in one of following states:
|
||||
- **Ready** - Default state
|
||||
- **Running** - Node is currently executed or one of its successors
|
||||
- **Success** - Node finished execution and returned success
|
||||
- **Failure** - Node finished execution and returned failure
|
||||
|
||||
When node is ready and parent decide to "enter" the node, then ```OnAllowInterrupt``` and ```OnEnter``` is called. After that ```Execute``` method is called which always must return some state.
|
||||
If running state is returned, then execution will be paused and resumed in next tick, but if running with children node is returned, then execution is "passed down" and continued in that node.
|
||||
When success or failure is returned, then this result is passed to the parent and ```OnExit``` is called. ```OnDisallowInterrupt``` is not called until the cycle ends or tree is aborted to higher priority node.
|
||||
|
||||
### Custom Leaf
|
||||
Leaf nodes are used to do designated task. It can be something simple as setting variable or very complex like enemy navigation along the path.
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using MBT;
|
||||
|
||||
// Empty Menu attribute prevents Node to show up in "Add Component" menu.
|
||||
[AddComponentMenu("")]
|
||||
// Register node in visual editor node finder
|
||||
[MBTNode(name = "Example/Custom Task")]
|
||||
public class CustomTask : Leaf
|
||||
{
|
||||
public BoolReference somePropertyRef = new BoolReference();
|
||||
|
||||
// These two methods are optional, override only when needed
|
||||
// public override void OnAllowInterrupt() {}
|
||||
// public override void OnEnter() {}
|
||||
|
||||
// This is called every tick as long as node is executed
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
if (somePropertyRef.Value == true)
|
||||
{
|
||||
return NodeResult.success;
|
||||
}
|
||||
return NodeResult.failure;
|
||||
}
|
||||
|
||||
// These two methods are optional, override only when needed
|
||||
// public override void OnExit() {}
|
||||
// public override void OnDisallowInterrupt() {}
|
||||
|
||||
// Usually there is no needed to override this method
|
||||
public override bool IsValid()
|
||||
{
|
||||
// You can do some custom validation here
|
||||
return !somePropertyRef.isInvalid;
|
||||
}
|
||||
}
|
||||
```
|
||||
### Custom Decorator / Condition
|
||||
Decorator nodes are mainy used to change flow of the tree. It has single child and must always return either success or failure. You can implement your own subclass by implementing ```Decorator``` class, however in most of cases you might want to create condition - in such a case use ```Condition``` subclass. Implement condition evaluation in ```Check()``` method and node will return success if condition is met.
|
||||
|
||||
If you need to implement abort system in your condition node, then below you can find a simple example.
|
||||
```csharp
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode(name = "Example/Custom Condition")]
|
||||
public class CustomCondition : Condition
|
||||
{
|
||||
public Abort abort;
|
||||
public BoolReference somePropertyRef = new BoolReference(VarRefMode.DisableConstant);
|
||||
|
||||
public override bool Check()
|
||||
{
|
||||
// Evaluate your custom condition
|
||||
return somePropertyRef.Value == true;
|
||||
}
|
||||
|
||||
public override void OnAllowInterrupt()
|
||||
{
|
||||
// Do not listen any changes if abort is disabled
|
||||
if (abort != Abort.None)
|
||||
{
|
||||
// This method cache current tree state used later by abort system
|
||||
ObtainTreeSnapshot();
|
||||
// If somePropertyRef is constant, then null exception will be thrown.
|
||||
// Use somePropertyRef.isConstant in case you need constant enabled.
|
||||
// Constant variable is disabled here, so it is safe to do this.
|
||||
somePropertyRef.GetVariable().AddListener(OnVariableChange);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDisallowInterrupt()
|
||||
{
|
||||
if (abort != Abort.None)
|
||||
{
|
||||
// Remove listener
|
||||
somePropertyRef.GetVariable().RemoveListener(OnVariableChange);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVariableChange(bool oldValue, bool newValue)
|
||||
{
|
||||
// Reevaluate Check() and abort tree when needed
|
||||
EvaluateConditionAndTryAbort(abort);
|
||||
}
|
||||
}
|
||||
```
|
||||
### Custom Service
|
||||
Service is a special decorator that performs the task as long as its branch is executed. This way you can periodically execute some task needed only by the ancestors. Additionally you can fully encapsulate your system into single behaviour tree without need of external scripts running on other game objects.
|
||||
|
||||
```csharp
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode("Example/Custom Service")]
|
||||
public class CustomService : Service
|
||||
{
|
||||
public Vector3Reference position = new Vector3Reference(VarRefMode.DisableConstant);
|
||||
|
||||
public override void Task()
|
||||
{
|
||||
// Reset variable to zero every X amount of time
|
||||
position.Value = Vector3.zero;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Debugging
|
||||
During playmode you can preview tree execution flow in editor window. Nodes are marked with the appropriate color corresponding to their state:
|
||||
- Ready - none (default)
|
||||
- Success - green
|
||||
- Failure - orange
|
||||
- Running - purple
|
||||
|
||||
When the node is invalid, an error icon will be displayed in the upper right corner. You should not run the tree when there are any errors in connected nodes.
|
||||
|
||||
Except that, you can set breakpoints on multiple nodes. Breakpoint will stop execution and pause play mode after node is entered, but before it get executed. Nodes with breakpoint enabled will have red node names.
|
||||
|
||||
## Known Issues
|
||||
- All nodes should be removed before deleting their script. When the missing script is restored and children of this node were connected to other parent, it will break the tree. Additionaly nodes with missing script remain hidden in the inspector and it is hard to remove them.
|
||||
- When tree is turned into prefab, all their instances should not change connections between nodes. Sometimes connections are desynchronized and the tree does not work properly.
|
7
Assets/MonoBehaviourTree/Source/README.md.meta
Normal file
7
Assets/MonoBehaviourTree/Source/README.md.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77ea71eb8c4c7e644b3dbe712459545b
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/MonoBehaviourTree/Source/Runtime.meta
Normal file
8
Assets/MonoBehaviourTree/Source/Runtime.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0265dd17d4c1acd4d899831242a13242
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
94
Assets/MonoBehaviourTree/Source/Runtime/Blackboard.cs
Normal file
94
Assets/MonoBehaviourTree/Source/Runtime/Blackboard.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
[DefaultExecutionOrder(-1000)]
|
||||
public class Blackboard : MonoBehaviour
|
||||
{
|
||||
public List<BlackboardVariable> variables = new List<BlackboardVariable>();
|
||||
private Dictionary<string, BlackboardVariable> dictionary = new Dictionary<string, BlackboardVariable>();
|
||||
[Tooltip("When set, this blackboard will replace variables with matching names from target parent")]
|
||||
public Blackboard masterBlackboard;
|
||||
|
||||
// IMPROVEMENT: https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
|
||||
void Awake()
|
||||
{
|
||||
// Initialize variables by keys
|
||||
dictionary.Clear();
|
||||
for (int i = 0; i < variables.Count; i++)
|
||||
{
|
||||
BlackboardVariable var = variables[i];
|
||||
dictionary.Add(var.key, var);
|
||||
}
|
||||
// Replace variables from master blackboard
|
||||
if (masterBlackboard != null)
|
||||
{
|
||||
List<BlackboardVariable> parentVars = masterBlackboard.variables;
|
||||
for (int i = 0; i < parentVars.Count; i++)
|
||||
{
|
||||
// Find if there is variable with the same key
|
||||
BlackboardVariable parentVar = parentVars[i];
|
||||
if (dictionary.TryGetValue(parentVar.key, out BlackboardVariable currentVar))
|
||||
{
|
||||
// Ensure that both of them are the same type
|
||||
if (currentVar.GetType().IsAssignableFrom(parentVar.GetType()))
|
||||
{
|
||||
// There are matching variables, replace current one with master blackboard var
|
||||
dictionary[parentVar.key] = parentVar;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogErrorFormat(this,
|
||||
"Blackboard variable key '{0}' cannot be replaced. " +
|
||||
"Master blackboard variable of type {1} cannot be assigned to {2}.",
|
||||
currentVar.key,
|
||||
parentVar.GetType().Name,
|
||||
currentVar.GetType().Name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BlackboardVariable[] GetAllVariables()
|
||||
{
|
||||
return variables.ToArray();
|
||||
}
|
||||
|
||||
public T GetVariable<T>(string key) where T : BlackboardVariable
|
||||
{
|
||||
return (dictionary.TryGetValue(key, out BlackboardVariable val)) ? (T)val : null;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[ContextMenu("Delete all variables", false)]
|
||||
protected void DeleteAllVariables()
|
||||
{
|
||||
for (int i = 0; i < variables.Count; i++)
|
||||
{
|
||||
UnityEditor.Undo.DestroyObjectImmediate(variables[i]);
|
||||
}
|
||||
variables.Clear();
|
||||
}
|
||||
|
||||
[ContextMenu("Delete all variables", true)]
|
||||
protected bool HasVariables()
|
||||
{
|
||||
return variables.Count > 0;
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (masterBlackboard == this)
|
||||
{
|
||||
Debug.LogWarning("Master blackboard cannot be the same instance.");
|
||||
masterBlackboard = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
11
Assets/MonoBehaviourTree/Source/Runtime/Blackboard.cs.meta
Normal file
11
Assets/MonoBehaviourTree/Source/Runtime/Blackboard.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b2217e4d35c0c94ea02cf2166c17e4b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: cf1e8cb5def7ab848bce542b344bd1aa, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
public interface IMonoBehaviourTreeTickListener
|
||||
{
|
||||
void OnBehaviourTreeTick();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a72cb8ac73fa6f342b5a07b27abefd66
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
33
Assets/MonoBehaviourTree/Source/Runtime/MBTExecutor.cs
Normal file
33
Assets/MonoBehaviourTree/Source/Runtime/MBTExecutor.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(MonoBehaviourTree))]
|
||||
public class MBTExecutor : MonoBehaviour
|
||||
{
|
||||
public MonoBehaviourTree monoBehaviourTree;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
monoBehaviourTree = GetComponent<MonoBehaviourTree>();
|
||||
OnValidate();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
monoBehaviourTree.Tick();
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (monoBehaviourTree != null && monoBehaviourTree.parent != null)
|
||||
{
|
||||
monoBehaviourTree = null;
|
||||
Debug.LogWarning("Subtree should not be target of update. Select parent tree instead.", this.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/MonoBehaviourTree/Source/Runtime/MBTExecutor.cs.meta
Normal file
11
Assets/MonoBehaviourTree/Source/Runtime/MBTExecutor.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e5f392bbea2fa2499665ed8b604ff86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
22
Assets/MonoBehaviourTree/Source/Runtime/MBTNode.cs
Normal file
22
Assets/MonoBehaviourTree/Source/Runtime/MBTNode.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public class MBTNode : Attribute
|
||||
{
|
||||
public string name;
|
||||
public int order;
|
||||
public string icon;
|
||||
|
||||
public MBTNode(string name = null, int order = 1000, string icon = null)
|
||||
{
|
||||
this.name = name;
|
||||
this.order = order;
|
||||
this.icon = icon;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/MonoBehaviourTree/Source/Runtime/MBTNode.cs.meta
Normal file
11
Assets/MonoBehaviourTree/Source/Runtime/MBTNode.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a03e885e14e321347ad1555636efa6f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
329
Assets/MonoBehaviourTree/Source/Runtime/MonoBehaviourTree.cs
Normal file
329
Assets/MonoBehaviourTree/Source/Runtime/MonoBehaviourTree.cs
Normal file
@ -0,0 +1,329 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
// [RequireComponent(typeof(Blackboard))]
|
||||
public class MonoBehaviourTree : MonoBehaviour
|
||||
{
|
||||
private static readonly ProfilerMarker _TickMarker = new ProfilerMarker("MonoBehaviourTree.Tick");
|
||||
|
||||
[HideInInspector]
|
||||
public Node selectedEditorNode;
|
||||
public bool repeatOnFinish = false;
|
||||
public int maxExecutionsPerTick = 1000;
|
||||
public MonoBehaviourTree parent;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when tree is about to be updated
|
||||
/// </summary>
|
||||
public event UnityAction onTick;
|
||||
private List<IMonoBehaviourTreeTickListener> tickListeners = new List<IMonoBehaviourTreeTickListener>();
|
||||
private Root rootNode;
|
||||
private List<Node> executionStack;
|
||||
private List<Node> executionLog;
|
||||
private List<Decorator> interruptingNodes = new List<Decorator>();
|
||||
public float LastTick { get; internal set; }
|
||||
|
||||
void Awake()
|
||||
{
|
||||
rootNode = GetComponent<Root>();
|
||||
if (rootNode == null) {
|
||||
Debug.LogWarning("Missing Root node in behaviour tree.", this);
|
||||
}
|
||||
|
||||
// Find master parent tree and all nodes
|
||||
MonoBehaviourTree masterTree = this.GetMasterTree();
|
||||
Node[] nodes = GetComponents<Node>();
|
||||
if(masterTree == this)
|
||||
{
|
||||
// Create lists with capicity
|
||||
executionStack = new List<Node>(8);
|
||||
executionLog = new List<Node>(nodes.Length);
|
||||
// Set start node when tree is created first time
|
||||
executionStack.Add(rootNode);
|
||||
executionLog.Add(rootNode);
|
||||
}
|
||||
// Initialize nodes of tree/subtree
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
Node n = nodes[i];
|
||||
n.behaviourTree = masterTree;
|
||||
n.runningNodeResult = new NodeResult(Status.Running, n);
|
||||
}
|
||||
}
|
||||
|
||||
private void EvaluateInterruptions()
|
||||
{
|
||||
if (interruptingNodes.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find node with highest priority - closest to the root (the smallest number)
|
||||
Decorator abortingNode = interruptingNodes[0];
|
||||
for (int i = 1; i < interruptingNodes.Count; i++)
|
||||
{
|
||||
Decorator d = interruptingNodes[i];
|
||||
if (d.runtimePriority < abortingNode.runtimePriority) {
|
||||
abortingNode = d;
|
||||
}
|
||||
}
|
||||
|
||||
// Revert stack
|
||||
executionStack.Clear();
|
||||
executionStack.AddRange(abortingNode.GetStoredTreeSnapshot());
|
||||
// Restore flow of events in nodes after abort
|
||||
for (int i = 0; i < executionStack.Count; i++)
|
||||
{
|
||||
Node node = executionStack[i];
|
||||
if (node.status == Status.Running)
|
||||
{
|
||||
// This node is still running and might need to restore the state
|
||||
node.OnBehaviourTreeAbort();
|
||||
}
|
||||
else if (node.status == Status.Success || node.status == Status.Failure)
|
||||
{
|
||||
// This node returned failure or success, so reenter it and call OnEnter
|
||||
node.OnEnter();
|
||||
}
|
||||
// All nodes in execution stack should be in running state
|
||||
node.status = Status.Running;
|
||||
}
|
||||
|
||||
int nodeIndex = abortingNode.runtimePriority - 1;
|
||||
// Sanity check
|
||||
if (abortingNode != executionLog[nodeIndex]) {
|
||||
Debug.LogWarning("Priority of node does not match with exectuion log");
|
||||
}
|
||||
// Abort nodes in log
|
||||
ResetNodesTo(abortingNode, true);
|
||||
// Reset aborting node
|
||||
abortingNode.status = Status.Ready;
|
||||
// Reset list and wait for new interruptions
|
||||
interruptingNodes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update tree state.
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
{
|
||||
_TickMarker.Begin();
|
||||
// Fire Tick event and notify listeners
|
||||
onTick?.Invoke();
|
||||
for (int i = 0; i < tickListeners.Count; i++)
|
||||
{
|
||||
tickListeners[i].OnBehaviourTreeTick();
|
||||
}
|
||||
|
||||
// Check if there are any interrupting nodes
|
||||
EvaluateInterruptions();
|
||||
|
||||
// Max number of traversed nodes
|
||||
int executionLimit = maxExecutionsPerTick;
|
||||
// Traverse tree
|
||||
while (executionStack.Count > 0)
|
||||
{
|
||||
if (executionLimit == 0) {
|
||||
LastTick = Time.time;
|
||||
_TickMarker.End();
|
||||
return;
|
||||
}
|
||||
executionLimit -= 1;
|
||||
|
||||
// Execute last element in stack
|
||||
Node currentNode = executionStack[executionStack.Count - 1];
|
||||
NodeResult nodeResult = currentNode.Execute();
|
||||
// Set new status
|
||||
currentNode.status = nodeResult.status;
|
||||
if (nodeResult.status == Status.Running) {
|
||||
// If node is running, then stop execution or continue children
|
||||
Node child = nodeResult.child;
|
||||
if (child == null) {
|
||||
// Stop execution and continue next tick
|
||||
LastTick = Time.time;
|
||||
_TickMarker.End();
|
||||
return;
|
||||
} else {
|
||||
// Add child to execution stack and execute it in next loop
|
||||
executionStack.Add(child);
|
||||
executionLog.Add(child);
|
||||
// IMPORTANT: Priority must be > 0 and assigned in this order
|
||||
child.runtimePriority = executionLog.Count;
|
||||
child.OnAllowInterrupt();
|
||||
child.OnEnter();
|
||||
#if UNITY_EDITOR
|
||||
// Stop execution if breakpoint is set on this node
|
||||
if (child.breakpoint)
|
||||
{
|
||||
Debug.Break();
|
||||
UnityEditor.Selection.activeGameObject = this.gameObject;
|
||||
Debug.Log("MBT Breakpoint: " + child.title, this);
|
||||
LastTick = Time.time;
|
||||
_TickMarker.End();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Remove last node from stack and move up (closer to root)
|
||||
currentNode.OnExit();
|
||||
executionStack.RemoveAt(executionStack.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run this when execution stack is empty and BT should repeat
|
||||
if (repeatOnFinish) {
|
||||
Restart();
|
||||
}
|
||||
|
||||
LastTick = Time.time;
|
||||
_TickMarker.End();
|
||||
}
|
||||
|
||||
public void AddTickListener(IMonoBehaviourTreeTickListener listener)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (tickListeners.Contains(listener))
|
||||
{
|
||||
Debug.LogErrorFormat(this, "Tick listener {0} has been already added.", listener);
|
||||
}
|
||||
#endif
|
||||
tickListeners.Add(listener);
|
||||
}
|
||||
|
||||
public void RemoveTickListener(IMonoBehaviourTreeTickListener listener)
|
||||
{
|
||||
tickListeners.Remove(listener);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method should be called to abort tree to given node
|
||||
/// </summary>
|
||||
/// <param name="node">Abort and revert tree to this node</param>
|
||||
internal void Interrupt(Decorator node)
|
||||
{
|
||||
if (!interruptingNodes.Contains(node)) {
|
||||
interruptingNodes.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ResetNodesTo(Node node, bool aborted = false)
|
||||
{
|
||||
int i = executionLog.Count - 1;
|
||||
// Reset status and get index of node
|
||||
while (i >= 0)
|
||||
{
|
||||
Node n = executionLog[i];
|
||||
if (n == node) {
|
||||
break;
|
||||
}
|
||||
// If node is running (on exec stack) then call exit
|
||||
if (n.status == Status.Running) {
|
||||
n.OnExit();
|
||||
// IMPROVEMENT: Abort event can be added or abort param onExit
|
||||
}
|
||||
n.status = Status.Ready;
|
||||
n.OnDisallowInterrupt();
|
||||
i -= 1;
|
||||
}
|
||||
// Reset log
|
||||
i += 1;
|
||||
if (i >= executionLog.Count) {
|
||||
return;
|
||||
}
|
||||
executionLog.RemoveRange(i, executionLog.Count - i);
|
||||
}
|
||||
|
||||
private void ResetNodes()
|
||||
{
|
||||
for (int i = 0; i < executionLog.Count; i++)
|
||||
{
|
||||
Node node = executionLog[i];
|
||||
if (node.status == Status.Running)
|
||||
{
|
||||
node.OnExit();
|
||||
}
|
||||
node.OnDisallowInterrupt();
|
||||
node.status = Status.Ready;
|
||||
}
|
||||
executionLog.Clear();
|
||||
executionStack.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets state to root node
|
||||
/// </summary>
|
||||
public void Restart()
|
||||
{
|
||||
ResetNodes();
|
||||
executionStack.Add(rootNode);
|
||||
executionLog.Add(rootNode);
|
||||
}
|
||||
|
||||
internal void GetStack(ref Node[] stack)
|
||||
{
|
||||
// Resize array when size is too small
|
||||
if (executionStack.Count > stack.Length)
|
||||
{
|
||||
// Node should not change priority and position during runtime
|
||||
// It means the array will be resized once during first call of this method
|
||||
Array.Resize<Node>(ref stack, executionStack.Count);
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
// Additional sanity check in case nodes are reordered or changed in editor
|
||||
if (stack.Length > executionStack.Count)
|
||||
{
|
||||
Debug.LogError("Changing order of MBT nodes during runtime might cause errors or unpredictable results.");
|
||||
}
|
||||
#endif
|
||||
// Copy elements to provided array
|
||||
executionStack.CopyTo(stack);
|
||||
}
|
||||
|
||||
public Node GetRoot()
|
||||
{
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
public MonoBehaviourTree GetMasterTree()
|
||||
{
|
||||
if (parent == null)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
return parent.GetMasterTree();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate()
|
||||
{
|
||||
if (maxExecutionsPerTick <= 0)
|
||||
{
|
||||
maxExecutionsPerTick = 1;
|
||||
}
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
if (parent == this)
|
||||
{
|
||||
parent = null;
|
||||
Debug.LogWarning("This tree cannot be its own parent.");
|
||||
return;
|
||||
}
|
||||
if (transform.parent == null || parent.gameObject != transform.parent.gameObject)
|
||||
{
|
||||
// parent = null;
|
||||
Debug.LogWarning("Parent tree should be also parent of this gameobject.", this.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b25af8627659c6949b60949c3aa8d91d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 8e319fb1e46299e4bb368bf700097d39, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/MonoBehaviourTree/Source/Runtime/Nodes.meta
Normal file
8
Assets/MonoBehaviourTree/Source/Runtime/Nodes.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab968361104bced4eb5870774bffc1d4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,27 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode("Services/Calculate Distance Service")]
|
||||
public class CalculateDistanceService : Service
|
||||
{
|
||||
[Space]
|
||||
public TransformReference transform1;
|
||||
public TransformReference transform2;
|
||||
public FloatReference variable = new FloatReference(VarRefMode.DisableConstant);
|
||||
|
||||
public override void Task()
|
||||
{
|
||||
Transform t1 = transform1.Value;
|
||||
Transform t2 = transform2.Value;
|
||||
if (t1 == null || t2 == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
variable.Value = Vector3.Distance(t1.position, t2.position);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc2659d9740e09744b583d40f78bde8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
47
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Composite.cs
Normal file
47
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Composite.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
public abstract class Composite : Node, IParentNode, IChildrenNode
|
||||
{
|
||||
private static readonly System.Random rng = new System.Random();
|
||||
|
||||
public bool random = false;
|
||||
|
||||
public override void AddChild(Node node)
|
||||
{
|
||||
if (!children.Contains(node))
|
||||
{
|
||||
// Remove parent in case there is one already
|
||||
if (node.parent != null) {
|
||||
node.parent.RemoveChild(node);
|
||||
}
|
||||
children.Add(node);
|
||||
node.parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemoveChild(Node node)
|
||||
{
|
||||
if (children.Contains(node))
|
||||
{
|
||||
children.Remove(node);
|
||||
node.parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected static void ShuffleList<T>(List<T> list)
|
||||
{
|
||||
int n = list.Count;
|
||||
while (n > 1) {
|
||||
n--;
|
||||
int k = rng.Next(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e91b1b2e9c2fc234e83cb440a812b4fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
47
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Condition.cs
Normal file
47
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Condition.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
public abstract class Condition : Decorator
|
||||
{
|
||||
protected bool lastConditionCheckResult = false;
|
||||
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
if (!TryGetChild(out Node node))
|
||||
{
|
||||
return NodeResult.failure;
|
||||
}
|
||||
if (node.status == Status.Success || node.status == Status.Failure) {
|
||||
return NodeResult.From(node.status);
|
||||
}
|
||||
lastConditionCheckResult = Check();
|
||||
if (lastConditionCheckResult == false) {
|
||||
return NodeResult.failure;
|
||||
}
|
||||
return node.runningNodeResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reevaluate condition and try to abort the tree if required
|
||||
/// </summary>
|
||||
/// <param name="abort">Abort type</param>
|
||||
protected void EvaluateConditionAndTryAbort(Abort abortType)
|
||||
{
|
||||
bool c = Check();
|
||||
if (c != lastConditionCheckResult)
|
||||
{
|
||||
lastConditionCheckResult = c;
|
||||
TryAbort(abortType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method called to check condition
|
||||
/// </summary>
|
||||
/// <returns>Condition result</returns>
|
||||
public abstract bool Check();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: caf17e0e40d8198449b5a547beb02515
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
89
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Cooldown.cs
Normal file
89
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Cooldown.cs
Normal file
@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode(name = "Decorators/Cooldown")]
|
||||
public class Cooldown : Decorator, IMonoBehaviourTreeTickListener
|
||||
{
|
||||
public AbortTypes abort = AbortTypes.None;
|
||||
[Space]
|
||||
public FloatReference time = new FloatReference(1f);
|
||||
[Tooltip("When set to true, there will be no cooldown when child node returns failure")]
|
||||
public bool resetOnChildFailure = false;
|
||||
private float cooldownTime = 0f;
|
||||
private bool entered = false;
|
||||
private bool childFailed = false;
|
||||
|
||||
public enum AbortTypes
|
||||
{
|
||||
None, LowerPriority
|
||||
}
|
||||
|
||||
public override void OnAllowInterrupt()
|
||||
{
|
||||
if (abort == AbortTypes.LowerPriority)
|
||||
{
|
||||
ObtainTreeSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
if (!TryGetChild(out Node node))
|
||||
{
|
||||
return NodeResult.failure;
|
||||
}
|
||||
if (node.status == Status.Success) {
|
||||
return NodeResult.success;
|
||||
}
|
||||
if (node.status == Status.Failure) {
|
||||
// If reset option is enabled flag will be raised and set true
|
||||
childFailed = resetOnChildFailure;
|
||||
return NodeResult.failure;
|
||||
}
|
||||
if (cooldownTime <= Time.time) {
|
||||
entered = true;
|
||||
return node.runningNodeResult;
|
||||
} else {
|
||||
return NodeResult.failure;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnExit()
|
||||
{
|
||||
// Setup cooldown and event when child was entered
|
||||
// Check reset option too
|
||||
if (entered && !childFailed)
|
||||
{
|
||||
cooldownTime = Time.time + time.Value;
|
||||
// For LowerPriority try to abort after given time
|
||||
if (abort == AbortTypes.LowerPriority)
|
||||
{
|
||||
behaviourTree.AddTickListener(this);
|
||||
}
|
||||
}
|
||||
// Reset flags
|
||||
entered = false;
|
||||
childFailed = false;
|
||||
}
|
||||
|
||||
public override void OnDisallowInterrupt()
|
||||
{
|
||||
behaviourTree.RemoveTickListener(this);
|
||||
}
|
||||
|
||||
void IMonoBehaviourTreeTickListener.OnBehaviourTreeTick()
|
||||
{
|
||||
if (cooldownTime <= Time.time)
|
||||
{
|
||||
// Task should be aborted, so there is no need to listen anymore
|
||||
behaviourTree.RemoveTickListener(this);
|
||||
TryAbort(Abort.LowerPriority);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2370820ab6f42c8449d09456ac02d66d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,68 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
public abstract class CoroutineService : Decorator
|
||||
{
|
||||
public float interval = 1f;
|
||||
public bool callOnEnter = true;
|
||||
protected Coroutine coroutine;
|
||||
private WaitForSeconds waitForSeconds;
|
||||
|
||||
public override void OnEnter()
|
||||
{
|
||||
// IMPROVEMENT: WaitForSeconds could be initialized in some special node init callback
|
||||
if (waitForSeconds == null)
|
||||
{
|
||||
// Create new WaitForSeconds
|
||||
OnValidate();
|
||||
}
|
||||
coroutine = StartCoroutine(ScheduleTask());
|
||||
if (callOnEnter)
|
||||
{
|
||||
Task();
|
||||
}
|
||||
}
|
||||
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
Node node = GetChild();
|
||||
if (node == null) {
|
||||
return NodeResult.failure;
|
||||
}
|
||||
if (node.status == Status.Success || node.status == Status.Failure) {
|
||||
return NodeResult.From(node.status);
|
||||
}
|
||||
return node.runningNodeResult;
|
||||
}
|
||||
|
||||
public abstract void Task();
|
||||
|
||||
public override void OnExit()
|
||||
{
|
||||
if (coroutine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
StopCoroutine(coroutine);
|
||||
coroutine = null;
|
||||
}
|
||||
|
||||
private IEnumerator ScheduleTask()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
yield return waitForSeconds;
|
||||
Task();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
interval = Mathf.Max(0f, interval);
|
||||
waitForSeconds = new WaitForSeconds(interval);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cf48bbd74f869449a0045ef1e4de316
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
111
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Decorator.cs
Normal file
111
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Decorator.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
public abstract class Decorator : Node, IParentNode, IChildrenNode
|
||||
{
|
||||
private Node[] stackState = new Node[0];
|
||||
|
||||
public override void AddChild(Node node)
|
||||
{
|
||||
// Allow only one children
|
||||
if (this.children.Count > 0)
|
||||
{
|
||||
Node child = this.children[0];
|
||||
if (child == node) {
|
||||
return;
|
||||
}
|
||||
child.parent.RemoveChild(child);
|
||||
this.children.Clear();
|
||||
}
|
||||
// Remove parent in case there is one already
|
||||
if (node.parent != null) {
|
||||
node.parent.RemoveChild(node);
|
||||
}
|
||||
this.children.Add(node);
|
||||
node.parent = this;
|
||||
}
|
||||
|
||||
protected Node GetChild()
|
||||
{
|
||||
if (children.Count > 0) {
|
||||
return children[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected bool TryGetChild(out Node node)
|
||||
{
|
||||
if (children.Count > 0)
|
||||
{
|
||||
node = children[0];
|
||||
return true;
|
||||
}
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool HasChild()
|
||||
{
|
||||
return children.Count > 0;
|
||||
}
|
||||
|
||||
public override void RemoveChild(Node node)
|
||||
{
|
||||
if (children.Contains(node))
|
||||
{
|
||||
children.Remove(node);
|
||||
node.parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy and store current state of execution stack if it was not saved before.
|
||||
/// </summary>
|
||||
protected void ObtainTreeSnapshot()
|
||||
{
|
||||
// Copy stack only when this method is called for the first time
|
||||
if (stackState.Length == 0)
|
||||
{
|
||||
behaviourTree.GetStack(ref stackState);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Obsolete]
|
||||
protected void DisposeBTState()
|
||||
{
|
||||
stackState = new Node[0];
|
||||
}
|
||||
|
||||
internal Node[] GetStoredTreeSnapshot()
|
||||
{
|
||||
return stackState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method used to abort nodes in valid case
|
||||
/// </summary>
|
||||
/// <param name="abort">Abort type</param>
|
||||
protected void TryAbort(Abort abort)
|
||||
{
|
||||
switch (abort)
|
||||
{
|
||||
case Abort.Self:
|
||||
if (status == Status.Running) {
|
||||
behaviourTree.Interrupt(this);
|
||||
}
|
||||
break;
|
||||
case Abort.LowerPriority:
|
||||
if (status == Status.Success || status == Status.Failure) {
|
||||
behaviourTree.Interrupt(this);
|
||||
}
|
||||
break;
|
||||
case Abort.Both:
|
||||
behaviourTree.Interrupt(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76bba4b4f778fa9409a634bedf9d1aa1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,37 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode(name = "Conditions/Distance Condition")]
|
||||
public class DistanceCondition : Condition
|
||||
{
|
||||
public Comparator comparator = Comparator.GreaterThan;
|
||||
public FloatReference distance = new FloatReference(10f);
|
||||
[Space]
|
||||
public TransformReference transform1;
|
||||
public TransformReference transform2;
|
||||
|
||||
public override bool Check()
|
||||
{
|
||||
// Squared magnitude is enough to compare distances
|
||||
float sqrMagnitude = (transform1.Value.position - transform2.Value.position).sqrMagnitude;
|
||||
float dist = distance.Value;
|
||||
if (comparator == Comparator.GreaterThan)
|
||||
{
|
||||
return sqrMagnitude > dist * dist;
|
||||
}
|
||||
else
|
||||
{
|
||||
return sqrMagnitude < dist * dist;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Comparator
|
||||
{
|
||||
GreaterThan, LessThan
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb0b7cfe3e9cfba47a378c725cf5d0b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
33
Assets/MonoBehaviourTree/Source/Runtime/Nodes/ForceResult.cs
Normal file
33
Assets/MonoBehaviourTree/Source/Runtime/Nodes/ForceResult.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode("Decorators/Force Result")]
|
||||
public class ForceResult : Decorator
|
||||
{
|
||||
[SerializeField] private ForcedResult result = ForcedResult.Success;
|
||||
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
if (!TryGetChild(out Node node))
|
||||
{
|
||||
return NodeResult.failure;
|
||||
}
|
||||
|
||||
if (node.status == Status.Success || node.status == Status.Failure)
|
||||
{
|
||||
return result == ForcedResult.Success ? NodeResult.success : NodeResult.failure;
|
||||
}
|
||||
|
||||
return node.runningNodeResult;
|
||||
}
|
||||
|
||||
private enum ForcedResult
|
||||
{
|
||||
Success, Failure
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 163d3a55bffbacb42b57e10e4d9f3d13
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
25
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Inverter.cs
Normal file
25
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Inverter.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode("Decorators/Inverter")]
|
||||
public class Inverter : Decorator
|
||||
{
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
if (!TryGetChild(out Node node))
|
||||
{
|
||||
return NodeResult.failure;
|
||||
}
|
||||
if (node.status == Status.Success) {
|
||||
return NodeResult.failure;
|
||||
} else if (node.status == Status.Failure) {
|
||||
return NodeResult.success;
|
||||
}
|
||||
return node.runningNodeResult;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df7dcd3c2da37eb4ba49162d0ca654c9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,86 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode(name = "Tasks/Invoke Unity Event")]
|
||||
public class InvokeUnityEvent : Leaf
|
||||
{
|
||||
public EventType type;
|
||||
|
||||
public TransformReference transformReference = new TransformReference();
|
||||
public GameObjectReference gameObjectReference = new GameObjectReference();
|
||||
public FloatReference floatReference = new FloatReference();
|
||||
public IntReference intReference = new IntReference();
|
||||
public BoolReference boolReference = new BoolReference();
|
||||
public StringReference stringReference = new StringReference();
|
||||
public Vector3Reference vector3Reference = new Vector3Reference();
|
||||
public Vector2Reference vector2Reference = new Vector2Reference();
|
||||
|
||||
public TransformEvent transformEvent;
|
||||
public GameObjectEvent gameObjectEvent;
|
||||
public FloatEvent floatEvent;
|
||||
public IntEvent intEvent;
|
||||
public BoolEvent boolEvent;
|
||||
public StringEvent stringEvent;
|
||||
public Vector3Event vector3Event;
|
||||
public Vector2Event vector2Event;
|
||||
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EventType.Transform: transformEvent.Invoke(transformReference.Value);
|
||||
break;
|
||||
case EventType.Float: floatEvent.Invoke(floatReference.Value);
|
||||
break;
|
||||
case EventType.Bool: boolEvent.Invoke(boolReference.Value);
|
||||
break;
|
||||
case EventType.String: stringEvent.Invoke(stringReference.Value);
|
||||
break;
|
||||
case EventType.Vector3: vector3Event.Invoke(vector3Reference.Value);
|
||||
break;
|
||||
case EventType.Vector2: vector2Event.Invoke(vector2Reference.Value);
|
||||
break;
|
||||
case EventType.Int: intEvent.Invoke(intReference.Value);
|
||||
break;
|
||||
case EventType.GameObject: gameObjectEvent.Invoke(gameObjectReference.Value);
|
||||
break;
|
||||
}
|
||||
return NodeResult.success;
|
||||
}
|
||||
|
||||
public override bool IsValid()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EventType.Transform: return !transformReference.isInvalid;
|
||||
case EventType.Float: return !floatReference.isInvalid;
|
||||
case EventType.Bool: return !boolReference.isInvalid;
|
||||
case EventType.String: return !stringReference.isInvalid;
|
||||
case EventType.Vector3: return !vector3Reference.isInvalid;
|
||||
case EventType.Vector2: return !vector2Reference.isInvalid;
|
||||
case EventType.Int: return !intReference.isInvalid;
|
||||
case EventType.GameObject: return !gameObjectReference.isInvalid;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EventType
|
||||
{
|
||||
Transform, GameObject, Float, Int, Bool, String, Vector3, Vector2
|
||||
}
|
||||
|
||||
[System.Serializable] public class TransformEvent : UnityEvent<Transform>{}
|
||||
[System.Serializable] public class GameObjectEvent : UnityEvent<GameObject>{}
|
||||
[System.Serializable] public class FloatEvent : UnityEvent<float>{}
|
||||
[System.Serializable] public class IntEvent : UnityEvent<int>{}
|
||||
[System.Serializable] public class BoolEvent : UnityEvent<bool>{}
|
||||
[System.Serializable] public class StringEvent : UnityEvent<string>{}
|
||||
[System.Serializable] public class Vector3Event : UnityEvent<Vector3>{}
|
||||
[System.Serializable] public class Vector2Event : UnityEvent<Vector2>{}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d47807df5b5587e44bfd9aface30c12f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
102
Assets/MonoBehaviourTree/Source/Runtime/Nodes/IsSetCondition.cs
Normal file
102
Assets/MonoBehaviourTree/Source/Runtime/Nodes/IsSetCondition.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode(name = "Conditions/Is Set Condition")]
|
||||
public class IsSetCondition : Condition
|
||||
{
|
||||
public Abort abort;
|
||||
public bool invert = false;
|
||||
public Type type = Type.Boolean;
|
||||
public BoolReference boolReference = new BoolReference(VarRefMode.DisableConstant);
|
||||
public GameObjectReference objectReference = new GameObjectReference(VarRefMode.DisableConstant);
|
||||
public TransformReference transformReference = new TransformReference(VarRefMode.DisableConstant);
|
||||
|
||||
public override bool Check()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Type.Boolean:
|
||||
return (boolReference.Value == true) ^ invert;
|
||||
case Type.GameObject:
|
||||
return (objectReference.Value != null) ^ invert;
|
||||
case Type.Transform:
|
||||
return (transformReference.Value != null) ^ invert;
|
||||
}
|
||||
return invert;
|
||||
}
|
||||
|
||||
public override void OnAllowInterrupt()
|
||||
{
|
||||
if (abort != Abort.None)
|
||||
{
|
||||
ObtainTreeSnapshot();
|
||||
switch (type)
|
||||
{
|
||||
case Type.Boolean:
|
||||
boolReference.GetVariable().AddListener(OnVariableChange);
|
||||
break;
|
||||
case Type.GameObject:
|
||||
objectReference.GetVariable().AddListener(OnVariableChange);
|
||||
break;
|
||||
case Type.Transform:
|
||||
transformReference.GetVariable().AddListener(OnVariableChange);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDisallowInterrupt()
|
||||
{
|
||||
if (abort != Abort.None)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Type.Boolean:
|
||||
boolReference.GetVariable().RemoveListener(OnVariableChange);
|
||||
break;
|
||||
case Type.GameObject:
|
||||
objectReference.GetVariable().RemoveListener(OnVariableChange);
|
||||
break;
|
||||
case Type.Transform:
|
||||
transformReference.GetVariable().RemoveListener(OnVariableChange);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVariableChange(bool oldValue, bool newValue)
|
||||
{
|
||||
EvaluateConditionAndTryAbort(abort);
|
||||
}
|
||||
|
||||
private void OnVariableChange(GameObject oldValue, GameObject newValue)
|
||||
{
|
||||
EvaluateConditionAndTryAbort(abort);
|
||||
}
|
||||
|
||||
private void OnVariableChange(Transform oldValue, Transform newValue)
|
||||
{
|
||||
EvaluateConditionAndTryAbort(abort);
|
||||
}
|
||||
|
||||
public override bool IsValid()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Type.Boolean: return !boolReference.isInvalid;
|
||||
case Type.GameObject: return !objectReference.isInvalid;
|
||||
case Type.Transform: return !transformReference.isInvalid;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type
|
||||
{
|
||||
Boolean, GameObject, Transform
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 534e3ec7de6cd444e984c2e7f68e42e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
19
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Leaf.cs
Normal file
19
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Leaf.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
public abstract class Leaf : Node, IChildrenNode
|
||||
{
|
||||
public sealed override void AddChild(Node node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public sealed override void RemoveChild(Node node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Leaf.cs.meta
Normal file
11
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Leaf.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cb4093606af8044fbf4aa9fac6dca97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
80
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Loop.cs
Normal file
80
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Loop.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode(name = "Decorators/Loop")]
|
||||
public class Loop : Decorator
|
||||
{
|
||||
public IntReference loops = new IntReference(3);
|
||||
public BoolReference infinite = new BoolReference(false);
|
||||
[Tooltip("Break loop when selected result is returned by child.")]
|
||||
public BreakMode breakOnStatus = BreakMode.Disabled;
|
||||
[Space]
|
||||
[Tooltip("Result returned by this node after the loop ends.")]
|
||||
public ResultRemapMode resultOnFinish = ResultRemapMode.Success;
|
||||
[Tooltip("The result returned by this node when loop is broken.")]
|
||||
public ResultRemapMode resultOnBreak = ResultRemapMode.Failure;
|
||||
private int count;
|
||||
|
||||
public enum ResultRemapMode
|
||||
{
|
||||
Success = 0,
|
||||
Failure = 1,
|
||||
Inherit = 2,
|
||||
InheritInverted = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum mapped to Status enum. Disabled is casted to 'running' as this state is never returned by child.
|
||||
/// </summary>
|
||||
public enum BreakMode
|
||||
{
|
||||
Disabled = 2,
|
||||
Success = 0,
|
||||
Failure = 1,
|
||||
}
|
||||
|
||||
public override void OnEnter()
|
||||
{
|
||||
count = loops.Value;
|
||||
}
|
||||
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
if (!TryGetChild(out Node node))
|
||||
{
|
||||
return NodeResult.failure;
|
||||
}
|
||||
|
||||
if (node.status == (Status)breakOnStatus)
|
||||
{
|
||||
return RemapResult(resultOnBreak, node.status);
|
||||
}
|
||||
|
||||
if (count > 0 || infinite.Value)
|
||||
{
|
||||
// Repeat children
|
||||
behaviourTree.ResetNodesTo(this);
|
||||
count -= 1;
|
||||
return node.runningNodeResult;
|
||||
}
|
||||
|
||||
return RemapResult(resultOnFinish, node.status);
|
||||
}
|
||||
|
||||
private NodeResult RemapResult(ResultRemapMode mode, Status childStatus)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ResultRemapMode.Success: return NodeResult.success;
|
||||
case ResultRemapMode.Failure: return NodeResult.failure;
|
||||
case ResultRemapMode.Inherit: return childStatus == Status.Success ? NodeResult.success : NodeResult.failure;
|
||||
case ResultRemapMode.InheritInverted: return childStatus == Status.Success ? NodeResult.failure : NodeResult.success;
|
||||
default: Debug.LogError("Unexpected behaviour", this); return NodeResult.failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Loop.cs.meta
Normal file
11
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Loop.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8595d37ac5b0f340b7f64ca227f8860
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
162
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Node.cs
Normal file
162
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Node.cs
Normal file
@ -0,0 +1,162 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[RequireComponent(typeof(MonoBehaviourTree))]
|
||||
public abstract class Node : MonoBehaviour
|
||||
{
|
||||
public const float NODE_DEFAULT_WIDTH = 160f;
|
||||
|
||||
public string title;
|
||||
[HideInInspector]
|
||||
public Rect rect = new Rect(0, 0, NODE_DEFAULT_WIDTH, 50);
|
||||
[HideInInspector]
|
||||
public Node parent;
|
||||
[HideInInspector]
|
||||
public List<Node> children = new List<Node>();
|
||||
[System.NonSerialized]
|
||||
public Status status = Status.Ready;
|
||||
[HideInInspector]
|
||||
public MonoBehaviourTree behaviourTree;
|
||||
// [HideInInspector]
|
||||
public NodeResult runningNodeResult { get; internal set;}
|
||||
[HideInInspector]
|
||||
public int runtimePriority = 0;
|
||||
[HideInInspector]
|
||||
public bool breakpoint = false;
|
||||
private bool _selected = false;
|
||||
public bool selected
|
||||
{
|
||||
get { return _selected; }
|
||||
set { _selected = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Time of last tick retrieved from Time.time
|
||||
/// </summary>
|
||||
public float LastTick => behaviourTree.LastTick;
|
||||
/// <summary>
|
||||
/// The interval in seconds from the last tick of behaviour tree.
|
||||
/// </summary>
|
||||
public float DeltaTime => Time.time - behaviourTree.LastTick;
|
||||
|
||||
public virtual void OnAllowInterrupt() {}
|
||||
public virtual void OnEnter() {}
|
||||
public abstract NodeResult Execute();
|
||||
public virtual void OnExit() {}
|
||||
public virtual void OnDisallowInterrupt() {}
|
||||
|
||||
public virtual void OnBehaviourTreeAbort() {}
|
||||
|
||||
public abstract void AddChild(Node node);
|
||||
public abstract void RemoveChild(Node node);
|
||||
|
||||
public virtual Node GetParent()
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
public virtual List<Node> GetChildren()
|
||||
{
|
||||
return children;
|
||||
}
|
||||
|
||||
public bool IsDescendantOf(Node node)
|
||||
{
|
||||
if (this.parent == null) {
|
||||
return false;
|
||||
} else if (this.parent == node) {
|
||||
return true;
|
||||
}
|
||||
return this.parent.IsDescendantOf(node);
|
||||
}
|
||||
|
||||
public List<Node> GetAllSuccessors()
|
||||
{
|
||||
List<Node> result = new List<Node>();
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
result.Add(children[i]);
|
||||
result.AddRange(children[i].GetAllSuccessors());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SortChildren()
|
||||
{
|
||||
this.children.Sort((c, d) => c.rect.x.CompareTo(d.rect.x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if node setup is valid
|
||||
/// </summary>
|
||||
/// <returns>Returns true if node is configured correctly</returns>
|
||||
public virtual bool IsValid()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
System.Reflection.FieldInfo[] propertyInfos = this.GetType().GetFields();
|
||||
for (int i = 0; i < propertyInfos.Length; i++)
|
||||
{
|
||||
if (propertyInfos[i].FieldType.IsSubclassOf(typeof(BaseVariableReference)))
|
||||
{
|
||||
BaseVariableReference varReference = propertyInfos[i].GetValue(this) as BaseVariableReference;
|
||||
if (varReference != null && varReference.isInvalid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Status
|
||||
{
|
||||
Success = 0,
|
||||
Failure = 1,
|
||||
Running = 2,
|
||||
Ready = 3
|
||||
}
|
||||
|
||||
public enum Abort
|
||||
{
|
||||
None, Self, LowerPriority, Both
|
||||
}
|
||||
|
||||
public class NodeResult
|
||||
{
|
||||
public Status status {get; private set;}
|
||||
public Node child {get; private set;}
|
||||
|
||||
public NodeResult(Status status, Node child = null)
|
||||
{
|
||||
this.status = status;
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
public static NodeResult From(Status s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case Status.Success: return success;
|
||||
case Status.Failure: return failure;
|
||||
default: return running;
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly NodeResult success = new NodeResult(Status.Success);
|
||||
public static readonly NodeResult failure = new NodeResult(Status.Failure);
|
||||
public static readonly NodeResult running = new NodeResult(Status.Running);
|
||||
}
|
||||
|
||||
public interface IChildrenNode{
|
||||
// void SetParent(Node node);
|
||||
}
|
||||
|
||||
public interface IParentNode{
|
||||
// void AddChild(Node node);
|
||||
}
|
||||
}
|
11
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Node.cs.meta
Normal file
11
Assets/MonoBehaviourTree/Source/Runtime/Nodes/Node.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d96d8d07ac425543b9f24ad5120b51d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
125
Assets/MonoBehaviourTree/Source/Runtime/Nodes/NumberCondition.cs
Normal file
125
Assets/MonoBehaviourTree/Source/Runtime/Nodes/NumberCondition.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode(name = "Conditions/Number Condition")]
|
||||
public class NumberCondition : Condition
|
||||
{
|
||||
public Abort abort;
|
||||
public Type type = Type.Float;
|
||||
public FloatReference floatReference = new FloatReference(VarRefMode.DisableConstant);
|
||||
public IntReference intReference = new IntReference(VarRefMode.DisableConstant);
|
||||
public Comparator comparator = Comparator.Equal;
|
||||
public FloatReference floatReference2 = new FloatReference(0f);
|
||||
public IntReference intReference2 = new IntReference(0);
|
||||
|
||||
// IMPROVEMENT: This class could be split into to different nodes
|
||||
public override bool Check()
|
||||
{
|
||||
if (type == Type.Float)
|
||||
{
|
||||
switch (comparator)
|
||||
{
|
||||
case Comparator.Equal:
|
||||
return floatReference.Value == floatReference2.Value;
|
||||
case Comparator.GreaterThan:
|
||||
return floatReference.Value > floatReference2.Value;
|
||||
case Comparator.LessThan:
|
||||
return floatReference.Value < floatReference2.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (comparator)
|
||||
{
|
||||
case Comparator.Equal:
|
||||
return intReference.Value == intReference2.Value;
|
||||
case Comparator.GreaterThan:
|
||||
return intReference.Value > intReference2.Value;
|
||||
case Comparator.LessThan:
|
||||
return intReference.Value < intReference2.Value;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnAllowInterrupt()
|
||||
{
|
||||
if (abort != Abort.None)
|
||||
{
|
||||
ObtainTreeSnapshot();
|
||||
if (type == Type.Float) {
|
||||
floatReference.GetVariable().AddListener(OnVariableChange);
|
||||
if (!floatReference2.isConstant)
|
||||
{
|
||||
floatReference2.GetVariable().AddListener(OnVariableChange);
|
||||
}
|
||||
} else {
|
||||
intReference.GetVariable().AddListener(OnVariableChange);
|
||||
if (!intReference2.isConstant)
|
||||
{
|
||||
intReference2.GetVariable().AddListener(OnVariableChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDisallowInterrupt()
|
||||
{
|
||||
if (abort != Abort.None)
|
||||
{
|
||||
if (type == Type.Float) {
|
||||
floatReference.GetVariable().RemoveListener(OnVariableChange);
|
||||
if (!floatReference2.isConstant)
|
||||
{
|
||||
floatReference2.GetVariable().RemoveListener(OnVariableChange);
|
||||
}
|
||||
} else {
|
||||
intReference.GetVariable().RemoveListener(OnVariableChange);
|
||||
if (!intReference2.isConstant)
|
||||
{
|
||||
intReference2.GetVariable().RemoveListener(OnVariableChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVariableChange(float newVal, float oldVal)
|
||||
{
|
||||
EvaluateConditionAndTryAbort(abort);
|
||||
}
|
||||
|
||||
private void OnVariableChange(int newVal, int oldVal)
|
||||
{
|
||||
EvaluateConditionAndTryAbort(abort);
|
||||
}
|
||||
|
||||
public override bool IsValid()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Type.Float: return !(floatReference.isInvalid || floatReference2.isInvalid);
|
||||
case Type.Int: return !(intReference.isInvalid || intReference2.isInvalid);
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type
|
||||
{
|
||||
Float, Int
|
||||
}
|
||||
|
||||
public enum Comparator
|
||||
{
|
||||
[InspectorName("==")]
|
||||
Equal,
|
||||
[InspectorName(">")]
|
||||
GreaterThan,
|
||||
[InspectorName("<")]
|
||||
LessThan
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0b95a918be8d9a4f9085201010bdec1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,43 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode("Decorators/Random Chance")]
|
||||
public class RandomChance : Decorator
|
||||
{
|
||||
[Tooltip("Probability should be between 0 and 1")]
|
||||
public FloatReference probability = new FloatReference(0.5f);
|
||||
private float roll;
|
||||
|
||||
public override void OnAllowInterrupt()
|
||||
{
|
||||
roll = Random.Range(0f, 1f);
|
||||
}
|
||||
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
Node node = GetChild();
|
||||
if (node == null) {
|
||||
return NodeResult.failure;
|
||||
}
|
||||
if (node.status == Status.Success || node.status == Status.Failure) {
|
||||
return NodeResult.From(node.status);
|
||||
}
|
||||
if (roll > probability.Value) {
|
||||
return NodeResult.failure;
|
||||
}
|
||||
return node.runningNodeResult;
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (probability.isConstant)
|
||||
{
|
||||
probability.Value = Mathf.Clamp(probability.GetConstant(), 0f, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9f8fc43d8d667e46a433232d46363ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
29
Assets/MonoBehaviourTree/Source/Runtime/Nodes/RandomFloat.cs
Normal file
29
Assets/MonoBehaviourTree/Source/Runtime/Nodes/RandomFloat.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MBT
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
[MBTNode(name = "Tasks/Random Float")]
|
||||
public class RandomFloat : Leaf
|
||||
{
|
||||
public FloatReference min = new FloatReference(0f);
|
||||
public FloatReference max = new FloatReference(1f);
|
||||
public FloatReference output = new FloatReference(VarRefMode.DisableConstant);
|
||||
|
||||
public override NodeResult Execute()
|
||||
{
|
||||
output.Value = Random.Range(min.Value, max.Value);
|
||||
return NodeResult.success;
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (min.isConstant && max.isConstant)
|
||||
{
|
||||
min.Value = Mathf.Min(min.GetConstant(), max.GetConstant());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c4b91b5cfd9ae042ba2496eb83eeb0c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user