재귀 트리를 반복 트리로 변경
This commit is contained in:
parent
4e73a15afd
commit
78556c08ca
BIN
Assets/_DDD/_Addressables/AI/Customer/Subtree/CustomerDefault.asset
(Stored with Git LFS)
BIN
Assets/_DDD/_Addressables/AI/Customer/Subtree/CustomerDefault.asset
(Stored with Git LFS)
Binary file not shown.
BIN
Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset
(Stored with Git LFS)
BIN
Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset
(Stored with Git LFS)
Binary file not shown.
BIN
Assets/_DDD/_Addressables/AI/Customer/Subtree/OrderSubtree.asset
(Stored with Git LFS)
BIN
Assets/_DDD/_Addressables/AI/Customer/Subtree/OrderSubtree.asset
(Stored with Git LFS)
Binary file not shown.
@ -0,0 +1,121 @@
|
||||
using Unity.Entities;
|
||||
using Opsive.BehaviorDesigner.Runtime;
|
||||
using Opsive.BehaviorDesigner.Runtime.Components;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks.Decorators;
|
||||
using Opsive.GraphDesigner.Runtime.Variables;
|
||||
using Opsive.Shared.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class LoopOnSuccess : DecoratorNode
|
||||
{
|
||||
[Tooltip("무한 반복 여부")]
|
||||
[SerializeField] private SharedVariable<bool> m_RepeatForever = true;
|
||||
|
||||
[Tooltip("반복 횟수(RepeatForever가 false일 때만 유효). Success를 반환한 횟수를 기준으로 카운트합니다.")]
|
||||
[SerializeField] private SharedVariable<int> m_RepeatCount;
|
||||
|
||||
[Tooltip("자식이 Failure면 즉시 종료할지 여부")]
|
||||
[SerializeField] private SharedVariable<bool> m_EndOnFailure = true;
|
||||
|
||||
public SharedVariable<bool> RepeatForever
|
||||
{
|
||||
get => m_RepeatForever;
|
||||
set => m_RepeatForever = value;
|
||||
}
|
||||
|
||||
public SharedVariable<int> RepeatCount
|
||||
{
|
||||
get => m_RepeatCount;
|
||||
set => m_RepeatCount = value;
|
||||
}
|
||||
|
||||
public SharedVariable<bool> EndOnFailure
|
||||
{
|
||||
get => m_EndOnFailure;
|
||||
set => m_EndOnFailure = value;
|
||||
}
|
||||
|
||||
private uint m_CurrentSuccessCount;
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
m_CurrentSuccessCount = 0;
|
||||
}
|
||||
|
||||
public override TaskStatus OnUpdate()
|
||||
{
|
||||
// 자식(TaskComponent)은 Decorator 바로 다음 인덱스로 가정
|
||||
var taskBuffer = m_BehaviorTree.World.EntityManager.GetBuffer<TaskComponent>(m_BehaviorTree.Entity);
|
||||
|
||||
var childStatus = taskBuffer[Index + 1].Status;
|
||||
|
||||
// 자식이 아직 실행 중이면 대기
|
||||
if (childStatus is TaskStatus.Running or TaskStatus.Queued)
|
||||
{
|
||||
return TaskStatus.Running;
|
||||
}
|
||||
|
||||
// 실패 처리: 옵션에 따라 즉시 종료(전파)하거나 상태 그대로 반환
|
||||
if (childStatus == TaskStatus.Failure)
|
||||
{
|
||||
return TaskStatus.Failure;
|
||||
}
|
||||
|
||||
// 반복 한도 체크(무한 반복이 아니면 카운트 기반으로 제한)
|
||||
if (!m_RepeatForever.Value && m_CurrentSuccessCount >= m_RepeatCount.Value)
|
||||
{
|
||||
// 한도에 도달했으면 최종적으로 Success 반환
|
||||
return TaskStatus.Success;
|
||||
}
|
||||
|
||||
if (childStatus == TaskStatus.Success)
|
||||
{
|
||||
ResetSubtreeToQueued(taskBuffer);
|
||||
}
|
||||
|
||||
// 계속 반복
|
||||
m_CurrentSuccessCount++;
|
||||
return TaskStatus.Running;
|
||||
}
|
||||
|
||||
public override MemberVisibility GetSaveReflectionType(int index) => MemberVisibility.None;
|
||||
|
||||
public override object Save(World world, Entity entity)
|
||||
{
|
||||
return m_CurrentSuccessCount;
|
||||
}
|
||||
|
||||
public override void Load(object saveData, World world, Entity entity)
|
||||
{
|
||||
m_CurrentSuccessCount = (uint)saveData;
|
||||
}
|
||||
|
||||
private void ResetSubtreeToQueued(DynamicBuffer<TaskComponent> tasks)
|
||||
{
|
||||
for (int i = Index + 1; i < tasks.Length; i++)
|
||||
{
|
||||
if (!IsDescendantOf(tasks, i)) continue;
|
||||
var t = tasks[i];
|
||||
t.Status = TaskStatus.Inactive;
|
||||
// Composite 내부 상태(현재 자식 인덱스 등) 별도 필드가 있다면 여기서 초기화 필요
|
||||
tasks[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsDescendantOf(DynamicBuffer<TaskComponent> tasks, int nodeIndex)
|
||||
{
|
||||
int p = tasks[nodeIndex].ParentIndex;
|
||||
while (p > Index)
|
||||
{
|
||||
p = tasks[p].ParentIndex;
|
||||
}
|
||||
return p == Index;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04dcb4b1d9cf4fcd8d2b88f0e86bc9f4
|
||||
timeCreated: 1756789547
|
@ -7,6 +7,7 @@ namespace DDD.Restaurant
|
||||
{
|
||||
public enum EvaluationStep
|
||||
{
|
||||
None,
|
||||
FoodSatisfactionCheck,
|
||||
FavoriteTasteCheck,
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
using Opsive.BehaviorDesigner.Runtime.Components;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks.Conditionals;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks.Decorators;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public class CheckCumulateOrderCount : Conditional
|
||||
public class CheckCumulateOrderCount : DecoratorNode
|
||||
{
|
||||
public enum ComparisonType
|
||||
{
|
||||
@ -18,16 +20,33 @@ public enum ComparisonType
|
||||
|
||||
[SerializeField] private int _compareOrderCount;
|
||||
[SerializeField] private ComparisonType _comparisonType;
|
||||
public override TaskStatus OnUpdate()
|
||||
private TaskStatus _evaluateResult;
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
var blackboard = gameObject.GetComponent<IAISharedBlackboard<RestaurantCustomerBlackboardKey>>();
|
||||
if (blackboard == null)
|
||||
{
|
||||
Debug.LogWarning($"블랙보드가 존재하지 않음 해시코드: {gameObject.GetHashCode()}");
|
||||
return TaskStatus.Failure;
|
||||
_evaluateResult = TaskStatus.Failure;
|
||||
return;
|
||||
}
|
||||
var cumulativeOrderCount = blackboard.GetBlackboardValue<int>(RestaurantCustomerBlackboardKey.CumulativeOrderCount);
|
||||
return Evaluate(cumulativeOrderCount) ? TaskStatus.Success : TaskStatus.Failure;
|
||||
_evaluateResult = Evaluate(cumulativeOrderCount) ? TaskStatus.Success : TaskStatus.Failure;
|
||||
}
|
||||
public override TaskStatus OnUpdate()
|
||||
{
|
||||
if (_evaluateResult == TaskStatus.Failure) return TaskStatus.Failure;
|
||||
|
||||
var taskComponents = m_BehaviorTree.World.EntityManager.GetBuffer<TaskComponent>(m_BehaviorTree.Entity);
|
||||
ref var child = ref taskComponents.ElementAt(Index + 1);
|
||||
|
||||
if (child.Status is TaskStatus.Success or TaskStatus.Failure)
|
||||
{
|
||||
return child.Status;
|
||||
}
|
||||
|
||||
return TaskStatus.Running;
|
||||
}
|
||||
|
||||
private bool Evaluate(int compareValue)
|
@ -104,6 +104,13 @@ public RestaurantOrderType GetInteractionSubsystemType()
|
||||
|
||||
public void SetInteractionSubsystemType(RestaurantOrderType inValue)
|
||||
{
|
||||
if (inValue == _currentRestaurantOrderType)
|
||||
{
|
||||
// Broadcast OnRestaurantOrderPhaseChanged
|
||||
// Customer-WaitForAvailableOrder -> OnStart -> OnRestaurantOrderPhaseChanged bind, OnEnd-> Unbind
|
||||
// Customer handling -> if Wait, am I first in queue? then, ...
|
||||
return;
|
||||
}
|
||||
_currentRestaurantOrderType = inValue;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,8 @@ private void Start()
|
||||
var canvas = GetComponentInChildren<Canvas>();
|
||||
canvas.worldCamera = Camera.main;
|
||||
|
||||
_patienceSlider = GetComponentInChildren<Slider>();
|
||||
_patienceSlider = GetComponentInChildren<Slider>(true);
|
||||
_patienceSlider.gameObject.SetActive(false);
|
||||
if (_patienceSlider == null)
|
||||
{
|
||||
Debug.LogWarning($"[{GetType().Name}] 슬라이더가 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}");
|
||||
@ -34,35 +35,43 @@ private void Start()
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeSubsystem();
|
||||
}
|
||||
|
||||
private void InitializeSubsystem()
|
||||
{
|
||||
var targetObject = _blackboard.GetBlackboardValue<GameObject>(RestaurantCustomerBlackboardKey.CurrentTargetGameObject);
|
||||
if (targetObject == null)
|
||||
{
|
||||
Debug.LogWarning($"[{GetType().Name}] 타겟 오브젝트가 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}");
|
||||
//Debug.LogWarning($"[{GetType().Name}] 타겟 오브젝트가 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetObject.TryGetComponent<IInteractionSubsystemOwner>(out var subsystemOwner))
|
||||
{
|
||||
Debug.LogWarning($"[{GetType().Name}] 서브시스템 오너가 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}");
|
||||
//Debug.LogWarning($"[{GetType().Name}] 서브시스템 오너가 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}");
|
||||
return;
|
||||
}
|
||||
subsystemOwner.TryGetSubsystemObject(out _subsystem);
|
||||
|
||||
if (_subsystem == null)
|
||||
{
|
||||
Debug.LogWarning($"[{GetType().Name}] RestaurantOrderType 서브시스템이 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}");
|
||||
//Debug.LogWarning($"[{GetType().Name}] RestaurantOrderType 서브시스템이 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentOrderType = _subsystem.GetInteractionSubsystemType();
|
||||
|
||||
_initialized = true;
|
||||
_patienceSlider.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_initialized) return;
|
||||
if (!_initialized)
|
||||
{
|
||||
InitializeSubsystem();
|
||||
return;
|
||||
}
|
||||
_currentOrderType = _subsystem.GetInteractionSubsystemType();
|
||||
if (!_targetOrderType.Contains(_currentOrderType))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user