chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0cdaa3305fa954c45a80c9662aa6f425
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e0a8f1df788b6274a9a24003859dfa7e, type: 3}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,117 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime
{
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using Opsive.Shared.Utility;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
/// <summary>
/// Storage class for the graph data.
/// </summary>
public partial class BehaviorTreeData
{
/// <summary>
/// Data structure which contains the properties for a subtree that will be injected.
/// </summary>
private struct SubtreeAssignment
{
[Tooltip("The index of the SubtreeNodesReference element.")]
public int ReferenceIndex;
[Tooltip("The index of the ISubtreeReference task.")]
public ushort NodeIndex;
[Tooltip("The index of the Subtree.")]
public int SubtreeIndex;
[Tooltip("The subtree that the task references.")]
public Subtree Subtree;
[Tooltip("The offset of the index. This will change as subtrees are added.")]
public ushort IndexOffset;
[Tooltip("The original parent index of the ISubtreeReference task.")]
public ushort ParentIndex;
[Tooltip("The original sibling index of the ISubtreeReference task.")]
public ushort SiblingIndex;
[Tooltip("The number of nodes that are a child of the ISubtreeReference.")]
public ushort NodeCount;
#if UNITY_EDITOR
[Tooltip("The position of the ISubtreeReference task.")]
public Vector2 NodePropertiesPosition;
[Tooltip("Is the ISubtreeReference task collapsed?")]
public bool Collapsed;
#endif
}
/// <summary>
/// Contains a reference to the subtree index and nodes.
/// </summary>
internal struct SubtreeNodesReference
{
[Tooltip("The ISubtreeReference.")]
public ISubtreeReference SubtreeReference;
[Tooltip("The index of the ISubtreeReference.")]
public ushort NodeIndex;
[Tooltip("The total number of nodes contained within the ISubtreeReference.")]
public ushort NodeCount;
[Tooltip("A reference to the subtrees that are loaded.")]
public Subtree[] Subtrees;
[Tooltip("The deserialized nodes.")]
public ITreeLogicNode[][] Nodes;
}
/// <summary>
/// Keeps a reference to the graph variables allowing them to be overwritten if a subtree is set.
/// </summary>
private struct VariableField
{
[Tooltip("The field that the SharedVariable is assigned to.")]
public FieldInfo Field;
[Tooltip("The task that the SharedVariable is assigned to.")]
public object Task;
[Tooltip("The name of the SharedVariable.")]
public string Name;
}
/// <summary>
/// Internal data structure for referencing a SharedVariable to its name/scope.
/// </summary>
public struct VariableAssignment
{
[Tooltip("The name of the SharedVariable.")]
public PropertyName Name;
[Tooltip("The scope of the SharedVariable.")]
public SharedVariable.SharingScope Scope;
/// <summary>
/// VariableAssignment constructor.
/// </summary>
/// <param name="name">The name of the SharedVariable.</param>
/// <param name="scope">The scope of the SharedVariable.</param>
public VariableAssignment(PropertyName name, SharedVariable.SharingScope scope)
{
Name = name;
Scope = scope;
}
}
/// <summary>
/// Internal data structure for restoring a task reference after it has been deserialized.
/// </summary>
public struct TaskAssignment
{
[Tooltip("The field of the task.")]
public FieldInfo Field;
[Tooltip("The task that the field belongs to.")]
public object Target;
[Tooltip("The value of the field. This will be the task object that should be assigned after the tree has been loaded.")]
public object Value;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3f13872ff04404c4da862a21fe89cac6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

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:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d9c918e9af659ea4fb420fd6af1433b7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Groups
{
using Opsive.BehaviorDesigner.Runtime.Systems;
using Unity.Entities;
/// <summary>
/// Grouping for the systems that should before other systems.
/// </summary>
[UpdateInGroup(typeof(BehaviorTreeSystemGroup), OrderFirst = true)]
public partial class BeforeTraversalSystemGroup : ComponentSystemGroup
{
}
/// <summary>
/// Grouping for the task systems that should reevaluate.
/// </summary>
[UpdateInGroup(typeof(BeforeTraversalSystemGroup))]
public partial class ReevaluateTaskSystemGroup : ComponentSystemGroup
{
}
/// <summary>
/// Grouping for the systems that run before the tree execution.
/// </summary>
[UpdateInGroup(typeof(BehaviorTreeSystemGroup))]
public partial class InterruptSystemGroup : ComponentSystemGroup
{
}
/// <summary>
/// Grouping for the task systems that can cause interrupts.
/// </summary>
[UpdateInGroup(typeof(InterruptSystemGroup))]
[UpdateAfter(typeof(InterruptSystem))]
[UpdateBefore(typeof(InterruptCleanupSystem))]
public partial class InterruptTaskSystemGroup : ComponentSystemGroup
{
}
}
#endif

View File

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

View File

@@ -0,0 +1,80 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Groups
{
using Opsive.BehaviorDesigner.Runtime.Systems;
using Unity.Entities;
using UnityEngine;
/// <summary>
/// System group which runs all of the behavior tree systems.
/// </summary>
[UpdateInGroup(typeof(SimulationSystemGroup))]
[UpdateAfter(typeof(BeginSimulationEntityCommandBufferSystem))]
public partial class BehaviorTreeSystemGroup : ComponentSystemGroup
{
private bool m_Alive;
public bool Alive { get => m_Alive; }
/// <summary>
/// Disable the group if there are no behavior trees.
/// </summary>
[RuntimeInitializeOnLoadMethod]
private static void Init()
{
if (BehaviorTree.BehaviorTreeCount > 0) {
return;
}
var worlds = World.All;
for (int i = 0; i < worlds.Count; ++i) {
var systemGroup = worlds[i].GetExistingSystemManaged<BehaviorTreeSystemGroup>();
if (systemGroup == null) {
continue;
}
systemGroup.Enabled = false;
}
}
/// <summary>
/// The group has been created.
/// </summary>
protected override void OnCreate()
{
base.OnCreate();
m_Alive = true;
}
/// <summary>
/// Update the system group.
/// </summary>
protected override void OnUpdate()
{
base.OnUpdate();
// Stop running if all trees have completed.
var systemHandle = World.GetExistingSystem<DetermineEvaluationSystem>();
var system = EntityManager.WorldUnmanaged.GetUnsafeSystemRef<DetermineEvaluationSystem>(systemHandle);
if (!system.Active) {
Enabled = false;
}
}
/// <summary>
/// The group has been destroyed.
/// </summary>
protected override void OnDestroy()
{
base.OnDestroy();
m_Alive = false;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,41 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Groups
{
using System;
using Unity.Entities;
using UnityEngine;
/// <summary>
/// Group that executes all of the tasks.
/// </summary>
[UpdateInGroup(typeof(TraversalSystemGroup))]
public partial class TraversalTaskSystemGroup : ComponentSystemGroup
{
[Tooltip("Callback before the outher tasks are updated.")]
public Action OnPreUpdate;
[Tooltip("Callback after the outher tasks are updated.")]
public Action OnPostUpdate;
/// <summary>
/// Updates the group.
/// </summary>
protected override void OnUpdate()
{
if (OnPreUpdate != null) {
OnPreUpdate();
}
base.OnUpdate();
if (OnPostUpdate != null) {
OnPostUpdate();
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,76 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Groups
{
using Opsive.BehaviorDesigner.Runtime.Systems;
using Unity.Entities;
/// <summary>
/// Main group for the systems that are responsible for traversing the behavior tree.
/// </summary>
[UpdateInGroup(typeof(BehaviorTreeSystemGroup))]
[UpdateAfter(typeof(InterruptSystemGroup))]
public partial class TraversalSystemGroup : ComponentSystemGroup
{
private SystemHandle m_EvaluationSystemHandle;
private SystemHandle m_DetermineEvaluationSystemHandle;
/// <summary>
/// The group has been created.
/// </summary>
protected override void OnCreate()
{
base.OnCreate();
m_EvaluationSystemHandle = World.GetExistingSystem<EvaluationSystem>();
m_DetermineEvaluationSystemHandle = World.GetExistingSystem<DetermineEvaluationSystem>();
}
/// <summary>
/// Updates the systems. Determines if the systems need to keep being evaluated.
/// </summary>
protected override void OnUpdate()
{
#if UNITY_EDITOR
var count = 0;
#endif
bool evaluate;
do {
base.OnUpdate();
var determineEvaluationSystem = EntityManager.WorldUnmanaged.GetUnsafeSystemRef<DetermineEvaluationSystem>(m_DetermineEvaluationSystemHandle);
determineEvaluationSystem.Complete(EntityManager);
evaluate = determineEvaluationSystem.Evaluate;
var evaluationSystem = EntityManager.WorldUnmanaged.GetUnsafeSystemRef<EvaluationSystem>(m_EvaluationSystemHandle);
evaluationSystem.Complete(EntityManager);
#if UNITY_EDITOR
if (evaluate) {
count++;
if (count == ushort.MaxValue / 10) {
UnityEngine.Debug.LogWarning("An infinite loop would have been caused by the TraversalSystemGroup. Please email support@opsive.com with steps to reproduce this error.");
break;
}
}
#endif
} while (evaluate);
}
/// <summary>
/// The group has stopped running.
/// </summary>
protected override void OnStopRunning()
{
base.OnStopRunning();
var evaluationSystem = EntityManager.WorldUnmanaged.GetUnsafeSystemRef<EvaluationSystem>(m_EvaluationSystemHandle);
evaluationSystem.Complete(EntityManager, true);
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,29 @@
{
"name": "Opsive.BehaviorDesigner.Runtime",
"rootNamespace": "Opsive.BehaviorDesigner.Runtime",
"references": [
"GUID:e9319bc8ab9315745bdf80aeffa01d6d",
"GUID:d8e89a79cd8df884b8d5b3356783eb74",
"GUID:734d92eba21c94caba915361bd5ac177",
"GUID:2665a8d13d1b3f18800f46e256720795",
"GUID:e0cd26848372d4e5c891c569017e11f1",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:a5baed0c9693541a5bd947d336ec7659",
"GUID:8819f35a0fc84499b990e90a4ca1911f"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.entities",
"expression": "1.3.5",
"define": "UNITY_ENTITIES"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f9fbcd65fc33f6847b0f124cf7e891b6
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,298 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using System;
using UnityEngine;
/// <summary>
/// The behavior tree stored within a Scriptable Object.
/// </summary>
[CreateAssetMenu(fileName = "Subtree", menuName = "Opsive/Behavior Designer/Subtree", order = 1)]
public class Subtree : ScriptableObject, IGraph, ISharedVariableContainer
{
[Tooltip("The behavior tree data.")]
[SerializeField] private BehaviorTreeData m_Data = new BehaviorTreeData();
public string Name { get => name; set => name = value; }
public SharedVariable.SharingScope VariableScope { get => SharedVariable.SharingScope.Graph; }
public BehaviorTreeData Data { get => m_Data; }
public UnityEngine.Object Parent { get => this; }
public ILogicNode[] LogicNodes { get => m_Data?.LogicNodes; set => m_Data.LogicNodes = value as ITreeLogicNode[]; }
public ITreeLogicNode[] TreeLogicNodes { get => m_Data?.LogicNodes; set => m_Data.LogicNodes = value; }
public IEventNode[] EventNodes { get => m_Data?.EventNodes; set => m_Data.EventNodes = value; }
public SharedVariable[] SharedVariables { get => m_Data?.SharedVariables; set => m_Data.SharedVariables = value; }
public SharedVariableGroup[] SharedVariableGroups {
#if UNITY_EDITOR
get => Data?.SharedVariableGroups;
set => Data.SharedVariableGroups = value;
#else
get => null;
set { }
#endif
}
public ushort[] DisabledLogicNodes { get => m_Data.DisabledLogicNodes; set => m_Data.DisabledLogicNodes = value; }
public ushort[] DisabledEventNodes { get => m_Data.DisabledEventNodes; set => m_Data.DisabledEventNodes = value; }
public LogicNodeProperties[] LogicNodeProperties
{
#if UNITY_EDITOR
get => m_Data.LogicNodeProperties;
set => m_Data.LogicNodeProperties = value;
#else
get => null;
set { }
#endif
}
public NodeProperties[] EventNodeProperties
{
#if UNITY_EDITOR
get => m_Data.EventNodeProperties;
set => m_Data.EventNodeProperties = value;
#else
get => null;
set { }
#endif
}
public GroupProperties[] GroupProperties
{
#if UNITY_EDITOR
get => m_Data.GroupProperties;
set => m_Data.GroupProperties = value;
#else
get => null;
set { }
#endif
}
public int UniqueID { get => m_Data.UniqueID; }
public bool Pooled { get; set; }
/// <summary>
/// Serializes the behavior tree.
/// </summary>
public void Serialize()
{
m_Data.Serialize();
}
/// <summary>
/// Deserialize the behavior tree.
/// </summary>
/// <param name="force">Should the behavior tree be force deserialized?</param>
/// <returns>True if the tree was deserialized.</returns>
public bool Deserialize(bool force = false)
{
return Deserialize(null, force, force, false, true, null);
}
/// <summary>
/// Deserialize the behavior tree.
/// </summary>
/// <param name="force">Should the behavior tree be force deserialized?</param>
/// <param name="forceSharedVariables">Should the shared variables be force deserialized?</param>
/// <param name="injectSubtrees">Should the subtrees be injected into the behavior tree?</param>
/// <param name="canDeepCopyVariables">Can the SharedVariables be deep copied?</param>
/// <returns>True if the tree was deserialized.</returns>
public bool Deserialize(bool force, bool forceSharedVariables, bool injectSubtrees, bool canDeepCopyVariables = true)
{
return Deserialize(null, force, forceSharedVariables, injectSubtrees, canDeepCopyVariables, null);
}
/// <summary>
/// Deserialize the behavior tree.
/// </summary>
/// <param name="graphComponent">The component that the graph is being deserialized from.</param>
/// <param name="force">Should the behavior tree be force deserialized?</param>
/// <param name="forceSharedVariables">Should the shared variables be force deserialized?</param>
/// <param name="injectSubtrees">Should the subtrees be injected into the behavior tree?</param>
/// <param name="canDeepCopyVariables">Can the SharedVariables be deep copied?</param>
/// <param name="sharedVariableOverrides">A list of SharedVariables that should override the current SharedVariable value.</param>
/// <returns>True if the tree was deserialized.</returns>
public bool Deserialize(IGraphComponent graphComponent, bool force, bool forceSharedVariables, bool injectSubtrees, bool canDeepCopyVariables, SharedVariableOverride[] sharedVariableOverrides)
{
if (m_Data == null) {
return false;
}
return m_Data.Deserialize(graphComponent, this, force, forceSharedVariables, injectSubtrees, canDeepCopyVariables, sharedVariableOverrides);
}
/// <summary>
/// Deserializes the SharedVariables. This allows the SharedVariables to be deserialized independently.
/// </summary>
/// <param name="force">Should the variables be forced deserialized?</param>
/// <returns>True if the SharedVariables were deserialized.</returns>
public bool DeserializeSharedVariables(bool force)
{
if (m_Data == null) {
return false;
}
return m_Data.DeserializeSharedVariables(this, force, false);
}
/// <summary>
/// Adds the specified logic node.
/// </summary>
/// <param name="node">The node that should be added.</param>
public void AddNode(ILogicNode node)
{
m_Data.AddNode(node as ITreeLogicNode);
}
/// <summary>
/// Removes the specified logic node.
/// </summary>
/// <param name="node">The node that should be removed.</param>
/// <returns>True if the node was removed.</returns>
public bool RemoveNode(ILogicNode node)
{
return m_Data.RemoveNode(node as ITreeLogicNode);
}
/// <summary>
/// Adds the specified event node.
/// </summary>
/// <param name="eventNode">The event node that should be added.</param>
public void AddNode(IEventNode eventNode)
{
m_Data.AddNode(eventNode);
}
/// <summary>
/// Removes the specified event node.
/// </summary>
/// <param name="eventNode">The event node that should be removed.</param>
/// <returns>True if the event node was removed.</returns>
public bool RemoveNode(IEventNode eventNode)
{
return m_Data.RemoveNode(eventNode);
}
/// <summary>
/// Returns the Node of the specified type.
/// </summary>
/// <param name="type">The type of Node that should be retrieved.</typeparam>
/// <returns>The Node of the specified type (can be null).</returns>
public ILogicNode GetNode(Type type)
{
return m_Data.GetNode(type);
}
/// <summary>
/// Returns the EventNode of the specified type.
/// </summary>
/// <param name="type">The type of EventNode that should be retrieved.</typeparam>
/// <returns>The EventNode of the specified type (can be null). If the node is found the index will also be returned.</returns>
public (IEventNode, ushort) GetEventNode(Type type)
{
return m_Data.GetEventNode(type);
}
/// <summary>
/// Returns the SharedVariable with the specified name.
/// </summary>
/// <param name="name">The name of the SharedVariable that should be retrieved.</param>
/// <returns>The SharedVariable with the specified name (can be null).</returns>
public SharedVariable GetVariable(PropertyName name)
{
Deserialize();
return m_Data.GetVariable(this, name, SharedVariable.SharingScope.Graph);
}
/// <summary>
/// Returns the SharedVariable of the specified name.
/// </summary>
/// <param name="name">The name of the SharedVariable that should be retrieved.</param>
/// <returns>The SharedVariable with the specified name (can be null).</returns>
public SharedVariable<T> GetVariable<T>(PropertyName name)
{
Deserialize();
return m_Data.GetVariable<T>(this, name, SharedVariable.SharingScope.Graph);
}
/// <summary>
/// Sets the value of the SharedVariable.
/// </summary>
/// <typeparam name="T">The type of SharedVarible.</typeparam>
/// <param name="name">The name of the SharedVariable.</param>
/// <param name="value">The value of the SharedVariable.</param>
/// <returns>True if the value was set.</returns>
public bool SetVariableValue<T>(PropertyName name, T value)
{
Deserialize();
return m_Data.SetVariableValue<T>(this, name, value, SharedVariable.SharingScope.Graph);
}
/// <summary>
/// Is the node with the specified index enabled?
/// </summary>
/// <param name="logicNode">Is the node a LogicNode?</param>
/// <param name="index">The index of the node.</param>
/// <returns>True if the node with the specified index is enabled.</returns>
public bool IsNodeEnabled(bool logicNode, int index)
{
return m_Data.IsNodeEnabled(logicNode, index);
}
/// <summary>
/// Is the node with the specified index active?
/// </summary>
/// <param name="logicNode">Is the node a LogicNode?</param>
/// <param name="index">The index of the node.</param>
/// <returns>True if the node with the specified index is active.</returns>
public bool IsNodeActive(bool logicNode, int index)
{
return false; // The subtree node itself is never active.
}
/// <summary>
/// Copies the graph onto the current graph.
/// </summary>
/// <param name="other">The graph that should be copied.</param>
public void Clone(IGraph other)
{
m_Data = new BehaviorTreeData();
m_Data.EventNodes = other.EventNodes;
m_Data.LogicNodes = other.LogicNodes as ITreeLogicNode[];
m_Data.SharedVariables = other.SharedVariables;
#if UNITY_EDITOR
m_Data.EventNodeProperties = other.EventNodeProperties;
m_Data.LogicNodeProperties = other.LogicNodeProperties;
m_Data.GroupProperties = other.GroupProperties;
#endif
m_Data.Serialize();
}
/// <summary>
/// Overrides ToString.
/// </summary>
/// <returns>The desired string value.</returns>
public override string ToString()
{
return name;
}
}
/// <summary>
/// Attribute indicating that a ReorderableList should be used for the Subtree array.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class SubtreeListAttribute : System.Attribute
{
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ab170e3869205449b993a35614c2084
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e0a8f1df788b6274a9a24003859dfa7e, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a33ce2113e48ee54f946531482da6261
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,412 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Systems
{
using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Groups;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.BehaviorDesigner.Runtime.Utility;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
/// <summary>
/// System which checks for any tasks that should be reevaluated with conditional aborts. This system only marks the tasks, it does not do
/// the actual reevaluation or interruption.
/// </summary>
[UpdateInGroup(typeof(BeforeTraversalSystemGroup), OrderFirst = true)]
[DisableAutoCreation]
public partial struct ReevaluateSystem : ISystem
{
private EntityQuery m_Query;
/// <summary>
/// Builds the query.
/// </summary>
/// <param name="state">THe current SystemState.</param>
private void OnCreate(ref SystemState state)
{
m_Query = SystemAPI.QueryBuilder().WithAllRW<TaskComponent>().WithAllRW<ReevaluateTaskComponent>().WithAll<BranchComponent>().WithAbsent<BakedBehaviorTree>().Build();
}
/// <summary>
/// Creates the ReevaluateJob.
/// </summary>
/// <param name="state">The current SystemState.</param>
private void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.TempJob);
state.Dependency = new ReevaluateJob()
{
EntityCommandBuffer = ecb.AsParallelWriter(),
}.ScheduleParallel(m_Query, state.Dependency);
// The job must run immediately for the next systems.
state.Dependency.Complete();
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
/// <summary>
/// Job which checks for any tasks that should be reevaluated with conditional aborts. This job only flags the tasks, it does not do
/// the actual reevaluation or interruption.
/// </summary>
[BurstCompile]
private partial struct ReevaluateJob : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="reevaluateTaskComponents">An array of reevaluate task components.</param>
[BurstCompile]
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, in DynamicBuffer<BranchComponent> branchComponents, ref DynamicBuffer<TaskComponent> taskComponents, ref DynamicBuffer<ReevaluateTaskComponent> reevaluateTaskComponents)
{
for (int i = 0; i < reevaluateTaskComponents.Length; ++i) {
var reevaluateTaskComponent = reevaluateTaskComponents[i];
// The task may not be able to reevaluate.
var taskComponent = taskComponents[reevaluateTaskComponent.Index];
if (!taskComponent.CanReevaluate || taskComponent.Disabled) {
continue;
}
// The branch may not be active.
var branchComponent = branchComponents[taskComponent.BranchIndex];
if (branchComponent.ActiveIndex == ushort.MaxValue) {
if (taskComponent.Reevaluate) {
taskComponent.Reevaluate = false;
taskComponents[reevaluateTaskComponent.Index] = taskComponent;
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Inactive;
reevaluateTaskComponents[i] = reevaluateTaskComponent;
}
continue;
}
var reevaluate = false;
if (reevaluateTaskComponent.AbortType == ConditionalAbortType.Self || reevaluateTaskComponent.AbortType == ConditionalAbortType.Both) {
if (branchComponent.ActiveIndex > taskComponent.Index && branchComponent.ActiveIndex <= reevaluateTaskComponent.SelfPriorityUpperIndex) {
// Reevaluate.
reevaluate = true;
if (reevaluateTaskComponent.ReevaluateStatus == ReevaluateStatus.Inactive) {
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Active;
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, reevaluateTaskComponent.ReevaluateFlagComponentType, true);
}
}
}
if (!reevaluate && (reevaluateTaskComponent.AbortType == ConditionalAbortType.LowerPriority || reevaluateTaskComponent.AbortType == ConditionalAbortType.Both)) {
if (branchComponent.ActiveIndex > reevaluateTaskComponent.LowerPriorityLowerIndex && branchComponent.ActiveIndex <= reevaluateTaskComponent.LowerPriorityUpperIndex) {
// Reevaluate.
reevaluate = true;
if (reevaluateTaskComponent.ReevaluateStatus == ReevaluateStatus.Inactive) {
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Active;
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, reevaluateTaskComponent.ReevaluateFlagComponentType, true);
}
}
}
// The task should no longer reevaluate.
if (!reevaluate && (taskComponent.Reevaluate || reevaluateTaskComponent.ReevaluateStatus == ReevaluateStatus.Dirty)) {
// The system needs to be kept active if there are other tasks with the same reevaluate tag.
var keepSystemActive = false;
for (int j = 0; j < reevaluateTaskComponents.Length; ++j) {
if (i == j) {
continue;
}
if ((reevaluateTaskComponents[j].ReevaluateStatus == ReevaluateStatus.Active || reevaluateTaskComponents[j].ReevaluateStatus == ReevaluateStatus.Dirty) &&
reevaluateTaskComponent.ReevaluateFlagComponentType == reevaluateTaskComponents[j].ReevaluateFlagComponentType) {
keepSystemActive = true;
break;
}
}
if (!keepSystemActive) {
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, reevaluateTaskComponent.ReevaluateFlagComponentType, false);
}
// The task should always disable itself.
taskComponent.Reevaluate = false;
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Inactive;
} else {
// Store the current status of the task. This status will be compared after the task is reevaluated within DetermineInterruptSystem.
reevaluateTaskComponent.OriginalStatus = taskComponent.Status;
}
reevaluateTaskComponents[i] = reevaluateTaskComponent;
taskComponent.Reevaluate = reevaluate;
taskComponents[reevaluateTaskComponent.Index] = taskComponent;
}
}
}
}
/// <summary>
/// The tasks have been reevaluated. Compare the status to determine if an interrupt should occur.
/// </summary>
[UpdateInGroup(typeof(InterruptSystemGroup))]
[UpdateBefore(typeof(InterruptSystem))]
public partial struct ConditionalAbortsInvokerSystem : ISystem
{
private EntityQuery m_Query;
/// <summary>
/// Builds the query.
/// </summary>
/// <param name="state">THe current SystemState.</param>
private void OnCreate(ref SystemState state)
{
m_Query = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAllRW<TaskComponent>().WithAllRW<ReevaluateTaskComponent>().WithAbsent<BakedBehaviorTree>().Build();
}
/// <summary>
/// Creates the jobs necessary for conditional aborts.
/// </summary>
/// <param name="state">The current SystemState.</param>
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.TempJob);
state.Dependency = new ConditionalAbortsJob()
{
EntityCommandBuffer = ecb.AsParallelWriter()
}.ScheduleParallel(m_Query, state.Dependency);
// The jobs must be run immediately for the next systems.
state.Dependency.Complete();
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
/// <summary>
/// Job which checks for any tasks that should be reevaluated with conditional aborts. This job only flags the tasks, it does not do
/// the actual reevaluation or interruption.
/// </summary>
[BurstCompile]
private partial struct ConditionalAbortsJob : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="reevaluateTaskComponents">An array of reevaluate task components.</param>
[BurstCompile]
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, ref DynamicBuffer<TaskComponent> taskComponents, ref DynamicBuffer<ReevaluateTaskComponent> reevaluateTaskComponents)
{
for (int i = 0; i < reevaluateTaskComponents.Length; ++i) {
var reevaluateTaskComponent = reevaluateTaskComponents[i];
var taskComponent = taskComponents[reevaluateTaskComponent.Index];
if (taskComponent.Reevaluate) {
if (reevaluateTaskComponent.OriginalStatus != taskComponent.Status) {
// The status is different. This will cause an interrupt.
var branchComponent = branchComponents[taskComponent.BranchIndex];
// The task with the highest priority should cause the abort.
if (branchComponent.InterruptType == InterruptType.None || taskComponent.Index < branchComponent.InterruptIndex) {
branchComponent.InterruptIndex = taskComponent.Index;
branchComponent.InterruptType = InterruptType.Branch;
branchComponents[taskComponent.BranchIndex] = branchComponent;
} else {
taskComponent.Status = TaskStatus.Inactive;
}
taskComponent.Reevaluate = false;
taskComponents[reevaluateTaskComponent.Index] = taskComponent;
EntityCommandBuffer.SetComponentEnabled<InterruptFlag>(entityIndex, entity, true);
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Dirty;
var reevaluateTaskComponentsBuffer = reevaluateTaskComponents;
reevaluateTaskComponentsBuffer[i] = reevaluateTaskComponent;
}
}
}
}
}
}
/// <summary>
/// Processes any interrupts.
/// </summary>
[UpdateInGroup(typeof(InterruptSystemGroup))]
[UpdateAfter(typeof(ConditionalAbortsInvokerSystem))]
public partial struct InterruptSystem : ISystem
{
private EntityQuery m_Query;
/// <summary>
/// Builds the query.
/// </summary>
/// <param name="state">THe current SystemState.</param>
private void OnCreate(ref SystemState state)
{
m_Query = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAllRW<TaskComponent>().WithAll<InterruptFlag>().Build();
}
/// <summary>
/// Creates the InterruptJob.
/// </summary>
/// <param name="state">The current SystemState.</param>
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.TempJob);
state.Dependency = new InterruptJob()
{
EntityCommandBuffer = ecb.AsParallelWriter(),
}.ScheduleParallel(m_Query, state.Dependency);
// The job must run immediately for the next systems.
state.Dependency.Complete();
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
/// <summary>
/// Triggers the interrupts.
/// </summary>
[BurstCompile]
private partial struct InterruptJob : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
[BurstCompile]
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, DynamicBuffer<BranchComponent> branchComponents, DynamicBuffer<TaskComponent> taskComponents)
{
for (ushort i = 0; i < branchComponents.Length; ++i) {
var branchComponent = branchComponents[i];
if (branchComponent.InterruptType != InterruptType.None) {
var targetTaskComponent = taskComponents[branchComponent.InterruptIndex];
var parentIndex = targetTaskComponent.ParentIndex == ushort.MaxValue ? targetTaskComponent.Index : targetTaskComponent.ParentIndex;
TaskStatus prevActiveNewStatus;
if (branchComponent.InterruptType == InterruptType.Branch) {
branchComponent.NextIndex = branchComponent.InterruptIndex;
branchComponents[i] = branchComponent;
// Start the target task.
targetTaskComponent.Status = TaskStatus.Running;
// Set the target branch tasks to running. Any parent that uses conditional aborts should implement IInterruptResponder.
while (parentIndex != ushort.MaxValue && taskComponents[parentIndex].Status != TaskStatus.Running) {
var parentTaskComponent = taskComponents[parentIndex];
parentTaskComponent.Status = TaskStatus.Running;
taskComponents[parentIndex] = parentTaskComponent;
parentIndex = parentTaskComponent.ParentIndex;
}
prevActiveNewStatus = TaskStatus.Failure;
} else { // InterruptType.ImmediateSuccess/Failure.
targetTaskComponent.Status = branchComponent.InterruptType == InterruptType.ImmediateSuccess ? TaskStatus.Success : TaskStatus.Failure;
var targetBranchComponent = branchComponents[targetTaskComponent.BranchIndex];
targetBranchComponent.NextIndex = targetTaskComponent.ParentIndex;
branchComponents[targetTaskComponent.BranchIndex] = targetBranchComponent;
prevActiveNewStatus = targetTaskComponent.Status;
}
// Determine if any other branches need to be interrupted.
for (ushort j = i; j < branchComponents.Length; ++j) {
if (i == j || TraversalUtility.IsParent((ushort)branchComponents[j].ActiveIndex, parentIndex, ref taskComponents)) {
AbortChildren((ushort)branchComponents[j].ActiveIndex, parentIndex, ref taskComponents, prevActiveNewStatus);
// Reset any queued children.
var taskComponentBuffer = taskComponents;
var childCount = TraversalUtility.GetChildCount(branchComponent.ActiveIndex, ref taskComponentBuffer);
for (int k = 0; k < childCount; ++k) {
var childTaskComponent = taskComponents[branchComponent.ActiveIndex + k + 1];
if (childTaskComponent.Status == TaskStatus.Queued) {
childTaskComponent.Status = TaskStatus.Inactive;
taskComponentBuffer[branchComponent.ActiveIndex + k + 1] = childTaskComponent;
}
}
// If the branch is a parallel branch then reset the NextIndex. The current branch (i) will be interrupted normally above.
var localBranchComponent = branchComponents[j];
if (localBranchComponent.InterruptType == InterruptType.None) {
localBranchComponent.NextIndex = ushort.MaxValue;
branchComponents[j] = localBranchComponent;
}
EntityCommandBuffer.SetComponentEnabled<InterruptedFlag>(entityIndex, entity, true);
}
}
taskComponents[targetTaskComponent.Index] = targetTaskComponent;
}
}
}
/// <summary>
/// Aborts all of the children within the specified branch.
/// </summary>
/// <param name="activeIndex">The index of the active task within the branch.</param>
/// <param name="parentIndex">Aborts the tasks up to the specified parent index.</param>
/// <param name="taskComponents">All of the tasks.</param>
/// <param name="status">The abort status.</param>
[BurstCompile]
private void AbortChildren(ushort activeIndex, ushort parentIndex, ref DynamicBuffer<TaskComponent> taskComponents, TaskStatus status)
{
while (activeIndex != ushort.MaxValue && activeIndex != parentIndex) {
var activeTask = taskComponents[activeIndex];
activeTask.Status = status;
taskComponents[activeIndex] = activeTask;
activeIndex = activeTask.ParentIndex;
}
}
}
}
/// <summary>
/// Cleanup the interrupts after they have run.
/// </summary>
[UpdateInGroup(typeof(InterruptSystemGroup), OrderLast = true)]
public partial struct InterruptCleanupSystem : ISystem
{
/// <summary>
/// Executes the system.
/// </summary>
/// <param name="state">The current SystemState.</param>
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
foreach (var (branchComponents, entity) in
SystemAPI.Query<DynamicBuffer<BranchComponent>>().WithAll<InterruptFlag>().WithEntityAccess()) {
for (int i = 0; i < branchComponents.Length; ++i) {
var branchComponent = branchComponents[i];
if (branchComponent.InterruptType != InterruptType.None) {
// Reset the interruption.
branchComponent.InterruptType = InterruptType.None;
branchComponent.InterruptIndex = 0;
var branchComponentBuffer = branchComponents;
branchComponentBuffer[i] = branchComponent;
state.EntityManager.SetComponentEnabled<InterruptFlag>(entity, false);
}
}
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,176 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Systems
{
using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Groups;
using Unity.Burst;
using Unity.Burst.Intrinsics;
using Unity.Collections;
using Unity.Entities;
/// <summary>
/// Resets the evaluation status.
/// </summary>
[DisableAutoCreation]
[UpdateInGroup(typeof(BehaviorTreeSystemGroup), OrderLast = true)]
[BurstCompile]
public partial struct EvaluationCleanupSystem : ISystem
{
private EntityQuery m_EvaluateCleanupQuery;
private ComponentTypeHandle<EnabledFlag> m_EnabledComponentHandle;
private ComponentTypeHandle<EvaluateFlag> m_EvaluateComponentHandle;
private BufferTypeHandle<BranchComponent> m_BranchComponentHandle;
/// <summary>
/// Creates the required objects for use within the job system.
/// </summary>
/// <param name="state">The current SystemState.</param>
[BurstCompile]
private void OnCreate(ref SystemState state)
{
m_EvaluateCleanupQuery = new EntityQueryBuilder(Allocator.Temp)
.WithAll<EvaluateFlag>()
.WithAllRW<BranchComponent>()
.WithOptions(EntityQueryOptions.IgnoreComponentEnabledState)
.Build(ref state);
m_EnabledComponentHandle = state.GetComponentTypeHandle<EnabledFlag>();
m_EvaluateComponentHandle = state.GetComponentTypeHandle<EvaluateFlag>();
m_BranchComponentHandle = state.GetBufferTypeHandle<BranchComponent>();
}
/// <summary>
/// Updates the data object values for use within the job system.
/// </summary>
/// <param name="state">The current SystemState.</param>
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
state.Dependency.Complete();
// Reset the evaluation status.
m_EnabledComponentHandle.Update(ref state);
m_EvaluateComponentHandle.Update(ref state);
m_BranchComponentHandle.Update(ref state);
var evaluationCleanupJob = new EvaluationCleanupJob()
{
EnabledComponentHandle = m_EnabledComponentHandle,
EvaluateComponentHandle = m_EvaluateComponentHandle,
BranchComponentHandle = m_BranchComponentHandle,
};
state.Dependency = evaluationCleanupJob.ScheduleParallel(m_EvaluateCleanupQuery, state.Dependency);
}
/// <summary>
/// Job that resets the EvaluationComponent component value.
/// </summary>
[BurstCompile(CompileSynchronously = true)]
public struct EvaluationCleanupJob : IJobChunk
{
[UnityEngine.Tooltip("A reference to the Enabled Component Handle.")]
public ComponentTypeHandle<EnabledFlag> EnabledComponentHandle;
[UnityEngine.Tooltip("A reference to the Evaluate Component Handle.")]
public ComponentTypeHandle<EvaluateFlag> EvaluateComponentHandle;
[UnityEngine.Tooltip("A reference to the Branch Component Handle.")]
public BufferTypeHandle<BranchComponent> BranchComponentHandle;
/// <summary>
/// Resets the EvaluationComponent component value.
/// </summary>
/// <param name="chunk">Block of memory that contains the entity and components.</param>
/// <param name="unfilteredChunkIndex">The index of the chunk.</param>
/// <param name="useEnabledMask">Should the enabled mask be used?</param>
/// <param name="chunkEnabledMask">The bitwise enabled mask.</param>
[BurstCompile]
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
{
var branchAccessor = chunk.GetBufferAccessor(ref BranchComponentHandle);
for (int i = 0; i < chunk.Count; i++) {
// If the chunk is enabled then it should be evaluated.
if (chunk.IsComponentEnabled<EnabledFlag>(ref EnabledComponentHandle, i)) {
chunk.SetComponentEnabled<EvaluateFlag>(ref EvaluateComponentHandle, i, true);
}
// Reset CanExecute for all branches so they can execute in the next tick.
var branchComponents = branchAccessor[i];
for (int j = 0; j < branchComponents.Length; j++) {
var branchComponent = branchComponents[j];
branchComponent.CanExecute = true;
branchComponent.LastActiveIndex = ushort.MaxValue;
branchComponents[j] = branchComponent;
}
}
}
}
}
/// <summary>
/// Resets the InterruptedFlag enabled value.
/// </summary>
[DisableAutoCreation]
[UpdateInGroup(typeof(BehaviorTreeSystemGroup), OrderLast = true)]
[BurstCompile]
public partial struct InterruptedCleanupSystem : ISystem
{
private EntityQuery m_InterruptedCleanupQuery;
private ComponentTypeHandle<InterruptedFlag> m_InterruptedComponentHandle;
/// <summary>
/// Creates the required objects for use within the job system.
/// </summary>
/// <param name="state">The current SystemState.</param>
[BurstCompile]
private void OnCreate(ref SystemState state)
{
m_InterruptedCleanupQuery = new EntityQueryBuilder(Allocator.Temp)
.WithAll<InterruptedFlag>()
.Build(ref state);
m_InterruptedComponentHandle = state.GetComponentTypeHandle<InterruptedFlag>();
}
/// <summary>
/// Updates the data object values for use within the job system.
/// </summary>
/// <param name="state">The current SystemState.</param>
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
// Clean up the interrupted tag.
m_InterruptedComponentHandle.Update(ref state);
var interruptedJob = new InterruptedCleanupJob()
{
InterruptedComponentHandle = m_InterruptedComponentHandle,
};
state.Dependency = interruptedJob.ScheduleParallel(m_InterruptedCleanupQuery, state.Dependency);
}
/// <summary>
/// Job that resets the InterruptedFlag value.
/// </summary>
[BurstCompile(CompileSynchronously = true)]
public partial struct InterruptedCleanupJob : IJobChunk
{
[UnityEngine.Tooltip("A reference to the Interrupted Component Handle.")]
public ComponentTypeHandle<InterruptedFlag> InterruptedComponentHandle;
/// <summary>
/// Resets the InterruptedFlag value.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
[BurstCompile]
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
{
for (int i = 0; i < chunk.Count; i++) {
// Only chunks with the tag enabled will be returned so there's no need to check if the tag is enabled.
chunk.SetComponentEnabled<InterruptedFlag>(ref InterruptedComponentHandle, i, false);
}
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,217 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Systems
{
using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Groups;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.BehaviorDesigner.Runtime.Utility;
using Opsive.GraphDesigner.Runtime;
using Unity.Entities;
using UnityEngine;
/// <summary>
/// Specifies that the node is an object task which can specify the next child that should run.
/// </summary>
public interface ITaskObjectParentNode
{
/// <summary>
/// Returns the index of the next child that should run. Set to ushort.MaxValue to ignore.
/// </summary>
ushort NextChildIndex { get; }
}
/// <summary>
/// The DOTS data structure for the TaskObject class.
/// </summary>
public struct TaskObjectComponent : IBufferElementData
{
[Tooltip("The index of the task.")]
public ushort Index;
}
/// <summary>
/// A DOTS flag indicating when an TaskObject node is active.
/// </summary>
public struct TaskObjectFlag : IComponentData, IEnableableComponent { }
/// <summary>
/// Runs the TaskObject logic.
/// </summary>
[DisableAutoCreation]
[UpdateInGroup(typeof(TraversalTaskSystemGroup), OrderLast = true)]
public partial struct TaskObjectSystem : ISystem
{
/// <summary>
/// Updates the logic.
/// </summary>
/// <param name="state">The current state of the system.</param>
private void OnUpdate(ref SystemState state)
{
// When the task is interrupted there is no callback which prevents Task.OnEnd from being called. Track the status within the referenced task object and if the status is different then
// the task was aborted and OnEnd needs to be called.
foreach (var (taskObjectComponents, taskComponents, entity) in
SystemAPI.Query<DynamicBuffer<TaskObjectComponent>, DynamicBuffer<TaskComponent>>().WithAll<InterruptedFlag>().WithEntityAccess()) {
var behaviorTree = BehaviorTree.GetBehaviorTree(entity);
if (behaviorTree == null) {
continue;
}
for (int i = 0; i < taskObjectComponents.Length; ++i) {
var taskObjectComponent = taskObjectComponents[i];
var taskComponent = taskComponents[taskObjectComponent.Index];
if (taskComponent.Status == TaskStatus.Success || taskComponent.Status == TaskStatus.Failure) {
var task = behaviorTree.GetTask(taskObjectComponent.Index) as Task;
if (task.Status != taskComponent.Status) {
task.OnEnd();
task.Status = taskComponent.Status;
}
}
}
}
// Update the task objects.
foreach (var (taskObjectComponents, taskComponents, branchComponents, entity) in
SystemAPI.Query<DynamicBuffer<TaskObjectComponent>, DynamicBuffer<TaskComponent>, DynamicBuffer<BranchComponent>>().WithAll<TaskObjectFlag, EvaluateFlag>().WithEntityAccess()) {
var behaviorTree = BehaviorTree.GetBehaviorTree(entity);
if (behaviorTree == null) {
continue;
}
for (int i = 0; i < taskObjectComponents.Length; ++i) {
var taskObjectComponent = taskObjectComponents[i];
var taskComponent = taskComponents[taskObjectComponent.Index];
var branchComponent = branchComponents[taskComponent.BranchIndex];
if (!branchComponent.CanExecute || branchComponent.ActiveIndex != taskComponent.Index) {
continue;
}
var task = behaviorTree.GetTask(taskObjectComponent.Index) as Task;
if (taskComponent.Status == TaskStatus.Queued) {
task.Status = taskComponent.Status = TaskStatus.Running;
var taskComponentBuffer = taskComponents;
taskComponentBuffer[taskComponent.Index] = taskComponent;
task.OnStart();
}
if (taskComponent.Status != TaskStatus.Running) {
continue;
}
var status = task.OnUpdate();
// Update the status if has changed.
if (status != taskComponent.Status) {
task.Status = taskComponent.Status = status;
var taskComponentBuffer = taskComponents;
taskComponentBuffer[taskComponent.Index] = taskComponent;
// End the task if it is done running.
if (status != TaskStatus.Running) {
task.OnEnd();
branchComponent = branchComponents[taskComponent.BranchIndex];
branchComponent.NextIndex = taskComponent.ParentIndex;
var branchComponentBuffer = branchComponents;
branchComponentBuffer[taskComponent.BranchIndex] = branchComponent;
}
}
if (task is IParentNode && (task is ITaskObjectParentNode taskObjectParentNode)) {
if (status == TaskStatus.Running) {
// Parent object tasks do not have a direct way to set the next child. Use the ITaskObjectParentNode to switch the child task.
if (taskObjectParentNode.NextChildIndex != ushort.MaxValue && taskComponents[taskObjectParentNode.NextChildIndex].Status != TaskStatus.Running) {
branchComponent = branchComponents[taskComponent.BranchIndex];
branchComponent.NextIndex = taskObjectParentNode.NextChildIndex;
var branchComponentBuffer = branchComponents;
branchComponentBuffer[taskComponent.BranchIndex] = branchComponent;
var nextTaskComponent = taskComponents[taskObjectParentNode.NextChildIndex];
nextTaskComponent.Status = TaskStatus.Queued;
var taskComponentBuffer = taskComponents;
taskComponentBuffer[taskObjectParentNode.NextChildIndex] = nextTaskComponent;
}
} else if (status == TaskStatus.Success || status == TaskStatus.Failure) {
// An interrupt should occur if the parent returns a success or failure status before the children.
var taskComponentBuffer = taskComponents;
var childCount = TraversalUtility.GetChildCount(taskComponent.Index, ref taskComponentBuffer);
var branchComponentBuffer = branchComponents;
var hasInterruptComponents = SystemAPI.HasComponent<InterruptFlag>(entity);
var interruptedFlagEnabled = SystemAPI.IsComponentEnabled<InterruptedFlag>(entity);
for (ushort j = (ushort)(taskComponent.Index + 1); j < taskComponent.Index + 1 + childCount; ++j) {
var childTaskComponent = taskComponentBuffer[j];
if (childTaskComponent.Status == TaskStatus.Running || childTaskComponent.Status == TaskStatus.Queued) {
childTaskComponent.Status = status;
taskComponentBuffer[j] = childTaskComponent;
branchComponent = branchComponentBuffer[childTaskComponent.BranchIndex];
if (!hasInterruptComponents) {
ComponentUtility.AddInterruptComponents(behaviorTree.World.EntityManager, entity);
hasInterruptComponents = true;
}
if (!interruptedFlagEnabled) {
SystemAPI.SetComponentEnabled<InterruptedFlag>(entity, true);
interruptedFlagEnabled = true;
}
if (branchComponent.ActiveIndex == childTaskComponent.Index) {
branchComponent.NextIndex = ushort.MaxValue;
branchComponentBuffer[childTaskComponent.BranchIndex] = branchComponent;
}
}
}
}
}
}
}
}
}
/// <summary>
/// A DOTS tag indicating when an TaskObject node needs to be reevaluated.
/// </summary>
public struct TaskObjectReevaluateFlag : IComponentData, IEnableableComponent
{
}
/// <summary>
/// Runs the TaskObject reevaluation logic.
/// </summary>
[DisableAutoCreation]
public partial struct TaskObjectReevaluateSystem : ISystem
{
/// <summary>
/// Updates the reevaluation logic.
/// </summary>
/// <param name="state">The current state of the system.</param>
private void OnUpdate(ref SystemState state)
{
foreach (var (taskComponents, taskObjectComponents, entity) in
SystemAPI.Query<DynamicBuffer<TaskComponent>, DynamicBuffer<TaskObjectComponent>>().WithAll<TaskObjectReevaluateFlag, EvaluateFlag>().WithEntityAccess()) {
for (int i = 0; i < taskObjectComponents.Length; ++i) {
var taskObjectComponent = taskObjectComponents[i];
var taskComponent = taskComponents[taskObjectComponent.Index];
if (!taskComponent.Reevaluate) {
continue;
}
var behaviorTree = BehaviorTree.GetBehaviorTree(entity);
if (behaviorTree == null) {
continue;
}
var task = behaviorTree.GetTask(taskObjectComponent.Index) as IConditionalReevaluation;
var status = task.OnReevaluateUpdate();
if (status != taskComponent.Status) {
taskComponent.Status = status;
var buffer = taskComponents;
buffer[taskComponent.Index] = taskComponent;
}
}
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,617 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Systems
{
using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Groups;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.BehaviorDesigner.Runtime.Utility;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using UnityEngine;
/// <summary>
/// Traverses and ensures the correct tasks are active.
/// </summary>
[UpdateInGroup(typeof(TraversalSystemGroup))]
[UpdateAfter(typeof(TraversalTaskSystemGroup))]
public partial struct EvaluationSystem : ISystem
{
private bool m_JobScheduled;
private EntityQuery m_Query;
private JobHandle m_Dependency;
private EntityCommandBuffer m_EntityCommandBuffer;
/// <summary>
/// The system has been created.
/// </summary>
/// <param name="state">The state of the system.</param>
private void OnCreate(ref SystemState state)
{
m_JobScheduled = false;
m_Query = SystemAPI.QueryBuilder().WithAllRW<BranchComponent, TaskComponent>().WithAbsent<BakedBehaviorTree>().Build();
}
/// <summary>
/// Starts the job which traverses the tree.
/// </summary>
/// <param name="state">The current state of the system.</param>
private void OnUpdate(ref SystemState state)
{
m_JobScheduled = true;
m_EntityCommandBuffer = new EntityCommandBuffer(state.WorldUpdateAllocator);
m_Dependency = state.Dependency = new EvaluationJob()
{
EntityCommandBuffer = m_EntityCommandBuffer.AsParallelWriter(),
}.ScheduleParallel(m_Query, state.Dependency);
}
/// <summary>
/// Completes the job and releases any memory.
/// </summary>
/// <param name="entityManager">The running EntityManager.</param>
/// <param name="stopRunning">Has the system been stopped?</param>
[BurstCompile]
public void Complete(EntityManager entityManager, bool stopRunning = false)
{
if (!m_JobScheduled) {
return;
}
if (!stopRunning) {
m_Dependency.Complete();
m_EntityCommandBuffer.Playback(entityManager);
m_EntityCommandBuffer.Dispose();
}
m_JobScheduled = false;
}
/// <summary>
/// Job which traverses the tree.
/// </summary>
[BurstCompile]
public partial struct EvaluationJob : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
[BurstCompile]
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, ref DynamicBuffer<TaskComponent> taskComponents)
{
for (int i = 0; i < branchComponents.Length; ++i) {
var branchComponent = branchComponents[i];
if (branchComponent.ActiveIndex != ushort.MaxValue && branchComponent.ActiveIndex == branchComponent.NextIndex) {
var activeTask = taskComponents[branchComponent.ActiveIndex];
if (activeTask.Status == TaskStatus.Success || activeTask.Status == TaskStatus.Failure) {
branchComponent.NextIndex = activeTask.ParentIndex;
}
}
if (branchComponent.ActiveIndex != branchComponent.NextIndex) {
// Do not switch into a disabled task.
if (branchComponent.NextIndex != ushort.MaxValue && taskComponents[branchComponent.NextIndex].Disabled) {
var taskComponent = taskComponents[branchComponent.NextIndex];
taskComponent.Status = TaskStatus.Inactive;
var taskComponentBuffer = taskComponents;
taskComponentBuffer[branchComponent.NextIndex] = taskComponent;
branchComponent.NextIndex = branchComponent.ActiveIndex;
} else {
// The status for all children should be reset back to their inactive state if the next task is within a new branch. This will prevent
// the return status from being reset when the task ends normally.
var taskComponentBuffer = taskComponents;
if (branchComponent.NextIndex != ushort.MaxValue &&
!TraversalUtility.IsParent((ushort)branchComponent.ActiveIndex, (ushort)branchComponent.NextIndex, ref taskComponentBuffer)) {
var nextTaskComponent = taskComponents[branchComponent.NextIndex];
if (branchComponent.ActiveIndex != ushort.MaxValue && nextTaskComponent.Status != TaskStatus.Running) { // If the next task is already running then an interrupt has already reset the children.
var childCount = TraversalUtility.GetChildCount(branchComponent.NextIndex, ref taskComponentBuffer);
for (int j = 0; j < childCount; ++j) {
var childTaskComponent = taskComponents[branchComponent.NextIndex + j + 1];
childTaskComponent.Status = TaskStatus.Inactive;
taskComponentBuffer[branchComponent.NextIndex + j + 1] = childTaskComponent;
}
}
nextTaskComponent.Status = nextTaskComponent.Status == TaskStatus.Running ? TaskStatus.Running : TaskStatus.Queued;
taskComponentBuffer[branchComponent.NextIndex] = nextTaskComponent;
}
branchComponent.ActiveIndex = branchComponent.NextIndex;
// Change the component tag if the task type is different.
var componentType = branchComponent.ActiveIndex != ushort.MaxValue ? taskComponents[branchComponent.ActiveIndex].FlagComponentType : new ComponentType();
if (componentType != branchComponent.ActiveFlagComponentType) {
if (branchComponent.ActiveFlagComponentType.TypeIndex != TypeIndex.Null) {
var deactivateTag = true;
for (int j = 0; j < branchComponents.Length; ++j) {
// The tag should be deactivated if no other tasks have the same tag type.
if (i != j && branchComponents[j].ActiveIndex != ushort.MaxValue &&
branchComponent.ActiveFlagComponentType == branchComponents[j].ActiveFlagComponentType) {
deactivateTag = false;
break;
}
}
// The task of that type is no longer active - disable the system to prevent it from running.
if (deactivateTag) {
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, branchComponent.ActiveFlagComponentType, false);
}
}
// A new system type should start.
if (branchComponent.ActiveIndex != ushort.MaxValue) {
var taskComponent = taskComponents[branchComponent.ActiveIndex];
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, taskComponent.FlagComponentType, true);
}
branchComponent.ActiveFlagComponentType = componentType;
}
}
var branchComponentBuffer = branchComponents;
branchComponentBuffer[i] = branchComponent;
}
}
}
}
}
/// <summary>
/// Loops through the active tasks to determine if the system should stay active for the current tick.
/// </summary>
[UpdateInGroup(typeof(TraversalSystemGroup))]
[UpdateAfter(typeof(EvaluationSystem))]
public partial struct DetermineEvaluationSystem : ISystem
{
[Tooltip("Should the group stay active? An inactive tree does not run.")]
public bool Active { get; private set; }
[Tooltip("Should the group be evaluated? This bool indicates if the entire tree should be evaluated instead of the reevaluation" +
"concept for conditional aborts. The tree will be reevaluated if any of the leaf tasks have a status of running.")]
public bool Evaluate { get; private set; }
private bool m_JobScheduled;
private JobHandle m_Dependency;
private EntityQuery m_Query32;
private EntityQuery m_Query64;
private EntityQuery m_Query128;
private EntityQuery m_Query512;
private EntityQuery m_Query4096;
private EntityCommandBuffer m_EntityCommandBuffer32;
private EntityCommandBuffer m_EntityCommandBuffer64;
private EntityCommandBuffer m_EntityCommandBuffer128;
private EntityCommandBuffer m_EntityCommandBuffer512;
private EntityCommandBuffer m_EntityCommandBuffer4096;
private NativeArray<bool> m_Results;
/// <summary>
/// The system has been created.
/// </summary>
/// <param name="state">The state of the system.</param>
private void OnCreate(ref SystemState state)
{
Active = Evaluate = true;
m_JobScheduled = false;
m_Query32 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent32, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
m_Query64 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent64, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
m_Query128 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent128, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
m_Query512 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent512, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
m_Query4096 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent4096, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
}
/// <summary>
/// Executes the job to determine if the system should stay active and evaluating.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
Active = Evaluate = true;
m_JobScheduled = true;
m_EntityCommandBuffer32 = new EntityCommandBuffer(Allocator.TempJob);
m_EntityCommandBuffer64 = new EntityCommandBuffer(Allocator.TempJob);
m_EntityCommandBuffer128 = new EntityCommandBuffer(Allocator.TempJob);
m_EntityCommandBuffer512 = new EntityCommandBuffer(Allocator.TempJob);
m_EntityCommandBuffer4096 = new EntityCommandBuffer(Allocator.TempJob);
m_Results = new NativeArray<bool>(3, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
for (int i = 0; i < m_Results.Length; ++i) {
m_Results[i] = false;
}
// Chain jobs sequentially since they all write to the shared Results array.
m_Dependency = new DetermineEvaluationJob32()
{
EntityCommandBuffer = m_EntityCommandBuffer32.AsParallelWriter(),
Results = m_Results
}.ScheduleParallel(m_Query32, state.Dependency);
m_Dependency = new DetermineEvaluationJob64()
{
EntityCommandBuffer = m_EntityCommandBuffer64.AsParallelWriter(),
Results = m_Results
}.ScheduleParallel(m_Query64, m_Dependency);
m_Dependency = new DetermineEvaluationJob128()
{
EntityCommandBuffer = m_EntityCommandBuffer128.AsParallelWriter(),
Results = m_Results
}.ScheduleParallel(m_Query128, m_Dependency);
m_Dependency = new DetermineEvaluationJob512()
{
EntityCommandBuffer = m_EntityCommandBuffer512.AsParallelWriter(),
Results = m_Results
}.ScheduleParallel(m_Query512, m_Dependency);
m_Dependency = new DetermineEvaluationJob4096()
{
EntityCommandBuffer = m_EntityCommandBuffer4096.AsParallelWriter(),
Results = m_Results
}.ScheduleParallel(m_Query4096, m_Dependency);
state.Dependency = m_Dependency;
}
/// <summary>
/// Completes the job and releases any memory.
/// </summary>
/// <param name="entityManager">The running EntityManager.</param>
[BurstCompile]
public void Complete(EntityManager entityManager)
{
if (!m_JobScheduled) {
return;
}
m_Dependency.Complete();
m_EntityCommandBuffer32.Playback(entityManager);
m_EntityCommandBuffer32.Dispose();
m_EntityCommandBuffer64.Playback(entityManager);
m_EntityCommandBuffer64.Dispose();
m_EntityCommandBuffer128.Playback(entityManager);
m_EntityCommandBuffer128.Dispose();
m_EntityCommandBuffer512.Playback(entityManager);
m_EntityCommandBuffer512.Dispose();
m_EntityCommandBuffer4096.Playback(entityManager);
m_EntityCommandBuffer4096.Dispose();
if (m_Results.IsCreated) {
if (m_Results[0]) {
Active = m_Results[1];
Evaluate = m_Results[2];
} else {
// If the first element is false then no trees executed.
Active = Evaluate = false;
}
m_Results.Dispose();
}
m_JobScheduled = false;
}
/// <summary>
/// The system has been destroyed.
/// </summary>
/// <param name="state">The current state of the system.</param>
private void OnDestroy(ref SystemState state)
{
if (m_Dependency.IsCompleted) {
return;
}
m_Results.Dispose();
}
/// <summary>
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
/// </summary>
[BurstCompile]
public partial struct DetermineEvaluationJob32 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
[BurstCompile]
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent32 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
/// <summary>
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
/// </summary>
[BurstCompile]
public partial struct DetermineEvaluationJob64 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
[BurstCompile]
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent64 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
/// <summary>
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
/// </summary>
[BurstCompile]
public partial struct DetermineEvaluationJob128 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
[BurstCompile]
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent128 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
/// <summary>
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
/// </summary>
[BurstCompile]
public partial struct DetermineEvaluationJob512 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
[BurstCompile]
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent512 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
/// <summary>
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
/// </summary>
[BurstCompile]
public partial struct DetermineEvaluationJob4096 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
/// <summary>
/// Executes the job.
/// </summary>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
[BurstCompile]
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent4096 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
}
/// <summary>
/// Utility functions for the task evaluation.
/// </summary>
[BurstCompile]
public struct EvaluationUtility
{
/// <summary>
/// Is the task at the specified index a parent task.
/// </summary>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="index">The index to check if it is a parent.</param>
/// <returns>True if the task at the specified index is a parent task.</returns>
[BurstCompile]
public static bool IsParentTask(ref DynamicBuffer<TaskComponent> taskComponents, int index)
{
// The last task cannot be a parent.
if (index == taskComponents.Length - 1) {
return false;
}
// The next child will have a parent of the current task.
if (taskComponents[index + 1].ParentIndex == index) {
return true;
}
// The parent index is different - the current task is not a parent.
return false;
}
/// <summary>
/// Core evaluation logic that works with any FixedList type for EvaluatedTasks.
/// </summary>
/// <typeparam name="TFixedList">The type of FixedList for EvaluatedTasks.</typeparam>
/// <param name="entity">The entity that is being acted upon.</param>
/// <param name="entityIndex">The index of the entity.</param>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="taskComponents">An array of task components.</param>
/// <param name="evaluatedTasks">The evaluated tasks list.</param>
/// <param name="evaluationType">The evaluation type.</param>
/// <param name="maxEvaluationCount">The maximum evaluation count.</param>
/// <param name="entityCommandBuffer">The command buffer for setting component data.</param>
/// <param name="results">The computed results array.</param>
[BurstCompile]
public static void DetermineEvaluation<TFixedList>(Entity entity, int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, DynamicBuffer<TaskComponent> taskComponents, ref TFixedList evaluatedTasks, EvaluationType evaluationType, ushort maxEvaluationCount, EntityCommandBuffer.ParallelWriter entityCommandBuffer, NativeArray<bool> results) where TFixedList : struct, INativeList<ulong>
{
results[0] = true; // The first element indicates that the job has been executed.
// No branches may be active.
var active = false;
var evaluate = false;
var evaluatedMask = new FixedList4096Bytes<ulong>();
for (int i = 0; i < branchComponents.Length; ++i) {
var branchComponent = branchComponents[i];
if (branchComponent.ActiveIndex == ushort.MaxValue || !branchComponent.CanExecute) {
continue;
}
active = true;
// Interrupts are processed in a separate system that is run outside of the task execution system. As a result the branch should not continue to evaluate.
if (branchComponent.InterruptType != InterruptType.None) {
continue;
}
var taskComponent = taskComponents[branchComponent.ActiveIndex];
var isParentTask = EvaluationUtility.IsParentTask(ref taskComponents, branchComponent.ActiveIndex);
// The branch can evaluate if the active task is an outer node (action or conditional) and is not running OR
// the task is an inner node (composite or decorator), is running, and is not a parallel task. Parent tasks cannot run without an active child.
if ((!isParentTask && taskComponent.Status != TaskStatus.Running && taskComponent.ParentIndex != ushort.MaxValue) ||
(isParentTask && (taskComponent.Status == TaskStatus.Queued || taskComponent.Status == TaskStatus.Running))) {
// Compute active task bit positions.
var bitIndex = branchComponent.ActiveIndex + 1;
var arrayIndex = bitIndex / ComponentUtility.ulongBitSize;
var bitInUlong = bitIndex % ComponentUtility.ulongBitSize;
while (evaluatedMask.Length <= arrayIndex) evaluatedMask.Add(0UL);
evaluatedMask[arrayIndex] |= (1UL << bitInUlong);
// Prevent evaluating the same task again within the same tick.
if (branchComponent.ActiveIndex == branchComponent.LastActiveIndex) {
branchComponent.CanExecute = false;
branchComponents.ElementAt(i) = branchComponent;
continue;
}
// Check if the task has already been evaluated this tick.
var alreadyEvaluated = (evaluatedTasks[arrayIndex] & (1UL << bitInUlong)) != 0;
// Decision to evaluate:
// - For parent tasks: always evaluate. The parent task should never be the last executing task.
// - For non-parent tasks: evaluate if this task hasn't been evaluated yet.
if (isParentTask || !alreadyEvaluated) {
evaluate = true;
branchComponent.LastActiveIndex = branchComponent.ActiveIndex;
} else {
branchComponent.CanExecute = false;
}
branchComponents.ElementAt(i) = branchComponent;
evaluatedTasks[arrayIndex] |= evaluatedMask[arrayIndex];
} else {
branchComponent.CanExecute = false;
branchComponents.ElementAt(i) = branchComponent;
}
}
// If a branch is active then at least one task within that branch is active.
if (active) {
results[1] = true; // Active result.
if (evaluate) {
if (evaluationType == EvaluationType.Count) {
// Use the last element of EvaluatedTasks as the counter.
evaluatedTasks[evaluatedTasks.Length - 1]++;
if (evaluatedTasks[evaluatedTasks.Length - 1] >= maxEvaluationCount) {
// Reset the counter and bitmask elements.
for (int i = 0; i < evaluatedTasks.Length; ++i) {
evaluatedTasks[i] = 0;
}
entityCommandBuffer.SetComponentEnabled<EvaluateFlag>(entityIndex, entity, false);
// Set the bitmask for current active tasks to prevent one extra task from being executed on subsequent frames.
SetActiveBranchBits(ref branchComponents, ref evaluatedTasks);
} else {
results[2] = true; // Evaluate result.
}
} else {
results[2] = true; // Evaluate result - continue the loop.
}
} else {
entityCommandBuffer.SetComponentEnabled<EvaluateFlag>(entityIndex, entity, false);
// Reset the evaluated tasks bitmask.
for (int i = 0; i < evaluatedTasks.Length; ++i) {
evaluatedTasks[i] = 0;
}
// The system is going to stop evaluating this entity. It will be resumed immediately the next update. Because the DetermineEvaluationJob is run after the tasks
// update the EvaluatedTasks value should be set to the next active task. If this value is set to 0 then one extra task will always be executed with subsequent frames.
SetActiveBranchBits(ref branchComponents, ref evaluatedTasks);
}
}
}
/// <summary>
/// Sets the bitmask bits for all active branches. This prevents one extra task from being executed on subsequent frames.
/// </summary>
/// <param name="branchComponents">An array of branch components.</param>
/// <param name="evaluatedTasks">The evaluated tasks list to update.</param>
[BurstCompile]
private static void SetActiveBranchBits<TFixedList>(ref DynamicBuffer<BranchComponent> branchComponents, ref TFixedList evaluatedTasks) where TFixedList : struct, INativeList<ulong>
{
for (int i = 0; i < branchComponents.Length; ++i) {
var branchComponent = branchComponents[i];
if (branchComponent.ActiveIndex == ushort.MaxValue) {
continue;
}
// Compute active task bit positions.
var bitIndex = branchComponent.ActiveIndex + 1;
var arrayIndex = bitIndex / ComponentUtility.ulongBitSize;
var bitInUlong = bitIndex % ComponentUtility.ulongBitSize;
evaluatedTasks[arrayIndex] |= (1UL << bitInUlong);
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0b7936234bfdce042b052d68dd0136f1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 72fd968959b610e4bb7994fedbaab0c9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions
{
using Opsive.GraphDesigner.Runtime;
/// <summary>
/// A TaskObject implementation of the Action task.
/// </summary>
[NodeIcon("3bbdfa553da4d554e9d74f8d88915aac", "6437308e972f99f48953f20198fd4e94")]
public abstract class Action : Task, IAction
{
}
}
#endif

View File

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

View File

@@ -0,0 +1,31 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions
{
using Opsive.GraphDesigner.Runtime;
using UnityEngine;
/// <summary>
/// A TaskObject implementation of the Action task. This class can be used when the task should not be grouped by the StackedAction task.
/// </summary>
[NodeIcon("3bbdfa553da4d554e9d74f8d88915aac", "6437308e972f99f48953f20198fd4e94")]
public abstract class ActionNode : Task, ITreeLogicNode, IAction
{
[Tooltip("The index of the node.")]
[SerializeField] ushort m_Index;
[Tooltip("The parent index of the node. ushort.MaxValue indicates no parent.")]
[SerializeField] ushort m_ParentIndex;
[Tooltip("The sibling index of the node. ushort.MaxValue indicates no sibling.")]
[SerializeField] ushort m_SiblingIndex;
public ushort Index { get => m_Index; set => m_Index = value; }
public ushort ParentIndex { get => m_ParentIndex; set => m_ParentIndex = value; }
public ushort SiblingIndex { get => m_SiblingIndex; set => m_SiblingIndex = value; }
public ushort RuntimeIndex { get; set; }
}
}
#endif

View File

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a57b0c8b5bd7c5142963151eb3723e6e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a boolean value to a float value (true = 1.0, false = 0.0).")]
[Shared.Utility.Category("Conversions")]
public class ConvertBoolToFloat : Action
{
[Tooltip("The boolean value to convert.")]
[SerializeField] protected SharedVariable<bool> m_Value;
[Tooltip("The variable that should be set to the converted float value.")]
[RequireShared] [SerializeField] protected SharedVariable<float> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = m_Value.Value ? 1.0f : 0.0f;
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a boolean value to an integer value (true = 1, false = 0).")]
[Shared.Utility.Category("Conversions")]
public class ConvertBoolToInt : Action
{
[Tooltip("The boolean value to convert.")]
[SerializeField] protected SharedVariable<bool> m_Value;
[Tooltip("The variable that should be set to the converted integer value.")]
[RequireShared] [SerializeField] protected SharedVariable<int> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = m_Value.Value ? 1 : 0;
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a boolean value to a string value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertBoolToString : Action
{
[Tooltip("The boolean value to convert.")]
[SerializeField] protected SharedVariable<bool> m_Value;
[Tooltip("The variable that should be set to the converted string value.")]
[RequireShared] [SerializeField] protected SharedVariable<string> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = m_Value.Value.ToString();
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a float value to a boolean value (0.0 = false, non-zero = true).")]
[Shared.Utility.Category("Conversions")]
public class ConvertFloatToBool : Action
{
[Tooltip("The float value to convert.")]
[SerializeField] protected SharedVariable<float> m_Value;
[Tooltip("The variable that should be set to the converted boolean value.")]
[RequireShared] [SerializeField] protected SharedVariable<bool> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = m_Value.Value != 0.0f;
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a float value to an integer value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertFloatToInt : Action
{
[Tooltip("The float value to convert.")]
[SerializeField] protected SharedVariable<float> m_Value;
[Tooltip("The variable that should be set to the converted integer value.")]
[RequireShared] [SerializeField] protected SharedVariable<int> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = Mathf.RoundToInt(m_Value.Value);
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a float value to a string value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertFloatToString : Action
{
[Tooltip("The float value to convert.")]
[SerializeField] protected SharedVariable<float> m_Value;
[Tooltip("The variable that should be set to the converted string value.")]
[RequireShared] [SerializeField] protected SharedVariable<string> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = m_Value.Value.ToString();
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,37 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a GameObject value to a Transform value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertGameObjectToTransform : Action
{
[Tooltip("The GameObject value to convert.")]
[SerializeField] protected SharedVariable<GameObject> m_Value;
[Tooltip("The variable that should be set to the converted Transform value.")]
[RequireShared] [SerializeField] protected SharedVariable<Transform> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (m_Value.Value != null) {
m_StoreResult.Value = m_Value.Value.transform;
return TaskStatus.Success;
}
m_StoreResult.Value = null;
return TaskStatus.Failure;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts an integer value to a boolean value (0 = false, non-zero = true).")]
[Shared.Utility.Category("Conversions")]
public class ConvertIntToBool : Action
{
[Tooltip("The integer value to convert.")]
[SerializeField] protected SharedVariable<int> m_Value;
[Tooltip("The variable that should be set to the converted boolean value.")]
[RequireShared] [SerializeField] protected SharedVariable<bool> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = m_Value.Value != 0;
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts an integer value to a float value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertIntToFloat : Action
{
[Tooltip("The integer value to convert.")]
[SerializeField] protected SharedVariable<int> m_Value;
[Tooltip("The variable that should be set to the converted float value.")]
[RequireShared] [SerializeField] protected SharedVariable<float> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = m_Value.Value;
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,33 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts an integer value to a string value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertIntToString : Action
{
[Tooltip("The integer value to convert.")]
[SerializeField] protected SharedVariable<int> m_Value;
[Tooltip("The variable that should be set to the converted string value.")]
[RequireShared] [SerializeField] protected SharedVariable<string> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_StoreResult.Value = m_Value.Value.ToString();
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,36 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a string value to a boolean value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertStringToBool : Action
{
[Tooltip("The string value to convert.")]
[SerializeField] protected SharedVariable<string> m_Value;
[Tooltip("The variable that should be set to the converted boolean value.")]
[RequireShared] [SerializeField] protected SharedVariable<bool> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (bool.TryParse(m_Value.Value, out bool result)) {
m_StoreResult.Value = result;
return TaskStatus.Success;
}
return TaskStatus.Failure;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,36 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a string value to a float value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertStringToFloat : Action
{
[Tooltip("The string value to convert.")]
[SerializeField] protected SharedVariable<string> m_Value;
[Tooltip("The variable that should be set to the converted float value.")]
[RequireShared] [SerializeField] protected SharedVariable<float> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (float.TryParse(m_Value.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float result)) {
m_StoreResult.Value = result;
return TaskStatus.Success;
}
return TaskStatus.Failure;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,36 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a string value to an integer value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertStringToInt : Action
{
[Tooltip("The string value to convert.")]
[SerializeField] protected SharedVariable<string> m_Value;
[Tooltip("The variable that should be set to the converted integer value.")]
[RequireShared] [SerializeField] protected SharedVariable<int> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (int.TryParse(m_Value.Value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out int result)) {
m_StoreResult.Value = result;
return TaskStatus.Success;
}
return TaskStatus.Failure;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,37 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Conversions
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Converts a Transform value to a GameObject value.")]
[Shared.Utility.Category("Conversions")]
public class ConvertTransformToGameObject : Action
{
[Tooltip("The Transform value to convert.")]
[SerializeField] protected SharedVariable<Transform> m_Value;
[Tooltip("The variable that should be set to the converted GameObject value.")]
[RequireShared] [SerializeField] protected SharedVariable<GameObject> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (m_Value.Value != null) {
m_StoreResult.Value = m_Value.Value.gameObject;
return TaskStatus.Success;
}
m_StoreResult.Value = null;
return TaskStatus.Failure;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 31447ed8785ef5146bbb37d6466c1954
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.UnityObjects
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using Opsive.Shared.Utility;
using UnityEngine;
[Opsive.Shared.Utility.Description("Adds the GameObject to the array.")]
[Shared.Utility.Category("Lists")]
public class AddGameObjectToArray : TargetGameObjectAction
{
[Tooltip("The list of possible GameObjects.")]
[RequireShared] [SerializeField] protected SharedVariable<GameObject[]> m_StoreResult;
[Tooltip("Are duplicates allowed to be added?")]
[SerializeField] protected SharedVariable<bool> m_AllowDuplicates = true;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (!m_AllowDuplicates.Value && m_StoreResult.Value.Contains(m_ResolvedGameObject)) {
return TaskStatus.Failure;
}
var array = m_StoreResult.Value;
if (array == null) {
array = new GameObject[1];
} else {
System.Array.Resize(ref array, array.Length + 1);
}
m_StoreResult.Value = array;
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,37 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.UnityObjects
{
using Opsive.GraphDesigner.Runtime.Variables;
using System.Collections.Generic;
using UnityEngine;
[Opsive.Shared.Utility.Description("Adds the GameObject to the list.")]
[Shared.Utility.Category("Lists")]
public class AddGameObjectToList : TargetGameObjectAction
{
[Tooltip("The list of possible GameObjects.")]
[RequireShared] [SerializeField] protected SharedVariable<List<GameObject>> m_StoreResult;
[Tooltip("Are duplicates allowed to be added?")]
[SerializeField] protected SharedVariable<bool> m_AllowDuplicates = true;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (!m_AllowDuplicates.Value && m_StoreResult.Value.Contains(m_ResolvedGameObject)) {
return TaskStatus.Failure;
}
m_StoreResult.Value.Add(m_ResolvedGameObject);
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,49 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.UnityObjects
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Sets a random GameObject value from the GameObject array.")]
[Shared.Utility.Category("Lists")]
public class RandomGameObjectFromArray : Action
{
[Tooltip("The list of possible GameObjects.")]
[SerializeField] protected SharedVariable<GameObject[]> m_GameObjects;
[Tooltip("The variable that should be set.")]
[RequireShared] [SerializeField] protected SharedVariable<GameObject> m_StoreResult;
[Tooltip("The seed of the random number generator. Set to 0 to disable.")]
[SerializeField] protected int m_Seed;
/// <summary>
/// Callback when the behavior tree is initialized.
/// </summary>
public override void OnAwake()
{
if (m_Seed != 0) {
Random.InitState(m_Seed);
}
}
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (m_GameObjects.Value == null || m_GameObjects.Value.Length == 0) {
return TaskStatus.Failure;
}
m_StoreResult.Value = m_GameObjects.Value[Random.Range(0, m_GameObjects.Value.Length)];
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,50 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.UnityObjects
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
using System.Collections.Generic;
[Opsive.Shared.Utility.Description("Sets a random GameObject value from the GameObject list.")]
[Shared.Utility.Category("Lists")]
public class RandomGameObjectFromList : Action
{
[Tooltip("The list of possible GameObjects.")]
[SerializeField] protected SharedVariable<List<GameObject>> m_GameObjects;
[Tooltip("The variable that should be set.")]
[RequireShared] [SerializeField] protected SharedVariable<GameObject> m_StoreResult;
[Tooltip("The seed of the random number generator. Set to 0 to disable.")]
[SerializeField] protected int m_Seed;
/// <summary>
/// Callback when the behavior tree is initialized.
/// </summary>
public override void OnAwake()
{
if (m_Seed != 0) {
Random.InitState(m_Seed);
}
}
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (m_GameObjects.Value == null || m_GameObjects.Value.Count == 0) {
return TaskStatus.Failure;
}
m_StoreResult.Value = m_GameObjects.Value[Random.Range(0, m_GameObjects.Value.Count)];
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,57 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.UnityObjects
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Removes the GameObject from the array.")]
[Shared.Utility.Category("Lists")]
public class RemoveGameObjectFromArray : TargetGameObjectAction
{
[Tooltip("The list of possible GameObjects.")]
[RequireShared] [SerializeField] protected SharedVariable<GameObject[]> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
var array = m_StoreResult.Value;
if (array == null) {
return TaskStatus.Failure;
}
// Find the index of the GameObject to remove.
var indexToRemove = -1;
for (int i = 0; i < array.Length; i++) {
if (array[i] == m_ResolvedGameObject) {
indexToRemove = i;
break;
}
}
if (indexToRemove == -1) {
return TaskStatus.Failure;
}
// Create a new array with the GameObject removed.
var newArray = new GameObject[array.Length - 1];
for (int i = 0, j = 0; i < array.Length; ++i) {
if (i != indexToRemove) {
newArray[j] = array[i];
++j;
}
}
m_StoreResult.Value = newArray;
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,31 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.UnityObjects
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using System.Collections.Generic;
using UnityEngine;
[Opsive.Shared.Utility.Description("Removes the GameObject from the list.")]
[Shared.Utility.Category("Lists")]
public class RemoveGameObjectFromList : TargetGameObjectAction
{
[Tooltip("The list of possible GameObjects.")]
[RequireShared] [SerializeField] protected SharedVariable<List<GameObject>> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
return m_StoreResult.Value.Remove(m_ResolvedGameObject) ? TaskStatus.Success : TaskStatus.Failure;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,39 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.UnityObjects
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Selects the GameObject from the array.")]
[Shared.Utility.Category("Lists")]
public class SelectGameObjectFromArray : Action
{
[Tooltip("The list of possible GameObjects.")]
[SerializeField] protected SharedVariable<GameObject[]> m_GameObjects;
[Tooltip("The index of the GameObject that should be selected.")]
[SerializeField] protected SharedVariable<int> m_ElementIndex;
[Tooltip("The selected GameObject.")]
[RequireShared] [SerializeField] protected SharedVariable<GameObject> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (m_GameObjects.Value == null || m_ElementIndex.Value < 0 || m_ElementIndex.Value > m_GameObjects.Value.Length) {
return TaskStatus.Failure;
}
m_StoreResult.Value = m_GameObjects.Value[m_ElementIndex.Value];
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,40 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.UnityObjects
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using System.Collections.Generic;
using UnityEngine;
[Opsive.Shared.Utility.Description("Selects the GameObject from the list.")]
[Shared.Utility.Category("Lists")]
public class SelectGameObjectFromList : Action
{
[Tooltip("The list of possible GameObjects.")]
[SerializeField] protected SharedVariable<List<GameObject>> m_GameObjects;
[Tooltip("The index of the GameObject that should be selected.")]
[SerializeField] protected SharedVariable<int> m_ElementIndex;
[Tooltip("The selected GameObject.")]
[RequireShared] [SerializeField] protected SharedVariable<GameObject> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (m_GameObjects.Value == null || m_ElementIndex.Value < 0 || m_ElementIndex.Value > m_GameObjects.Value.Count) {
return TaskStatus.Failure;
}
m_StoreResult.Value = m_GameObjects.Value[m_ElementIndex.Value];
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,96 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions
{
using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.GraphDesigner.Runtime;
using Unity.Entities;
using Unity.Burst;
using UnityEngine;
/// <summary>
/// A node representation of the idle task.
/// </summary>
[NodeIcon("fc4d1b83384913b4abfbd8455db6df5b", "79a6985a753bb244fb5b32dc0f26addb")]
[Opsive.Shared.Utility.Description("Returns a TaskStatus of running. The task will only stop when interrupted or a conditional abort is triggered.")]
public class Idle : ECSActionTask<IdleTaskSystem, IdleComponent>
{
/// <summary>
/// The type of tag that should be enabled when the task is running.
/// </summary>
public override ComponentType Flag { get => typeof(IdleFlag); }
/// <summary>
/// Returns a new TBufferElement for use by the system.
/// </summary>
/// <returns>A new TBufferElement for use by the system.</returns>
public override IdleComponent GetBufferElement()
{
return new IdleComponent() {
Index = RuntimeIndex
};
}
}
/// <summary>
/// The DOTS data structure for the Idle class.
/// </summary>
public struct IdleComponent : IBufferElementData
{
[Tooltip("The index of the node.")]
public ushort Index;
}
/// <summary>
/// A DOTS tag indicating when a Idle node is active.
/// </summary>
public struct IdleFlag : IComponentData, IEnableableComponent { }
/// <summary>
/// Runs the Idle logic.
/// </summary>
[DisableAutoCreation]
public partial struct IdleTaskSystem : ISystem
{
/// <summary>
/// Creates the job.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
var query = SystemAPI.QueryBuilder().WithAllRW<TaskComponent>().WithAll<IdleComponent, IdleFlag, EvaluateFlag>().Build();
state.Dependency = new IdleJob().ScheduleParallel(query, state.Dependency);
}
/// <summary>
/// Job which executes the task logic.
/// </summary>
[BurstCompile]
private partial struct IdleJob : IJobEntity
{
/// <summary>
/// Executes the idle logic.
/// </summary>
/// <param name="taskComponents">An array of TaskComponents.</param>
/// <param name="idleComponents">An array of IdleComponents.</param>
[BurstCompile]
public void Execute(ref DynamicBuffer<TaskComponent> taskComponents, ref DynamicBuffer<IdleComponent> idleComponents)
{
for (int i = 0; i < idleComponents.Length; ++i) {
var idleComponent = idleComponents[i];
var taskComponent = taskComponents[idleComponent.Index];
if (taskComponent.Status == TaskStatus.Queued) {
taskComponent.Status = TaskStatus.Running;
taskComponents[idleComponent.Index] = taskComponent;
}
}
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,34 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
/// <summary>
/// Logs the specified string.
/// </summary>
[NodeIcon("c97bee71424b3e247a161d1279643506", "138439e3588de5d449b7949d68d32ad8")]
[Opsive.Shared.Utility.Description("A simple task which will output the specified text and return success. It can be used for debugging.")]
public class Log : Action
{
[Tooltip("The string that should be outputted to the console.")]
[SerializeField] protected SharedVariable<string> m_Text;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
Debug.Log(m_Text.Value);
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,38 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
/// <summary>
/// Logs the specified value.
/// </summary>
[NodeIcon("c97bee71424b3e247a161d1279643506", "138439e3588de5d449b7949d68d32ad8")]
public class LogValue : Action
{
[Tooltip("The value that should be outputted to the console.")]
[RequireShared] [SerializeField] protected SharedVariable m_Value;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
if (m_Value == null || m_Value.Scope == SharedVariable.SharingScope.Empty) {
Debug.LogWarning("Warning: The LogValue.Value variable must be set.");
return TaskStatus.Failure;
}
Debug.Log(m_Value.GetValue());
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7177abfd9732b2d45aec5e96e4415d68
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Math
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Flips the value of the boolean.")]
[Shared.Utility.Category("Math")]
public class BoolFlip : Action
{
[Tooltip("The bool that should be flipped.")]
[SerializeField] protected SharedVariable<bool> m_Bool;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
m_Bool.Value = !m_Bool.Value;
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,61 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Math
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Performs a math operation on the two booleans.")]
[Shared.Utility.Category("Math")]
public class BoolOperator : Action
{
/// <summary>
/// Specifies the type of bool operation that should be performed.
/// </summary>
protected enum Operation
{
AND, // Returns the AND between two booleans.
OR, // Returns the OR between two booleans.
NAND, // Returns the NAND between two booleans.
XOR, // Returns the XOR between two booleans.
}
[Tooltip("The operation to perform.")]
[SerializeField] protected SharedVariable<Operation> m_Operation;
[Tooltip("The first boolean.")]
[SerializeField] protected SharedVariable<bool> m_Bool1;
[Tooltip("The second boolean.")]
[SerializeField] protected SharedVariable<bool> m_Bool2;
[Tooltip("The variable to store the result.")]
[RequireShared] [SerializeField] protected SharedVariable<bool> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
switch (m_Operation.Value) {
case Operation.AND:
m_StoreResult.Value = m_Bool1.Value && m_Bool2.Value;
break;
case Operation.OR:
m_StoreResult.Value = m_Bool1.Value || m_Bool2.Value;
break;
case Operation.NAND:
m_StoreResult.Value = !(m_Bool1.Value && m_Bool2.Value);
break;
case Operation.XOR:
m_StoreResult.Value = (m_Bool1.Value ^ m_Bool2.Value);
break;
}
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,73 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Math
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Performs a math operation on the two floats.")]
[Shared.Utility.Category("Math")]
public class FloatOperator : Action
{
/// <summary>
/// Specifies the type of float operation that should be performed.
/// </summary>
protected enum Operation
{
Add, // Returns the addition between two floats.
Subtract, // Returns the division between two floats.
Multiply, // Returns the multiplication between two floats.
Divide, // Returns the division between two floats.
Modulo, // Returns the modulo between two floats.
Min, // Returns the minimum of two floats.
Max, // Returns the maximum of two floats.
}
[Tooltip("The operation to perform.")]
[SerializeField] protected SharedVariable<Operation> m_Operation;
[Tooltip("The first float.")]
[SerializeField] protected SharedVariable<float> m_Float1;
[Tooltip("The second float.")]
[SerializeField] protected SharedVariable<float> m_Float2;
[Tooltip("The variable to store the result.")]
[RequireShared] [SerializeField] protected SharedVariable<float> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
switch (m_Operation.Value) {
case Operation.Add:
m_StoreResult.Value = m_Float1.Value + m_Float2.Value;
break;
case Operation.Subtract:
m_StoreResult.Value = m_Float1.Value - m_Float2.Value;
break;
case Operation.Multiply:
m_StoreResult.Value = m_Float1.Value * m_Float2.Value;
break;
case Operation.Divide:
m_StoreResult.Value = m_Float1.Value / m_Float2.Value;
break;
case Operation.Modulo:
m_StoreResult.Value = m_Float1.Value % m_Float2.Value;
break;
case Operation.Min:
m_StoreResult.Value = Mathf.Min(m_Float1.Value, m_Float2.Value);
break;
case Operation.Max:
m_StoreResult.Value = Mathf.Max(m_Float1.Value, m_Float2.Value);
break;
}
return TaskStatus.Success;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,73 @@
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Math
{
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
[Opsive.Shared.Utility.Description("Performs a math operation on the two integers.")]
[Shared.Utility.Category("Math")]
public class IntOperator : Action
{
/// <summary>
/// Specifies the type of int operation that should be performed.
/// </summary>
protected enum Operation
{
Add, // Returns the addition between two integers.
Subtract, // Returns the division between two integers.
Multiply, // Returns the multiplication between two integers.
Divide, // Returns the division between two integers.
Modulo, // Returns the modulo between two integers.
Min, // Returns the minimum of two integers.
Max, // Returns the maximum of two integers.
}
[Tooltip("The operation to perform.")]
[SerializeField] protected SharedVariable<Operation> m_Operation;
[Tooltip("The first integer.")]
[SerializeField] protected SharedVariable<int> m_Integer1;
[Tooltip("The second integer.")]
[SerializeField] protected SharedVariable<int> m_Integer2;
[Tooltip("The variable to store the result.")]
[RequireShared] [SerializeField] protected SharedVariable<int> m_StoreResult;
/// <summary>
/// Executes the task.
/// </summary>
/// <returns>The execution status of the task.</returns>
public override TaskStatus OnUpdate()
{
switch (m_Operation.Value) {
case Operation.Add:
m_StoreResult.Value = m_Integer1.Value + m_Integer2.Value;
break;
case Operation.Subtract:
m_StoreResult.Value = m_Integer1.Value - m_Integer2.Value;
break;
case Operation.Multiply:
m_StoreResult.Value = m_Integer1.Value * m_Integer2.Value;
break;
case Operation.Divide:
m_StoreResult.Value = m_Integer1.Value / m_Integer2.Value;
break;
case Operation.Modulo:
m_StoreResult.Value = m_Integer1.Value % m_Integer2.Value;
break;
case Operation.Min:
m_StoreResult.Value = Mathf.Min(m_Integer1.Value, m_Integer2.Value);
break;
case Operation.Max:
m_StoreResult.Value = Mathf.Max(m_Integer1.Value, m_Integer2.Value);
break;
}
return TaskStatus.Success;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77d11b9520f296b43a19d4a987844d73
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