using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Utility;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.GraphDesigner.Runtime;
using Opsive.Shared.Utility;
using Unity.Collections;
using Unity.Entities;
using Unity.Burst;
using UnityEngine;
using System;
using System.Collections.Generic;
using Opsive.GraphDesigner.Runtime.Variables;
namespace Cielonos.MainGame.Characters.AI
{
///
/// 用于持有浮点权重的不可变 Blob 数据结构
///
public struct FloatBlob {
public BlobArray Weights;
}
[NodeIcon("Assets/Sprites/Icon/Dice.png")]
[Opsive.Shared.Utility.Description("类似于 RandomSelector(随机选择器),但允许为其每个子节点配置不同的出现权重。权重越大的节点越容易被优先执行。\n如果有节点返回 Failure,会依据排好的权重顺序继续尝试下一个节点。\n如果所有子节点的权重加起来全都是 0,将退化为均匀随机分布。")]
[Category("Cielonos")]
public class WeightedSelector : ECSCompositeTask, IParentNode, IConditionalAbortParent, IInterruptResponder, ISavableTask, ICloneable
{
[Tooltip("子节点条件预检与中断重估策略。")]
[SerializeField] ConditionalAbortType m_AbortType;
[Tooltip("随机生成种子,0 表示使用实体的索引作为默认种子。")]
[SerializeField] uint m_Seed;
[Tooltip("配置各子节点的出现权重 (从左到右匹配子节点,如果列表长度不够,超出的子节点权重默认为 1.0)。")]
[SerializeField] List> m_Weights = new List>();
private ushort m_ComponentIndex;
public ConditionalAbortType AbortType { get => m_AbortType; set => m_AbortType = value; }
public uint Seed { get => m_Seed; set => m_Seed = value; }
public List> Weights { get => m_Weights; set => m_Weights = value; }
public override ComponentType Flag { get => typeof(WeightedSelectorFlag); }
public Type InterruptSystemType { get => typeof(WeightedSelectorInterruptSystem); }
public override WeightedSelectorComponent GetBufferElement()
{
return new WeightedSelectorComponent()
{
Index = RuntimeIndex,
Seed = m_Seed,
};
}
public override int AddBufferElement(World world, Entity entity, GameObject gameObject)
{
m_ComponentIndex = (ushort)base.AddBufferElement(world, entity, gameObject);
var components = world.EntityManager.GetBuffer(entity);
var component = components[m_ComponentIndex];
// 将 OOP 端配置的权重转换为 DOTS 高性能的 Blob 传给 System
if (m_Weights != null && m_Weights.Count > 0) {
var builder = new BlobBuilder(Allocator.Temp);
ref var root = ref builder.ConstructRoot();
var array = builder.Allocate(ref root.Weights, m_Weights.Count);
for (int i = 0; i < m_Weights.Count; i++)
{
array[i] = m_Weights[i].Value;
}
component.WeightsBlob = builder.CreateBlobAssetReference(Allocator.Persistent);
builder.Dispose();
}
components[m_ComponentIndex] = component;
return m_ComponentIndex;
}
public MemberVisibility GetSaveReflectionType(int index) { return MemberVisibility.None; }
public object Save(World world, Entity entity)
{
var components = world.EntityManager.GetBuffer(entity);
var component = components[m_ComponentIndex];
var saveData = new object[2];
saveData[0] = component.ActiveRelativeChildIndex;
if (component.TaskOrder.IsCreated) {
var taskOrder = component.TaskOrder.Value.Indicies.ToArray();
saveData[1] = taskOrder;
}
return saveData;
}
public void Load(object saveData, World world, Entity entity)
{
var components = world.EntityManager.GetBuffer(entity);
var component = components[m_ComponentIndex];
var taskSaveData = (object[])saveData;
component.ActiveRelativeChildIndex = (ushort)taskSaveData[0];
if (taskSaveData[1] != null) {
var taskOrder = (ushort[])taskSaveData[1];
var builder = new BlobBuilder(Allocator.Temp);
ref var root = ref builder.ConstructRoot();
var orderArray = builder.Allocate(ref root.Indicies, taskOrder.Length);
for (int i = 0; i < taskOrder.Length; i++) {
orderArray[i] = taskOrder[i];
}
component.TaskOrder = builder.CreateBlobAssetReference(Allocator.Persistent);
builder.Dispose();
}
components[m_ComponentIndex] = component;
}
public object Clone()
{
var clone = Activator.CreateInstance();
clone.Index = Index;
clone.ParentIndex = ParentIndex;
clone.SiblingIndex = SiblingIndex;
clone.AbortType = AbortType;
if (m_Weights != null) clone.m_Weights = new List>(m_Weights);
return clone;
}
}
///
/// WeightedSelector 的 ECS 数据持有者。
///
public struct WeightedSelectorComponent : IBufferElementData
{
public ushort Index;
public ushort ActiveRelativeChildIndex;
public uint Seed;
public Unity.Mathematics.Random RandomNumberGenerator;
public BlobAssetReference TaskOrder;
public BlobAssetReference WeightsBlob;
}
public struct WeightedSelectorFlag : IComponentData, IEnableableComponent { }
///
/// WeightedSelector 核心逻辑调度系统,在 Burst 引擎内超高速执行无放回加权随机构建。
///
[DisableAutoCreation]
public partial struct WeightedSelectorTaskSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
foreach (var (branchComponents, taskComponents, selectorComponents, entity) in
SystemAPI.Query, DynamicBuffer, DynamicBuffer>().WithAll().WithEntityAccess()) {
for (int i = 0; i < selectorComponents.Length; ++i) {
var component = selectorComponents[i];
var taskComponent = taskComponents[component.Index];
var branchComponent = branchComponents[taskComponent.BranchIndex];
// 分支被中断,或当前不可执行的情况下跳过
if (branchComponent.InterruptType != InterruptType.None || !branchComponent.CanExecute) {
continue;
}
var branchComponentsBuffer = branchComponents;
var taskComponentsBuffer = taskComponents;
var selectorComponentsBuffer = selectorComponents;
// 当该节点刚刚轮入执行队列时...
if (taskComponent.Status == TaskStatus.Queued) {
taskComponent.Status = TaskStatus.Running;
taskComponentsBuffer[taskComponent.Index] = taskComponent;
if (!component.TaskOrder.IsCreated) {
var childCount = TraversalUtility.GetImmediateChildCount(ref taskComponent, ref taskComponentsBuffer);
var builder = new BlobBuilder(Allocator.Temp);
ref var root = ref builder.ConstructRoot();
builder.Allocate(ref root.Indicies, childCount);
component.TaskOrder = builder.CreateBlobAssetReference(Allocator.Persistent);
builder.Dispose();
}
if (component.RandomNumberGenerator.state == 0) {
component.RandomNumberGenerator = Unity.Mathematics.Random.CreateFromIndex(component.Seed != 0 ? component.Seed : (uint)entity.Index);
}
var childCountOriginal = component.TaskOrder.Value.Indicies.Length;
// 由于 DOTS/Burst 中禁止开辟 GC 堆内存,这里使用 Temp 级别的 NativeArray 处理随机抽取序列
var tempIndices = new NativeArray(childCountOriginal, Allocator.Temp);
var tempWeights = new NativeArray(childCountOriginal, Allocator.Temp);
var tmpChildIndex = taskComponent.Index + 1;
for (int j = 0; j < childCountOriginal; ++j) {
tempIndices[j] = (ushort)tmpChildIndex;
if (component.WeightsBlob.IsCreated && j < component.WeightsBlob.Value.Weights.Length) {
tempWeights[j] = component.WeightsBlob.Value.Weights[j];
} else {
// 配置中缺失权重位时,兜底给该节点 1.0f 的权重
tempWeights[j] = 1f;
}
// 借助 SiblingIndex 在内存中穿梭跃迁到下一个直系子节点
tmpChildIndex = taskComponentsBuffer[tmpChildIndex].SiblingIndex;
}
ref var initialTaskOrder = ref component.TaskOrder.Value.Indicies;
// 真正的无放回加权打乱算法 (Weighted Shuffle without replacement)
for (int j = 0; j < childCountOriginal; j++) {
int remaining = childCountOriginal - j;
float totWeight = 0;
for (int k = 0; k < remaining; k++) totWeight += tempWeights[k];
int picked = 0;
if (totWeight > 0f) {
float r = component.RandomNumberGenerator.NextFloat(0, totWeight);
float cum = 0;
for (int k = 0; k < remaining; k++) {
cum += tempWeights[k];
if (r <= cum || k == remaining - 1) {
picked = k;
break;
}
}
} else {
// 退化情况:所有子节点加起来权重等于 0,那就按纯平均随机去决定
float r = component.RandomNumberGenerator.NextFloat();
picked = (int)Unity.Mathematics.math.floor(r * remaining);
if (picked >= remaining) picked = remaining - 1;
}
// 将抽中的原始子节点 ID 写入最终被执行的序列当中
initialTaskOrder[j] = tempIndices[picked];
// 高效将挑剩下的最后一个元素盖到被抽走的位置上,缩小编历范围
tempIndices[picked] = tempIndices[remaining - 1];
tempWeights[picked] = tempWeights[remaining - 1];
}
// Burst Compile 中必须手动解除 NativeArray 分配的内存
tempIndices.Dispose();
tempWeights.Dispose();
component.ActiveRelativeChildIndex = 0;
selectorComponentsBuffer[i] = component;
branchComponent.NextIndex = initialTaskOrder[component.ActiveRelativeChildIndex];
branchComponentsBuffer[taskComponent.BranchIndex] = branchComponent;
var nextChildTaskComponent = taskComponentsBuffer[branchComponent.NextIndex];
nextChildTaskComponent.Status = TaskStatus.Queued;
taskComponentsBuffer[branchComponent.NextIndex] = nextChildTaskComponent;
} else if (taskComponent.Status != TaskStatus.Running) {
continue;
}
// System 轮询期间:监控当前派发的子节点状况
ref var taskOrder = ref component.TaskOrder.Value.Indicies;
var childTaskComponent = taskComponentsBuffer[taskOrder[component.ActiveRelativeChildIndex]];
if (childTaskComponent.Status == TaskStatus.Queued || childTaskComponent.Status == TaskStatus.Running) {
continue; // 子节点还在挣扎,继续等
}
if (component.ActiveRelativeChildIndex == taskOrder.Length - 1 || childTaskComponent.Status == TaskStatus.Success) {
// 子节点全试完但都崩了,或者是只要有一个成功了,我们就宣告成功/收敛失败
taskComponent.Status = childTaskComponent.Status != TaskStatus.Inactive ? childTaskComponent.Status : TaskStatus.Failure;
component.ActiveRelativeChildIndex = 0;
taskComponentsBuffer[component.Index] = taskComponent;
branchComponent.NextIndex = taskComponent.ParentIndex;
branchComponentsBuffer[taskComponent.BranchIndex] = branchComponent;
} else {
// 刚刚那个子节点失败了,按排好的权重序列推进,尝试下一个
component.ActiveRelativeChildIndex++;
var nextIndex = taskOrder[component.ActiveRelativeChildIndex];
var nextTaskComponent = taskComponentsBuffer[nextIndex];
nextTaskComponent.Status = TaskStatus.Queued;
taskComponentsBuffer[nextIndex] = nextTaskComponent;
branchComponent.NextIndex = nextIndex;
branchComponentsBuffer[taskComponent.BranchIndex] = branchComponent;
}
selectorComponentsBuffer[i] = component;
}
}
}
public void OnDestroy(ref SystemState state)
{
foreach (var selectorComponents in SystemAPI.Query>()) {
for (int i = 0; i < selectorComponents.Length; ++i) {
var component = selectorComponents[i];
if (component.TaskOrder.IsCreated) {
component.TaskOrder.Dispose();
}
if (component.WeightsBlob.IsCreated) {
component.WeightsBlob.Dispose();
}
}
}
}
}
[DisableAutoCreation]
public partial struct WeightedSelectorInterruptSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
foreach (var (taskComponents, selectorComponents) in
SystemAPI.Query, DynamicBuffer>().WithAll()) {
for (int i = 0; i < selectorComponents.Length; ++i) {
var component = selectorComponents[i];
var taskComponent = taskComponents[component.Index];
if (taskComponent.Status == TaskStatus.Running && taskComponents[component.TaskOrder.Value.Indicies[component.ActiveRelativeChildIndex]].Status != TaskStatus.Running) {
ushort relativeChildIndex = 0;
int maxChildren = component.TaskOrder.Value.Indicies.Length;
while (relativeChildIndex < maxChildren && taskComponents[component.TaskOrder.Value.Indicies[relativeChildIndex]].Status != TaskStatus.Running) {
relativeChildIndex++;
}
if (relativeChildIndex < maxChildren) {
component.ActiveRelativeChildIndex = relativeChildIndex;
var selectorComponentsBuffer = selectorComponents;
selectorComponentsBuffer[i] = component;
}
}
}
}
}
}
}