This commit is contained in:
2026-05-27 16:48:22 +08:00
parent 0082c0b727
commit e81e4547e7
535 changed files with 35957 additions and 10 deletions

View File

@@ -0,0 +1,120 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Components
{
using Opsive.BehaviorDesigner.Runtime.Groups;
using Opsive.BehaviorDesigner.Runtime.Systems;
using Unity.Entities;
using UnityEngine;
/// <summary>
/// Indicates that the behavior tree was baked.
/// </summary>
public class BakedBehaviorTree : IComponentData
{
[Tooltip("The index of the connected start task.")]
public int StartEventConnectedIndex;
[Tooltip("Should the behavior tree be started after it has been baked?")]
public bool StartEvaluation;
[Tooltip("The indicies of the reevaluate task systems.")]
public string[] ReevaluateTaskSystems;
[Tooltip("The indicies of the interrupt task systems.")]
public string[] InterruptTaskSystems;
[Tooltip("The indicies of the traversal task systems.")]
public string[] TraversalTaskSystems;
[Tooltip("The hashes that correspond to the TaskComponent's ComponentType.")]
public ulong[] TagStableTypeHashes;
[Tooltip("The hashes that correspond to the ReevaluateTaskComponent's ComponentType.")]
public ulong[] ReevaluateFlagStableTypeHashes;
}
/// <summary>
/// The behavior tree has been baked. Start the tree using the baked data.
/// </summary>
public partial struct StartBakedBehaviorTreeSystem : ISystem
{
/// <summary>
/// Restricts when the system should run.
/// </summary>
/// <param name="state">The current SystemState.</param>
private void OnCreate(ref SystemState state)
{
state.RequireForUpdate<BakedBehaviorTree>();
state.Enabled = false;
}
/// <summary>
/// Starts the baked behavior tree.
/// </summary>
/// <param name="state">The current SystemState.</param>
private void OnUpdate(ref SystemState state)
{
state.Enabled = false;
// The components are baked, but systems are not baked. Create the required systems within the current world.
var reevaluateTaskSystemGroup = state.World.GetOrCreateSystemManaged<ReevaluateTaskSystemGroup>();
var interruptTaskSystemGroup = state.World.GetOrCreateSystemManaged<InterruptTaskSystemGroup>();
var traversalTaskSystemGroup = state.World.GetOrCreateSystemManaged<TraversalTaskSystemGroup>();
// Add the necessary cleanup systems.
var behaviorTreeSystemGroup = state.World.GetOrCreateSystemManaged<BehaviorTreeSystemGroup>();
behaviorTreeSystemGroup.AddSystemToUpdateList(state.World.GetOrCreateSystem<EvaluationCleanupSystem>());
behaviorTreeSystemGroup.AddSystemToUpdateList(state.World.GetOrCreateSystem<InterruptedCleanupSystem>());
var canReevaluate = false;
var ecb = new EntityCommandBuffer(state.WorldUpdateAllocator);
foreach (var (bakedBehaviorTree, entity) in SystemAPI.Query<BakedBehaviorTree>().WithEntityAccess()) {
AddSystems(state.World, reevaluateTaskSystemGroup, bakedBehaviorTree.ReevaluateTaskSystems);
AddSystems(state.World, interruptTaskSystemGroup, bakedBehaviorTree.InterruptTaskSystems);
AddSystems(state.World, traversalTaskSystemGroup, bakedBehaviorTree.TraversalTaskSystems);
// ComponentTypes cannot be serialized. Convert the StableTypeHash to a ComponentType.
var taskComponents = state.World.EntityManager.GetBuffer<TaskComponent>(entity);
for (int i = 0; i < taskComponents.Length; ++i) {
var taskComponent = taskComponents[i];
taskComponent.FlagComponentType = ComponentType.FromTypeIndex(TypeManager.GetTypeIndexFromStableTypeHash(bakedBehaviorTree.TagStableTypeHashes[i]));
taskComponents[i] = taskComponent;
}
if (state.World.EntityManager.HasBuffer<ReevaluateTaskComponent>(entity)) {
var reevaluateComponents = state.World.EntityManager.GetBuffer<ReevaluateTaskComponent>(entity);
canReevaluate = true;
for (int i = 0; i < reevaluateComponents.Length; ++i) {
var reevaluateComponent = reevaluateComponents[i];
reevaluateComponent.ReevaluateFlagComponentType = ComponentType.FromTypeIndex(TypeManager.GetTypeIndexFromStableTypeHash(bakedBehaviorTree.ReevaluateFlagStableTypeHashes[i]));
reevaluateComponents[i] = reevaluateComponent;
}
}
// All of the systems have been added. Start the behavior tree.
BehaviorTree.StartBranch(state.World, entity, (ushort)bakedBehaviorTree.StartEventConnectedIndex, bakedBehaviorTree.StartEvaluation);
ecb.RemoveComponent<BakedBehaviorTree>(entity);
}
if (canReevaluate) {
reevaluateTaskSystemGroup.AddSystemToUpdateList(state.World.GetOrCreateSystem<ReevaluateSystem>());
}
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
/// <summary>
/// Adds the systems indicated by the SystemTypeIndex to the specified group.
/// </summary>
/// <param name="world">The current World.</param>
/// <param name="group">The group that the systems should be added to.</param>
/// <param name="systemTypes">The types of systems that should be added.</param>
private void AddSystems(World world, ComponentSystemGroup group, string[] systemTypes)
{
if (systemTypes == null) { return; }
for (int i = 0; i < systemTypes.Length; ++i) {
group.AddSystemToUpdateList(world.GetOrCreateSystem(Shared.Utility.TypeUtility.GetType(systemTypes[i])));
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f68d4eca856988e4a8f4073516e7de57
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,261 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Components
{
using Opsive.BehaviorDesigner.Runtime.Tasks;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
/// <summary>
/// The runtime DOTS data associated with a task.
/// </summary>
[System.Serializable]
public struct TaskComponent : IBufferElementData
{
[Tooltip("The index of the task within the behavior tree.")]
public ushort Index;
[Tooltip("The index of the parent task within the behavior tree.")]
public ushort ParentIndex;
//public ushort m_ParentIndex;
//public ushort ParentIndex { get { return m_ParentIndex; } set { UnityEngine.Debug.Log(Index + " parent: " + value); m_ParentIndex = value; } }
[Tooltip("The index of the sibling task within the behavior tree.")]
public ushort SiblingIndex;
//public ushort m_SiblingIndex;
//public ushort SiblingIndex { get { return m_SiblingIndex; } set { UnityEngine.Debug.Log(Index + " sibling: " + value); m_SiblingIndex = value; } }
[Tooltip("The index of the branch the task executes within.")]
public ushort BranchIndex;
//public ushort m_BranchIndex;
//public ushort BranchIndex { get { return m_BranchIndex; } set { UnityEngine.Debug.Log(Index + " branch: " + value); m_BranchIndex = value; } }
[Tooltip("The component type responsible for indicating that the task is active.")]
public ComponentType FlagComponentType;
[Tooltip("Is the task disabled?")]
[MarshalAs(UnmanagedType.U1)]
public bool Disabled;
[Tooltip("The current execution status of the task.")]
//public TaskStatus Status;
[SerializeField] private TaskStatus m_Status;
public TaskStatus Status {
get => m_Status;
set {
m_Status = value;
CanReevaluate = value != TaskStatus.Inactive;
//UnityEngine.Debug.Log(string.Format("{0} status: {1}", Index, value));
}
}
[Tooltip("Can the task be reevaluated with conditional aborts?")]
[MarshalAs(UnmanagedType.U1)]
public bool CanReevaluate;
[Tooltip("Is the task being reevaluated with conditional aborts?")]
[MarshalAs(UnmanagedType.U1)]
public bool Reevaluate;
//public bool m_Reevaluate;
//public bool Reevaluate { get { return m_Reevaluate; } set { UnityEngine.Debug.Log(Index + " reevaluate: " + value + " " + GetHashCode()); m_Reevaluate = value; } }
}
/// <summary>
/// Specifies when the behavior tree should be updated.
/// </summary>
public enum UpdateMode
{
EveryFrame, // The behavior tree should be updated every frame.
Manual // The behavior tree should be updated manually via a user script.
}
/// <summary>
/// Specifies how many tasks should be evaluated. Evaluation will end if all branches return a status of TaskStatus.Running.
/// </summary>
public enum EvaluationType : byte
{
EntireTree, // Evaluates up to all of the tasks within the tree.
Count // Evaluates up to the specified MaxEvaluationCount.
}
/// <summary>
/// Specifies if the tree should be evaluated.
/// </summary>
public struct EvaluationComponent32 : IComponentData
{
[Tooltip("Specifies how many tasks should be updated during a single tick.")]
public EvaluationType EvaluationType;
[Tooltip("The maximum number of tasks that can run if the evaluation type is set to EvaluationType.Count.")]
public ushort MaxEvaluationCount;
[Tooltip("A bitmask of the tasks that have been evaluated. For EvaluationType.Count, the last element stores the execution count.")]
public FixedList32Bytes<ulong> EvaluatedTasks;
}
/// <summary>
/// Specifies if the tree should be evaluated.
/// </summary>
public struct EvaluationComponent64 : IComponentData
{
[Tooltip("Specifies how many tasks should be updated during a single tick.")]
public EvaluationType EvaluationType;
[Tooltip("The maximum number of tasks that can run if the evaluation type is set to EvaluationType.Count.")]
public ushort MaxEvaluationCount;
[Tooltip("A bitmask of the tasks that have been evaluated. For EvaluationType.Count, the last element stores the execution count.")]
public FixedList64Bytes<ulong> EvaluatedTasks;
}
/// <summary>
/// Specifies if the tree should be evaluated.
/// </summary>
public struct EvaluationComponent128 : IComponentData
{
[Tooltip("Specifies how many tasks should be updated during a single tick.")]
public EvaluationType EvaluationType;
[Tooltip("The maximum number of tasks that can run if the evaluation type is set to EvaluationType.Count.")]
public ushort MaxEvaluationCount;
[Tooltip("A bitmask of the tasks that have been evaluated. For EvaluationType.Count, the last element stores the execution count.")]
public FixedList128Bytes<ulong> EvaluatedTasks;
}
/// <summary>
/// Specifies if the tree should be evaluated.
/// </summary>
public struct EvaluationComponent512 : IComponentData
{
[Tooltip("Specifies how many tasks should be updated during a single tick.")]
public EvaluationType EvaluationType;
[Tooltip("The maximum number of tasks that can run if the evaluation type is set to EvaluationType.Count.")]
public ushort MaxEvaluationCount;
[Tooltip("A bitmask of the tasks that have been evaluated. For EvaluationType.Count, the last element stores the execution count.")]
public FixedList512Bytes<ulong> EvaluatedTasks;
}
/// <summary>
/// Specifies if the tree should be evaluated.
/// </summary>
public struct EvaluationComponent4096 : IComponentData
{
[Tooltip("Specifies how many tasks should be updated during a single tick.")]
public EvaluationType EvaluationType;
[Tooltip("The maximum number of tasks that can run if the evaluation type is set to EvaluationType.Count.")]
public ushort MaxEvaluationCount;
[Tooltip("A bitmask of the tasks that have been evaluated. For EvaluationType.Count, the last element stores the execution count.")]
public FixedList4096Bytes<ulong> EvaluatedTasks;
}
/// <summary>
/// Specifies how the branch was interrupted.
/// </summary>
public enum InterruptType : byte
{
None, // No interrupt.
Branch, // A conditional abort or utility selector triggered the interruption.
ImmediateSuccess, // The branch was interrupted with a success status.
ImmediateFailure, // The branch was interrupted with a failure status.
}
/// <summary>
/// The runtime DOTS data associated with a branch.
/// </summary>
public struct BranchComponent : IBufferElementData
{
[Tooltip("The index of the task that is currently active.")]
public ushort ActiveIndex;
//public ushort m_ActiveIndex;
//public ushort ActiveIndex { get { return m_ActiveIndex; } set { Debug.Log(string.Format("Active: {0}", value)); m_ActiveIndex = value; } }
[Tooltip("The index of the task that should execute next.")]
public ushort NextIndex;
//public ushort m_NextIndex;
//public ushort NextIndex { get { return m_NextIndex; } set { Debug.Log(string.Format("Next: {0}", value)); m_NextIndex = value; } }
[Tooltip("The index of the last active task.")]
public ushort LastActiveIndex;
[Tooltip("The component tag that is active.")]
public ComponentType ActiveFlagComponentType;
//public ComponentType m_ActiveFlagComponentType;
//public ComponentType ActiveFlagComponentType { get { return m_ActiveFlagComponentType; } set { Debug.Log(string.Format("Tag: {0}", value)); m_ActiveFlagComponentType = value; } }
[Tooltip("Specifies how the branch is interrupted.")]
public InterruptType InterruptType;
//public InterruptType m_InterruptType;
//public InterruptType InterruptType { get { return m_InterruptType; } set { m_InterruptType = value; Debug.Log("Interrupt Type " + value); } }
[Tooltip("The index of the task that caused an interruption. A value of 0 indicates no interruption.")]
public ushort InterruptIndex;
[Tooltip("Specifies if the branch can execute tasks. Set to false when all tasks in the branch have executed this tick.")]
public bool CanExecute;
//public bool m_CanExecute;
//public bool CanExecute { get { return m_CanExecute; } set { m_CanExecute = value; Debug.Log("Can Execute " + value); } }
}
/// <summary>
/// Specifies if the tree can be evaluated.
/// </summary>
public struct EvaluateFlag : IComponentData, IEnableableComponent { }
/// <summary>
/// Specifies if the tree is enabled.
/// </summary>
public struct EnabledFlag : IComponentData, IEnableableComponent { }
/// <summary>
/// Flag used to indicate when the branch should be interrupted.
/// </summary>
public struct InterruptFlag : IComponentData, IEnableableComponent { }
/// <summary>
/// Flag used to indicate that the branch has been interrupted.
/// </summary>
public struct InterruptedFlag : IComponentData, IEnableableComponent { }
/// <summary>
/// Specifies the reevaluation status of the task.
/// </summary>
public enum ReevaluateStatus : byte
{
Inactive, // The task is not being reevaluated.
Active, // The task is currently being reevaluated.
Dirty // The task was reevaluated and triggered a change.
}
/// <summary>
/// The runtime DOTS data associated with conditional aborts.
/// </summary>
public struct ReevaluateTaskComponent : IBufferElementData
{
[Tooltip("The index of the task.")]
public ushort Index;
[Tooltip("The type of conditional abort.")]
public ConditionalAbortType AbortType;
[Tooltip("The lower bound index of the next task if a lower priority abort is specified.")]
public ushort LowerPriorityLowerIndex;
[Tooltip("The upper bound index of the next task if a lower priority abort is specified.")]
public ushort LowerPriorityUpperIndex;
[Tooltip("The upper bound index of the next task if a self priority abort is specified.")]
public ushort SelfPriorityUpperIndex;
[Tooltip("The original status of the task.")]
public TaskStatus OriginalStatus;
[Tooltip("The tag specifiying the task should be reevaluated.")]
public ComponentType ReevaluateFlagComponentType;
[Tooltip("The current reevaluation status of the task.")]
public ReevaluateStatus ReevaluateStatus;
}
/// <summary>
/// Runtime representation of IEventNodes that can run its own entity logic.
/// </summary>
public interface IEventNodeEntityReceiver
{
/// <summary>
/// Adds the IBufferElementData to the entity.
/// </summary>
/// <param name="world">The world that the entity exists in.</param>
/// <param name="entity">The entity that the IBufferElementData should be assigned to.</param>
/// <param name="gameObject">The GameObject that the entity is attached to.</param>
/// <param name="taskOffset">The offset between the connected index and the runtime index.</param>
void AddBufferElement(World world, Entity entity, GameObject gameObject, ushort taskOffset);
/// <summary>
/// Clears the IBufferElementData from the entity.
/// </summary>
/// <param name="world">The world that the entity exists in.</param>
/// <param name="entity">The entity that the IBufferElementData should be cleared from.</param>
void ClearBufferElement(World world, Entity entity);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12b6df69a20daeb4ab4ce1ccaa7bc7ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: