[Add] Synaptic AI Pro
https://assetstore.unity.com/packages/tools/generative-ai/synaptic-ai-pro-natural-language-control-for-unity-336030
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SynapticPro.BehaviorTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Selector (OR) - Returns Success if ANY child succeeds
|
||||
/// Tries children in order until one succeeds
|
||||
/// </summary>
|
||||
public class BTSelector : BTComposite
|
||||
{
|
||||
public BTSelector(string name = "Selector") : base(name) { }
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
for (int i = currentChildIndex; i < children.Count; i++)
|
||||
{
|
||||
var status = children[i].Tick();
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case BTStatus.Success:
|
||||
currentChildIndex = 0;
|
||||
IsRunning = false;
|
||||
return BTStatus.Success;
|
||||
|
||||
case BTStatus.Running:
|
||||
currentChildIndex = i;
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
|
||||
case BTStatus.Failure:
|
||||
// Try next child
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// All children failed
|
||||
currentChildIndex = 0;
|
||||
IsRunning = false;
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sequence (AND) - Returns Success only if ALL children succeed
|
||||
/// Executes children in order, stops on first failure
|
||||
/// </summary>
|
||||
public class BTSequence : BTComposite
|
||||
{
|
||||
public BTSequence(string name = "Sequence") : base(name) { }
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
for (int i = currentChildIndex; i < children.Count; i++)
|
||||
{
|
||||
var status = children[i].Tick();
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case BTStatus.Success:
|
||||
// Continue to next child
|
||||
continue;
|
||||
|
||||
case BTStatus.Running:
|
||||
currentChildIndex = i;
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
|
||||
case BTStatus.Failure:
|
||||
currentChildIndex = 0;
|
||||
IsRunning = false;
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
}
|
||||
|
||||
// All children succeeded
|
||||
currentChildIndex = 0;
|
||||
IsRunning = false;
|
||||
return BTStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parallel - Runs all children simultaneously
|
||||
/// Returns based on policy (RequireAll or RequireOne)
|
||||
/// </summary>
|
||||
public class BTParallel : BTComposite
|
||||
{
|
||||
public enum Policy
|
||||
{
|
||||
RequireAll, // Success only if ALL succeed
|
||||
RequireOne // Success if ANY succeeds
|
||||
}
|
||||
|
||||
public Policy SuccessPolicy { get; set; } = Policy.RequireAll;
|
||||
public Policy FailurePolicy { get; set; } = Policy.RequireOne;
|
||||
|
||||
private List<BTStatus> childStatuses = new List<BTStatus>();
|
||||
|
||||
public BTParallel(string name = "Parallel") : base(name) { }
|
||||
|
||||
public BTParallel(Policy successPolicy, Policy failurePolicy, string name = "Parallel") : base(name)
|
||||
{
|
||||
SuccessPolicy = successPolicy;
|
||||
FailurePolicy = failurePolicy;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
// Initialize status tracking
|
||||
while (childStatuses.Count < children.Count)
|
||||
{
|
||||
childStatuses.Add(BTStatus.Running);
|
||||
}
|
||||
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
int runningCount = 0;
|
||||
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
// Skip already completed children
|
||||
if (childStatuses[i] != BTStatus.Running)
|
||||
{
|
||||
if (childStatuses[i] == BTStatus.Success) successCount++;
|
||||
else failureCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var status = children[i].Tick();
|
||||
childStatuses[i] = status;
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case BTStatus.Success:
|
||||
successCount++;
|
||||
break;
|
||||
case BTStatus.Failure:
|
||||
failureCount++;
|
||||
break;
|
||||
case BTStatus.Running:
|
||||
runningCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check failure policy
|
||||
if (FailurePolicy == Policy.RequireOne && failureCount > 0)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
if (FailurePolicy == Policy.RequireAll && failureCount == children.Count)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
|
||||
// Check success policy
|
||||
if (SuccessPolicy == Policy.RequireOne && successCount > 0)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
}
|
||||
if (SuccessPolicy == Policy.RequireAll && successCount == children.Count)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
// Still running
|
||||
if (runningCount > 0)
|
||||
{
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
|
||||
// Default to failure
|
||||
Reset();
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
childStatuses.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Random Selector - Randomly picks a child to execute
|
||||
/// </summary>
|
||||
public class BTRandomSelector : BTComposite
|
||||
{
|
||||
private List<int> shuffledIndices = new List<int>();
|
||||
private int currentIndex = 0;
|
||||
|
||||
public BTRandomSelector(string name = "RandomSelector") : base(name) { }
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
// Shuffle on first tick
|
||||
if (shuffledIndices.Count == 0)
|
||||
{
|
||||
ShuffleChildren();
|
||||
}
|
||||
|
||||
for (int i = currentIndex; i < shuffledIndices.Count; i++)
|
||||
{
|
||||
var childIndex = shuffledIndices[i];
|
||||
var status = children[childIndex].Tick();
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case BTStatus.Success:
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
|
||||
case BTStatus.Running:
|
||||
currentIndex = i;
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
|
||||
case BTStatus.Failure:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Reset();
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
|
||||
private void ShuffleChildren()
|
||||
{
|
||||
shuffledIndices.Clear();
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
shuffledIndices.Add(i);
|
||||
}
|
||||
|
||||
// Fisher-Yates shuffle
|
||||
for (int i = shuffledIndices.Count - 1; i > 0; i--)
|
||||
{
|
||||
int j = Random.Range(0, i + 1);
|
||||
var temp = shuffledIndices[i];
|
||||
shuffledIndices[i] = shuffledIndices[j];
|
||||
shuffledIndices[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
shuffledIndices.Clear();
|
||||
currentIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Random Sequence - Executes children in random order
|
||||
/// </summary>
|
||||
public class BTRandomSequence : BTComposite
|
||||
{
|
||||
private List<int> shuffledIndices = new List<int>();
|
||||
private int currentIndex = 0;
|
||||
|
||||
public BTRandomSequence(string name = "RandomSequence") : base(name) { }
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
// Shuffle on first tick
|
||||
if (shuffledIndices.Count == 0)
|
||||
{
|
||||
ShuffleChildren();
|
||||
}
|
||||
|
||||
for (int i = currentIndex; i < shuffledIndices.Count; i++)
|
||||
{
|
||||
var childIndex = shuffledIndices[i];
|
||||
var status = children[childIndex].Tick();
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case BTStatus.Success:
|
||||
continue;
|
||||
|
||||
case BTStatus.Running:
|
||||
currentIndex = i;
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
|
||||
case BTStatus.Failure:
|
||||
Reset();
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
}
|
||||
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
private void ShuffleChildren()
|
||||
{
|
||||
shuffledIndices.Clear();
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
shuffledIndices.Add(i);
|
||||
}
|
||||
|
||||
for (int i = shuffledIndices.Count - 1; i > 0; i--)
|
||||
{
|
||||
int j = Random.Range(0, i + 1);
|
||||
var temp = shuffledIndices[i];
|
||||
shuffledIndices[i] = shuffledIndices[j];
|
||||
shuffledIndices[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
shuffledIndices.Clear();
|
||||
currentIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86de43f4d7cbe473fbd33222e6a6b693
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/Runtime/BehaviorTree/BTComposites.cs
|
||||
uploadId: 920982
|
||||
@@ -0,0 +1,395 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SynapticPro.BehaviorTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Inverter - Inverts the result of child
|
||||
/// Success -> Failure, Failure -> Success, Running -> Running
|
||||
/// </summary>
|
||||
public class BTInverter : BTDecorator
|
||||
{
|
||||
public BTInverter(string name = "Inverter") : base(name) { }
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (child == null) return BTStatus.Failure;
|
||||
|
||||
var status = child.Tick();
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case BTStatus.Success:
|
||||
return BTStatus.Failure;
|
||||
case BTStatus.Failure:
|
||||
return BTStatus.Success;
|
||||
default:
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Succeeder - Always returns Success (ignores child result)
|
||||
/// </summary>
|
||||
public class BTSucceeder : BTDecorator
|
||||
{
|
||||
public BTSucceeder(string name = "Succeeder") : base(name) { }
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (child == null) return BTStatus.Success;
|
||||
|
||||
var status = child.Tick();
|
||||
|
||||
if (status == BTStatus.Running)
|
||||
{
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
|
||||
return BTStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Failer - Always returns Failure (ignores child result)
|
||||
/// </summary>
|
||||
public class BTFailer : BTDecorator
|
||||
{
|
||||
public BTFailer(string name = "Failer") : base(name) { }
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (child == null) return BTStatus.Failure;
|
||||
|
||||
var status = child.Tick();
|
||||
|
||||
if (status == BTStatus.Running)
|
||||
{
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repeater - Repeats child execution N times or until condition
|
||||
/// </summary>
|
||||
public class BTRepeater : BTDecorator
|
||||
{
|
||||
public int RepeatCount { get; set; } = -1; // -1 = infinite
|
||||
public bool RepeatUntilFail { get; set; } = false;
|
||||
public bool RepeatUntilSuccess { get; set; } = false;
|
||||
|
||||
private int currentCount = 0;
|
||||
|
||||
public BTRepeater(int count = -1, string name = "Repeater") : base(name)
|
||||
{
|
||||
RepeatCount = count;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (child == null) return BTStatus.Failure;
|
||||
|
||||
var status = child.Tick();
|
||||
|
||||
if (status == BTStatus.Running)
|
||||
{
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
|
||||
// Check termination conditions
|
||||
if (RepeatUntilFail && status == BTStatus.Failure)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
if (RepeatUntilSuccess && status == BTStatus.Success)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
currentCount++;
|
||||
|
||||
// Check count limit
|
||||
if (RepeatCount > 0 && currentCount >= RepeatCount)
|
||||
{
|
||||
Reset();
|
||||
return status;
|
||||
}
|
||||
|
||||
// Reset child and continue
|
||||
child.Reset();
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
currentCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cooldown - Prevents child execution for a duration after completion
|
||||
/// </summary>
|
||||
public class BTCooldown : BTDecorator
|
||||
{
|
||||
public float CooldownTime { get; set; } = 1f;
|
||||
|
||||
private float lastExecutionTime = float.MinValue;
|
||||
private bool childCompleted = false;
|
||||
|
||||
public BTCooldown(float cooldown, string name = "Cooldown") : base(name)
|
||||
{
|
||||
CooldownTime = cooldown;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (child == null) return BTStatus.Failure;
|
||||
|
||||
// Check cooldown
|
||||
float currentTime = Time.time;
|
||||
if (currentTime - lastExecutionTime < CooldownTime)
|
||||
{
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
|
||||
var status = child.Tick();
|
||||
|
||||
if (status == BTStatus.Running)
|
||||
{
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
|
||||
// Child completed, start cooldown
|
||||
lastExecutionTime = currentTime;
|
||||
IsRunning = false;
|
||||
return status;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
// Don't reset lastExecutionTime - cooldown persists
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force reset cooldown
|
||||
/// </summary>
|
||||
public void ResetCooldown()
|
||||
{
|
||||
lastExecutionTime = float.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timeout - Fails if child takes too long
|
||||
/// </summary>
|
||||
public class BTTimeout : BTDecorator
|
||||
{
|
||||
public float TimeoutDuration { get; set; } = 5f;
|
||||
|
||||
private float startTime;
|
||||
private bool started = false;
|
||||
|
||||
public BTTimeout(float timeout, string name = "Timeout") : base(name)
|
||||
{
|
||||
TimeoutDuration = timeout;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (child == null) return BTStatus.Failure;
|
||||
|
||||
if (!started)
|
||||
{
|
||||
startTime = Time.time;
|
||||
started = true;
|
||||
}
|
||||
|
||||
// Check timeout
|
||||
if (Time.time - startTime >= TimeoutDuration)
|
||||
{
|
||||
child.Abort();
|
||||
Reset();
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
|
||||
var status = child.Tick();
|
||||
|
||||
if (status != BTStatus.Running)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
IsRunning = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
started = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Condition Decorator - Only executes child if condition is true
|
||||
/// </summary>
|
||||
public class BTConditional : BTDecorator
|
||||
{
|
||||
private Func<BTContext, bool> condition;
|
||||
|
||||
public BTConditional(Func<BTContext, bool> condition, string name = "Conditional") : base(name)
|
||||
{
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (condition == null || !condition(Context))
|
||||
{
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
|
||||
if (child == null) return BTStatus.Success;
|
||||
|
||||
var status = child.Tick();
|
||||
IsRunning = status == BTStatus.Running;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retry - Retries child on failure
|
||||
/// </summary>
|
||||
public class BTRetry : BTDecorator
|
||||
{
|
||||
public int MaxRetries { get; set; } = 3;
|
||||
|
||||
private int currentRetries = 0;
|
||||
|
||||
public BTRetry(int maxRetries = 3, string name = "Retry") : base(name)
|
||||
{
|
||||
MaxRetries = maxRetries;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (child == null) return BTStatus.Failure;
|
||||
|
||||
var status = child.Tick();
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case BTStatus.Success:
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
|
||||
case BTStatus.Running:
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
|
||||
case BTStatus.Failure:
|
||||
currentRetries++;
|
||||
if (currentRetries >= MaxRetries)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
child.Reset();
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
|
||||
return BTStatus.Failure;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
currentRetries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delay - Waits before executing child
|
||||
/// </summary>
|
||||
public class BTDelay : BTDecorator
|
||||
{
|
||||
public float DelayTime { get; set; } = 1f;
|
||||
|
||||
private float startTime;
|
||||
private bool waiting = false;
|
||||
private bool delayComplete = false;
|
||||
|
||||
public BTDelay(float delay, string name = "Delay") : base(name)
|
||||
{
|
||||
DelayTime = delay;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (!waiting && !delayComplete)
|
||||
{
|
||||
startTime = Time.time;
|
||||
waiting = true;
|
||||
}
|
||||
|
||||
if (waiting)
|
||||
{
|
||||
if (Time.time - startTime >= DelayTime)
|
||||
{
|
||||
waiting = false;
|
||||
delayComplete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
}
|
||||
|
||||
if (child == null)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
var status = child.Tick();
|
||||
|
||||
if (status != BTStatus.Running)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
IsRunning = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
waiting = false;
|
||||
delayComplete = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58ef143ab804043b2beb2a2ca8bfd85d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/Runtime/BehaviorTree/BTDecorators.cs
|
||||
uploadId: 920982
|
||||
@@ -0,0 +1,356 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SynapticPro.BehaviorTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Action node - Executes a function
|
||||
/// </summary>
|
||||
public class BTAction : BTNode
|
||||
{
|
||||
private Func<BTContext, BTStatus> action;
|
||||
private Action<BTContext> onStart;
|
||||
private Action<BTContext> onEnd;
|
||||
|
||||
public BTAction(Func<BTContext, BTStatus> action, string name = "Action") : base(name)
|
||||
{
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public BTAction(Action<BTContext> simpleAction, string name = "Action") : base(name)
|
||||
{
|
||||
this.action = (ctx) =>
|
||||
{
|
||||
simpleAction?.Invoke(ctx);
|
||||
return BTStatus.Success;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set callback for when action starts
|
||||
/// </summary>
|
||||
public BTAction OnStart(Action<BTContext> callback)
|
||||
{
|
||||
onStart = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set callback for when action ends
|
||||
/// </summary>
|
||||
public BTAction OnEnd(Action<BTContext> callback)
|
||||
{
|
||||
onEnd = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (action == null) return BTStatus.Failure;
|
||||
|
||||
if (!IsRunning)
|
||||
{
|
||||
onStart?.Invoke(Context);
|
||||
}
|
||||
|
||||
var status = action(Context);
|
||||
IsRunning = status == BTStatus.Running;
|
||||
|
||||
if (status != BTStatus.Running)
|
||||
{
|
||||
onEnd?.Invoke(Context);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public override void Abort()
|
||||
{
|
||||
base.Abort();
|
||||
onEnd?.Invoke(Context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Condition node - Checks a condition
|
||||
/// </summary>
|
||||
public class BTCondition : BTNode
|
||||
{
|
||||
private Func<BTContext, bool> condition;
|
||||
|
||||
public BTCondition(Func<BTContext, bool> condition, string name = "Condition") : base(name)
|
||||
{
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (condition == null) return BTStatus.Failure;
|
||||
|
||||
return condition(Context) ? BTStatus.Success : BTStatus.Failure;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait node - Waits for specified duration
|
||||
/// </summary>
|
||||
public class BTWait : BTNode
|
||||
{
|
||||
public float Duration { get; set; }
|
||||
|
||||
private float startTime;
|
||||
private bool started = false;
|
||||
|
||||
public BTWait(float duration, string name = "Wait") : base(name)
|
||||
{
|
||||
Duration = duration;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (!started)
|
||||
{
|
||||
startTime = Time.time;
|
||||
started = true;
|
||||
}
|
||||
|
||||
if (Time.time - startTime >= Duration)
|
||||
{
|
||||
Reset();
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
started = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log node - Logs a message (for debugging)
|
||||
/// </summary>
|
||||
public class BTLog : BTNode
|
||||
{
|
||||
private string message;
|
||||
private Func<BTContext, string> dynamicMessage;
|
||||
|
||||
public BTLog(string message, string name = "Log") : base(name)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public BTLog(Func<BTContext, string> messageFunc, string name = "Log") : base(name)
|
||||
{
|
||||
this.dynamicMessage = messageFunc;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
string msg = dynamicMessage != null ? dynamicMessage(Context) : message;
|
||||
Debug.Log($"[BT] {msg}");
|
||||
return BTStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set Value node - Sets a value in context
|
||||
/// </summary>
|
||||
public class BTSetValue<T> : BTNode
|
||||
{
|
||||
private string key;
|
||||
private T value;
|
||||
private Func<BTContext, T> valueFunc;
|
||||
|
||||
public BTSetValue(string key, T value, string name = "SetValue") : base(name)
|
||||
{
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public BTSetValue(string key, Func<BTContext, T> valueFunc, string name = "SetValue") : base(name)
|
||||
{
|
||||
this.key = key;
|
||||
this.valueFunc = valueFunc;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
T val = valueFunc != null ? valueFunc(Context) : value;
|
||||
Context.Set(key, val);
|
||||
return BTStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check Value node - Checks a value in context
|
||||
/// </summary>
|
||||
public class BTCheckValue<T> : BTNode where T : IEquatable<T>
|
||||
{
|
||||
private string key;
|
||||
private T expectedValue;
|
||||
private Func<T, T, bool> comparer;
|
||||
|
||||
public BTCheckValue(string key, T expectedValue, string name = "CheckValue") : base(name)
|
||||
{
|
||||
this.key = key;
|
||||
this.expectedValue = expectedValue;
|
||||
}
|
||||
|
||||
public BTCheckValue(string key, T expectedValue, Func<T, T, bool> comparer, string name = "CheckValue") : base(name)
|
||||
{
|
||||
this.key = key;
|
||||
this.expectedValue = expectedValue;
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
var value = Context.Get<T>(key);
|
||||
|
||||
bool match;
|
||||
if (comparer != null)
|
||||
{
|
||||
match = comparer(value, expectedValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
match = value != null && value.Equals(expectedValue);
|
||||
}
|
||||
|
||||
return match ? BTStatus.Success : BTStatus.Failure;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move To node - Moves towards a target position
|
||||
/// </summary>
|
||||
public class BTMoveTo : BTNode
|
||||
{
|
||||
public float Speed { get; set; } = 5f;
|
||||
public float StoppingDistance { get; set; } = 0.5f;
|
||||
|
||||
private Vector3 targetPosition;
|
||||
private Func<BTContext, Vector3> targetFunc;
|
||||
|
||||
public BTMoveTo(Vector3 target, string name = "MoveTo") : base(name)
|
||||
{
|
||||
targetPosition = target;
|
||||
}
|
||||
|
||||
public BTMoveTo(Func<BTContext, Vector3> targetFunc, string name = "MoveTo") : base(name)
|
||||
{
|
||||
this.targetFunc = targetFunc;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (Context?.Transform == null) return BTStatus.Failure;
|
||||
|
||||
Vector3 target = targetFunc != null ? targetFunc(Context) : targetPosition;
|
||||
Vector3 currentPos = Context.Transform.position;
|
||||
|
||||
float distance = Vector3.Distance(currentPos, target);
|
||||
|
||||
if (distance <= StoppingDistance)
|
||||
{
|
||||
IsRunning = false;
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
// Move towards target
|
||||
Vector3 direction = (target - currentPos).normalized;
|
||||
Context.Transform.position += direction * Speed * Time.deltaTime;
|
||||
|
||||
// Face direction
|
||||
if (direction != Vector3.zero)
|
||||
{
|
||||
direction.y = 0;
|
||||
if (direction.sqrMagnitude > 0.001f)
|
||||
{
|
||||
Context.Transform.forward = direction;
|
||||
}
|
||||
}
|
||||
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Look At node - Rotates to face a target
|
||||
/// </summary>
|
||||
public class BTLookAt : BTNode
|
||||
{
|
||||
public float RotationSpeed { get; set; } = 5f;
|
||||
public float Tolerance { get; set; } = 5f; // degrees
|
||||
|
||||
private Transform target;
|
||||
private Func<BTContext, Transform> targetFunc;
|
||||
|
||||
public BTLookAt(Transform target, string name = "LookAt") : base(name)
|
||||
{
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public BTLookAt(Func<BTContext, Transform> targetFunc, string name = "LookAt") : base(name)
|
||||
{
|
||||
this.targetFunc = targetFunc;
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
if (Context?.Transform == null) return BTStatus.Failure;
|
||||
|
||||
Transform lookTarget = targetFunc != null ? targetFunc(Context) : target;
|
||||
if (lookTarget == null) return BTStatus.Failure;
|
||||
|
||||
Vector3 direction = lookTarget.position - Context.Transform.position;
|
||||
direction.y = 0;
|
||||
|
||||
if (direction.sqrMagnitude < 0.001f)
|
||||
{
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
Quaternion targetRotation = Quaternion.LookRotation(direction);
|
||||
float angle = Quaternion.Angle(Context.Transform.rotation, targetRotation);
|
||||
|
||||
if (angle <= Tolerance)
|
||||
{
|
||||
return BTStatus.Success;
|
||||
}
|
||||
|
||||
Context.Transform.rotation = Quaternion.Slerp(
|
||||
Context.Transform.rotation,
|
||||
targetRotation,
|
||||
RotationSpeed * Time.deltaTime
|
||||
);
|
||||
|
||||
IsRunning = true;
|
||||
return BTStatus.Running;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Random Success node - Randomly returns success or failure
|
||||
/// </summary>
|
||||
public class BTRandomSuccess : BTNode
|
||||
{
|
||||
public float SuccessChance { get; set; } = 0.5f;
|
||||
|
||||
public BTRandomSuccess(float chance = 0.5f, string name = "RandomSuccess") : base(name)
|
||||
{
|
||||
SuccessChance = Mathf.Clamp01(chance);
|
||||
}
|
||||
|
||||
public override BTStatus Tick()
|
||||
{
|
||||
return UnityEngine.Random.value < SuccessChance ? BTStatus.Success : BTStatus.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f5de2b2723dd433bac886ded1d38724
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/Runtime/BehaviorTree/BTLeaves.cs
|
||||
uploadId: 920982
|
||||
@@ -0,0 +1,252 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SynapticPro.BehaviorTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Node execution status
|
||||
/// </summary>
|
||||
public enum BTStatus
|
||||
{
|
||||
Success,
|
||||
Failure,
|
||||
Running
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all Behavior Tree nodes
|
||||
/// </summary>
|
||||
public abstract class BTNode
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public BTNode Parent { get; protected set; }
|
||||
public bool IsRunning { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Context data shared across the tree
|
||||
/// </summary>
|
||||
public BTContext Context { get; set; }
|
||||
|
||||
protected BTNode(string name = "Node")
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute this node
|
||||
/// </summary>
|
||||
public abstract BTStatus Tick();
|
||||
|
||||
/// <summary>
|
||||
/// Reset node state
|
||||
/// </summary>
|
||||
public virtual void Reset()
|
||||
{
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when node is aborted
|
||||
/// </summary>
|
||||
public virtual void Abort()
|
||||
{
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set parent node
|
||||
/// </summary>
|
||||
internal void SetParent(BTNode parent)
|
||||
{
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for nodes with children
|
||||
/// </summary>
|
||||
public abstract class BTComposite : BTNode
|
||||
{
|
||||
protected List<BTNode> children = new List<BTNode>();
|
||||
protected int currentChildIndex = 0;
|
||||
|
||||
public IReadOnlyList<BTNode> Children => children;
|
||||
|
||||
protected BTComposite(string name = "Composite") : base(name) { }
|
||||
|
||||
/// <summary>
|
||||
/// Add a child node
|
||||
/// </summary>
|
||||
public BTComposite AddChild(BTNode child)
|
||||
{
|
||||
if (child != null)
|
||||
{
|
||||
child.SetParent(this);
|
||||
child.Context = Context;
|
||||
children.Add(child);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add multiple children
|
||||
/// </summary>
|
||||
public BTComposite AddChildren(params BTNode[] nodes)
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
AddChild(node);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a child node
|
||||
/// </summary>
|
||||
public void RemoveChild(BTNode child)
|
||||
{
|
||||
if (child != null)
|
||||
{
|
||||
child.SetParent(null);
|
||||
children.Remove(child);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all children
|
||||
/// </summary>
|
||||
public void ClearChildren()
|
||||
{
|
||||
foreach (var child in children)
|
||||
{
|
||||
child.SetParent(null);
|
||||
}
|
||||
children.Clear();
|
||||
currentChildIndex = 0;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
currentChildIndex = 0;
|
||||
foreach (var child in children)
|
||||
{
|
||||
child.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Abort()
|
||||
{
|
||||
base.Abort();
|
||||
foreach (var child in children)
|
||||
{
|
||||
child.Abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for decorator nodes (single child)
|
||||
/// </summary>
|
||||
public abstract class BTDecorator : BTNode
|
||||
{
|
||||
protected BTNode child;
|
||||
|
||||
public BTNode Child => child;
|
||||
|
||||
protected BTDecorator(string name = "Decorator") : base(name) { }
|
||||
|
||||
/// <summary>
|
||||
/// Set the child node
|
||||
/// </summary>
|
||||
public BTDecorator SetChild(BTNode node)
|
||||
{
|
||||
if (child != null)
|
||||
{
|
||||
child.SetParent(null);
|
||||
}
|
||||
child = node;
|
||||
if (child != null)
|
||||
{
|
||||
child.SetParent(this);
|
||||
child.Context = Context;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
child?.Reset();
|
||||
}
|
||||
|
||||
public override void Abort()
|
||||
{
|
||||
base.Abort();
|
||||
child?.Abort();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shared context data for the behavior tree
|
||||
/// </summary>
|
||||
public class BTContext
|
||||
{
|
||||
private Dictionary<string, object> data = new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// The GameObject this tree is attached to
|
||||
/// </summary>
|
||||
public GameObject GameObject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Transform of the GameObject
|
||||
/// </summary>
|
||||
public Transform Transform => GameObject?.transform;
|
||||
|
||||
/// <summary>
|
||||
/// Set a value in the context
|
||||
/// </summary>
|
||||
public void Set<T>(string key, T value)
|
||||
{
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a value from the context
|
||||
/// </summary>
|
||||
public T Get<T>(string key, T defaultValue = default)
|
||||
{
|
||||
if (data.TryGetValue(key, out var value) && value is T typedValue)
|
||||
{
|
||||
return typedValue;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a key exists
|
||||
/// </summary>
|
||||
public bool Has(string key)
|
||||
{
|
||||
return data.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a value
|
||||
/// </summary>
|
||||
public void Remove(string key)
|
||||
{
|
||||
data.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all data
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
data.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf24ecfaec86e41f3a4b25d6f3979fa4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/Runtime/BehaviorTree/BTNode.cs
|
||||
uploadId: 920982
|
||||
@@ -0,0 +1,443 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace SynapticPro.BehaviorTree
|
||||
{
|
||||
/// <summary>
|
||||
/// MonoBehaviour that runs a Behavior Tree
|
||||
/// </summary>
|
||||
public class BehaviorTreeRunner : MonoBehaviour
|
||||
{
|
||||
[Header("Execution Settings")]
|
||||
[SerializeField] private bool autoStart = true;
|
||||
[SerializeField] private float tickInterval = 0f; // 0 = every frame
|
||||
[SerializeField] private bool pauseWhenDisabled = true;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool debugMode = false;
|
||||
[SerializeField] private string currentNodeName = "";
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent OnTreeStarted;
|
||||
public UnityEvent OnTreeCompleted;
|
||||
public UnityEvent OnTreeSucceeded;
|
||||
public UnityEvent OnTreeFailed;
|
||||
|
||||
// Runtime
|
||||
private BTNode rootNode;
|
||||
private BTContext context;
|
||||
private float lastTickTime;
|
||||
private bool isRunning;
|
||||
private BTStatus lastStatus = BTStatus.Running;
|
||||
|
||||
/// <summary>
|
||||
/// The root node of the tree
|
||||
/// </summary>
|
||||
public BTNode RootNode => rootNode;
|
||||
|
||||
/// <summary>
|
||||
/// The shared context
|
||||
/// </summary>
|
||||
public BTContext Context => context;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the tree is currently running
|
||||
/// </summary>
|
||||
public bool IsRunning => isRunning;
|
||||
|
||||
/// <summary>
|
||||
/// Last tick status
|
||||
/// </summary>
|
||||
public BTStatus LastStatus => lastStatus;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
context = new BTContext { GameObject = gameObject };
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (autoStart && rootNode != null)
|
||||
{
|
||||
StartTree();
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!isRunning || rootNode == null) return;
|
||||
|
||||
// Check tick interval
|
||||
if (tickInterval > 0 && Time.time - lastTickTime < tickInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastTickTime = Time.time;
|
||||
|
||||
// Tick the tree
|
||||
lastStatus = rootNode.Tick();
|
||||
|
||||
if (debugMode && rootNode != null)
|
||||
{
|
||||
currentNodeName = GetCurrentNodeName(rootNode);
|
||||
}
|
||||
|
||||
// Handle completion
|
||||
if (lastStatus != BTStatus.Running)
|
||||
{
|
||||
OnTreeCompleted?.Invoke();
|
||||
|
||||
if (lastStatus == BTStatus.Success)
|
||||
{
|
||||
OnTreeSucceeded?.Invoke();
|
||||
if (debugMode) Debug.Log($"[BT] Tree completed with Success");
|
||||
}
|
||||
else
|
||||
{
|
||||
OnTreeFailed?.Invoke();
|
||||
if (debugMode) Debug.Log($"[BT] Tree completed with Failure");
|
||||
}
|
||||
|
||||
// Reset for next run
|
||||
rootNode.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (pauseWhenDisabled && rootNode != null)
|
||||
{
|
||||
// Resume
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (pauseWhenDisabled && rootNode != null)
|
||||
{
|
||||
// Pause (just stop ticking, state preserved)
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the root node of the tree
|
||||
/// </summary>
|
||||
public void SetTree(BTNode root)
|
||||
{
|
||||
rootNode = root;
|
||||
if (rootNode != null)
|
||||
{
|
||||
PropagateContext(rootNode, context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start running the tree
|
||||
/// </summary>
|
||||
public void StartTree()
|
||||
{
|
||||
if (rootNode == null)
|
||||
{
|
||||
Debug.LogWarning("[BT] Cannot start tree: root node is null");
|
||||
return;
|
||||
}
|
||||
|
||||
isRunning = true;
|
||||
lastTickTime = Time.time;
|
||||
lastStatus = BTStatus.Running;
|
||||
OnTreeStarted?.Invoke();
|
||||
|
||||
if (debugMode) Debug.Log("[BT] Tree started");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the tree
|
||||
/// </summary>
|
||||
public void StopTree()
|
||||
{
|
||||
if (!isRunning) return;
|
||||
|
||||
isRunning = false;
|
||||
rootNode?.Abort();
|
||||
rootNode?.Reset();
|
||||
|
||||
if (debugMode) Debug.Log("[BT] Tree stopped");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pause the tree
|
||||
/// </summary>
|
||||
public void PauseTree()
|
||||
{
|
||||
isRunning = false;
|
||||
if (debugMode) Debug.Log("[BT] Tree paused");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume the tree
|
||||
/// </summary>
|
||||
public void ResumeTree()
|
||||
{
|
||||
isRunning = true;
|
||||
if (debugMode) Debug.Log("[BT] Tree resumed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset and restart the tree
|
||||
/// </summary>
|
||||
public void RestartTree()
|
||||
{
|
||||
StopTree();
|
||||
StartTree();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a value in the context
|
||||
/// </summary>
|
||||
public void SetContextValue<T>(string key, T value)
|
||||
{
|
||||
context?.Set(key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a value from the context
|
||||
/// </summary>
|
||||
public T GetContextValue<T>(string key, T defaultValue = default)
|
||||
{
|
||||
return context != null ? context.Get(key, defaultValue) : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagate context to all nodes
|
||||
/// </summary>
|
||||
private void PropagateContext(BTNode node, BTContext ctx)
|
||||
{
|
||||
if (node == null) return;
|
||||
|
||||
node.Context = ctx;
|
||||
|
||||
if (node is BTComposite composite)
|
||||
{
|
||||
foreach (var child in composite.Children)
|
||||
{
|
||||
PropagateContext(child, ctx);
|
||||
}
|
||||
}
|
||||
else if (node is BTDecorator decorator)
|
||||
{
|
||||
PropagateContext(decorator.Child, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of currently executing node
|
||||
/// </summary>
|
||||
private string GetCurrentNodeName(BTNode node)
|
||||
{
|
||||
if (node == null) return "";
|
||||
|
||||
if (node.IsRunning)
|
||||
{
|
||||
if (node is BTComposite composite)
|
||||
{
|
||||
foreach (var child in composite.Children)
|
||||
{
|
||||
string childName = GetCurrentNodeName(child);
|
||||
if (!string.IsNullOrEmpty(childName))
|
||||
{
|
||||
return childName;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node is BTDecorator decorator)
|
||||
{
|
||||
string childName = GetCurrentNodeName(decorator.Child);
|
||||
if (!string.IsNullOrEmpty(childName))
|
||||
{
|
||||
return childName;
|
||||
}
|
||||
}
|
||||
|
||||
return node.Name;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!debugMode || rootNode == null) return;
|
||||
|
||||
// Draw current node info
|
||||
Vector3 pos = transform.position + Vector3.up * 2f;
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.Handles.Label(pos, $"BT: {currentNodeName}\nStatus: {lastStatus}");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builder for creating Behavior Trees fluently
|
||||
/// </summary>
|
||||
public class BehaviorTreeBuilder
|
||||
{
|
||||
private BTNode root;
|
||||
private BTNode current;
|
||||
|
||||
/// <summary>
|
||||
/// Start with a Selector
|
||||
/// </summary>
|
||||
public BehaviorTreeBuilder Selector(string name = "Selector")
|
||||
{
|
||||
var node = new BTSelector(name);
|
||||
SetNode(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start with a Sequence
|
||||
/// </summary>
|
||||
public BehaviorTreeBuilder Sequence(string name = "Sequence")
|
||||
{
|
||||
var node = new BTSequence(name);
|
||||
SetNode(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start with a Parallel
|
||||
/// </summary>
|
||||
public BehaviorTreeBuilder Parallel(string name = "Parallel")
|
||||
{
|
||||
var node = new BTParallel(name);
|
||||
SetNode(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an Action
|
||||
/// </summary>
|
||||
public BehaviorTreeBuilder Action(Func<BTContext, BTStatus> action, string name = "Action")
|
||||
{
|
||||
var node = new BTAction(action, name);
|
||||
AddToCurrentComposite(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a simple Action (auto-succeeds)
|
||||
/// </summary>
|
||||
public BehaviorTreeBuilder Do(Action<BTContext> action, string name = "Action")
|
||||
{
|
||||
var node = new BTAction(action, name);
|
||||
AddToCurrentComposite(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a Condition
|
||||
/// </summary>
|
||||
public BehaviorTreeBuilder Condition(Func<BTContext, bool> condition, string name = "Condition")
|
||||
{
|
||||
var node = new BTCondition(condition, name);
|
||||
AddToCurrentComposite(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a Wait
|
||||
/// </summary>
|
||||
public BehaviorTreeBuilder Wait(float duration, string name = "Wait")
|
||||
{
|
||||
var node = new BTWait(duration, name);
|
||||
AddToCurrentComposite(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an Inverter decorator
|
||||
/// </summary>
|
||||
public BehaviorTreeBuilder Invert()
|
||||
{
|
||||
// This should wrap the next node added
|
||||
// For simplicity, we'll handle this differently
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the tree
|
||||
/// </summary>
|
||||
public BTNode Build()
|
||||
{
|
||||
return root;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build and assign to runner
|
||||
/// </summary>
|
||||
public void BuildAndRun(BehaviorTreeRunner runner)
|
||||
{
|
||||
runner.SetTree(Build());
|
||||
runner.StartTree();
|
||||
}
|
||||
|
||||
private void SetNode(BTNode node)
|
||||
{
|
||||
if (root == null)
|
||||
{
|
||||
root = node;
|
||||
}
|
||||
else if (current is BTComposite composite)
|
||||
{
|
||||
composite.AddChild(node);
|
||||
}
|
||||
|
||||
current = node;
|
||||
}
|
||||
|
||||
private void AddToCurrentComposite(BTNode node)
|
||||
{
|
||||
if (current is BTComposite composite)
|
||||
{
|
||||
composite.AddChild(node);
|
||||
}
|
||||
else if (root == null)
|
||||
{
|
||||
root = node;
|
||||
current = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static helper for building trees
|
||||
/// </summary>
|
||||
public static class BT
|
||||
{
|
||||
public static BTSelector Selector(string name = "Selector") => new BTSelector(name);
|
||||
public static BTSequence Sequence(string name = "Sequence") => new BTSequence(name);
|
||||
public static BTParallel Parallel(string name = "Parallel") => new BTParallel(name);
|
||||
public static BTRandomSelector RandomSelector(string name = "RandomSelector") => new BTRandomSelector(name);
|
||||
public static BTRandomSequence RandomSequence(string name = "RandomSequence") => new BTRandomSequence(name);
|
||||
|
||||
public static BTInverter Inverter(string name = "Inverter") => new BTInverter(name);
|
||||
public static BTSucceeder Succeeder(string name = "Succeeder") => new BTSucceeder(name);
|
||||
public static BTFailer Failer(string name = "Failer") => new BTFailer(name);
|
||||
public static BTRepeater Repeater(int count = -1, string name = "Repeater") => new BTRepeater(count, name);
|
||||
public static BTCooldown Cooldown(float time, string name = "Cooldown") => new BTCooldown(time, name);
|
||||
public static BTTimeout Timeout(float time, string name = "Timeout") => new BTTimeout(time, name);
|
||||
public static BTRetry Retry(int count = 3, string name = "Retry") => new BTRetry(count, name);
|
||||
public static BTDelay Delay(float time, string name = "Delay") => new BTDelay(time, name);
|
||||
|
||||
public static BTAction Action(Func<BTContext, BTStatus> action, string name = "Action") => new BTAction(action, name);
|
||||
public static BTAction Action(Action<BTContext> action, string name = "Action") => new BTAction(action, name);
|
||||
public static BTCondition Condition(Func<BTContext, bool> condition, string name = "Condition") => new BTCondition(condition, name);
|
||||
public static BTWait Wait(float duration, string name = "Wait") => new BTWait(duration, name);
|
||||
public static BTLog Log(string message, string name = "Log") => new BTLog(message, name);
|
||||
public static BTMoveTo MoveTo(Vector3 target, string name = "MoveTo") => new BTMoveTo(target, name);
|
||||
public static BTMoveTo MoveTo(Func<BTContext, Vector3> targetFunc, string name = "MoveTo") => new BTMoveTo(targetFunc, name);
|
||||
public static BTRandomSuccess RandomSuccess(float chance = 0.5f, string name = "RandomSuccess") => new BTRandomSuccess(chance, name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb8445612d6634f2dbe123206811c1b6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/Runtime/BehaviorTree/BehaviorTreeRunner.cs
|
||||
uploadId: 920982
|
||||
Reference in New Issue
Block a user