레스토랑 상호작용 시스템 개선:

- 고객 AI 주문 시작/대기/가용성 로직 정리 및 안정성 향상
- IInteractionSubsystemOwner 및 Order Subsystem 연동 보강
- PlayerInteraction/InteractionComponent 이벤트 흐름 수정
- Wait 주문 솔버 로직 보정
- 고객 기본/주문 서브트리 에셋 업데이트
This commit is contained in:
Jeonghyeon Ha 2025-08-28 14:52:21 +09:00
parent 86c0f67c7c
commit a7d741b81e
10 changed files with 90 additions and 58 deletions

Binary file not shown.

Binary file not shown.

View File

@ -8,12 +8,6 @@ public class StartRestaurantOrder : Action
{ {
[Tooltip("상호작용할 RestaurantOrderType")] [Tooltip("상호작용할 RestaurantOrderType")]
[SerializeField] private RestaurantOrderType _targetOrderType = RestaurantOrderType.Wait; [SerializeField] private RestaurantOrderType _targetOrderType = RestaurantOrderType.Wait;
[Tooltip("실제 상호작용 가능 여부를 확인하고 수행합니다")]
[SerializeField] private bool _requireCanInteract = true;
[Tooltip("성공 시 블랙보드에 현재 인터랙션 대상을 등록합니다")]
[SerializeField] private bool _registerOnBlackboard = true;
[Tooltip("성공 시 블랙보드에 현재 인터랙션 대상을 등록합니다")]
[SerializeField] private bool _UnregisterOnBlackboard = false;
private IInteractor _interactor; private IInteractor _interactor;
private bool _isGetInteractor; private bool _isGetInteractor;
@ -27,30 +21,56 @@ public override void OnStart()
} }
public override TaskStatus OnUpdate() public override TaskStatus OnUpdate()
{
var blackboard = gameObject.GetComponent<IAISharedBlackboard>();
var target = blackboard?.GetBlackboardGameObject(nameof(RestaurantCustomerBlackboardKey.CurrentInteractionTarget));
IInteractable currentInteractable = target?.GetComponent<IInteractable>();
if (_targetOrderType == RestaurantOrderType.Wait)
{ {
// 레스토랑 주문 인터랙션 후보를 가져옴 // 레스토랑 주문 인터랙션 후보를 가져옴
TaskStatus targetSearchSuccess = RestaurantOrderAvailable.FindAvailableOrderInteractable(_requireCanInteract, _targetOrderType, out var TaskStatus targetSearchSuccess = RestaurantOrderAvailable.FindAvailableOrderInteractable(_targetOrderType, out currentInteractable);
outInteractable);
if (targetSearchSuccess == TaskStatus.Failure) if (targetSearchSuccess == TaskStatus.Failure)
{ {
return TaskStatus.Failure; return TaskStatus.Failure;
} }
var customerBlackboard = gameObject.GetComponent<IAISharedBlackboard>();
customerBlackboard?.SetBlackboardGameObject(nameof(RestaurantCustomerBlackboardKey.CurrentInteractionTarget), currentInteractable.GetInteractableGameObject());
}
// Check order type of the current interactable
if (currentInteractable is IInteractionSubsystemOwner subsystemOwner)
{
if (subsystemOwner.TryGetSubsystemEnumType<RestaurantOrderType>(out var subsystemType))
{
if (subsystemType != _targetOrderType)
{
Debug.LogWarning($"[{GetType().Name}] 상호작용할 RestaurantOrderType이 다릅니다: {subsystemType} != {_targetOrderType}");
return TaskStatus.Failure;
}
}
else
{
return TaskStatus.Failure;
}
}
if (currentInteractable == null)
{
Debug.Assert(false);
return TaskStatus.Failure;
}
// 상호작용 수행: 액션이 붙은 에이전트를 Interactor로 사용 // 상호작용 수행: 액션이 붙은 에이전트를 Interactor로 사용
if (!_isGetInteractor || !_interactor.CanInteractTo(outInteractable)) if (!_isGetInteractor)
{ {
return TaskStatus.Failure; return TaskStatus.Failure;
} }
RestaurantEvents.InteractionEvent.RequestInteraction(_interactor.GetInteractorGameObject(), outInteractable.gameObject, outInteractable.GetInteractionType()); RestaurantEvents.InteractionEvent.RequestInteraction(_interactor.GetInteractorGameObject(), currentInteractable.GetInteractableGameObject(), currentInteractable.GetInteractionType());
if (_registerOnBlackboard) if (_targetOrderType == RestaurantOrderType.Busy)
{
var customerBlackboard = gameObject.GetComponent<IAISharedBlackboard>();
customerBlackboard?.SetBlackboardGameObject(nameof(RestaurantCustomerBlackboardKey.CurrentInteractionTarget), outInteractable.gameObject);
}
if (_UnregisterOnBlackboard)
{ {
var customerBlackboard = gameObject.GetComponent<IAISharedBlackboard>(); var customerBlackboard = gameObject.GetComponent<IAISharedBlackboard>();
customerBlackboard?.SetBlackboardGameObject(nameof(RestaurantCustomerBlackboardKey.CurrentInteractionTarget), null); customerBlackboard?.SetBlackboardGameObject(nameof(RestaurantCustomerBlackboardKey.CurrentInteractionTarget), null);

View File

@ -9,8 +9,9 @@ namespace DDD.Restaurant
public class WaitForPlayerInteraction : Action public class WaitForPlayerInteraction : Action
{ {
[SerializeField] private RestaurantOrderType _targetOrderType; [SerializeField] private RestaurantOrderType _targetOrderType;
[SerializeField] private RestaurantOrderType _nextOrderType;
private IInteractionSubsystemObject<RestaurantOrderType> _interactionSubsystem; private IInteractionSubsystemObject<RestaurantOrderType> _interactionSubsystem;
private bool _isGetInteractionSubsystem; private bool _isSubsystemExist;
public override void OnStart() public override void OnStart()
{ {
@ -44,12 +45,18 @@ public override void OnStart()
return; return;
} }
_isGetInteractionSubsystem = true; if (_interactionSubsystem?.GetInteractionSubsystemType() != _targetOrderType)
{
Debug.Log($"[{GetType().Name}] 다른 Order Type: {_interactionSubsystem?.GetInteractionSubsystemType()} != {_targetOrderType}");
return;
}
_isSubsystemExist = true;
} }
public override TaskStatus OnUpdate() public override TaskStatus OnUpdate()
{ {
if (!_isGetInteractionSubsystem) return TaskStatus.Failure; if (!_isSubsystemExist) return TaskStatus.Failure;
TaskStatus result = CheckToSubsystemStatus(); TaskStatus result = CheckToSubsystemStatus();
if (result == TaskStatus.Success) Debug.Log($"[{GetType().Name}] Success"); if (result == TaskStatus.Success) Debug.Log($"[{GetType().Name}] Success");
@ -58,9 +65,10 @@ public override TaskStatus OnUpdate()
private TaskStatus CheckToSubsystemStatus() private TaskStatus CheckToSubsystemStatus()
{ {
return _interactionSubsystem.GetInteractionSubsystemType() == _targetOrderType var currentSubsystemType = _interactionSubsystem.GetInteractionSubsystemType();
? TaskStatus.Running if (currentSubsystemType == _nextOrderType)
: TaskStatus.Success; return TaskStatus.Success;
return TaskStatus.Running;
} }
} }
} }

View File

@ -28,13 +28,13 @@ public bool CheckCanInteract
public override TaskStatus OnUpdate() public override TaskStatus OnUpdate()
{ {
TaskStatus targetSearchSuccess = FindAvailableOrderInteractable(_checkCanInteract, _targetOrderType, out var TaskStatus targetSearchSuccess = FindAvailableOrderInteractable(_targetOrderType, out var
outInteractable); outInteractable);
return targetSearchSuccess; return targetSearchSuccess;
} }
public static TaskStatus FindAvailableOrderInteractable<T>(bool checkCanInteract, T targetOrderType, out RestaurantInteractionComponent outInteractable) where T : Enum public static TaskStatus FindAvailableOrderInteractable(RestaurantOrderType targetOrderType, out IInteractable outInteractable)
{ {
outInteractable = null; outInteractable = null;
@ -45,25 +45,17 @@ public static TaskStatus FindAvailableOrderInteractable<T>(bool checkCanInteract
} }
var interactables = environmentState.GetInteractablesByType(InteractionType.RestaurantOrder); var interactables = environmentState.GetInteractablesByType(InteractionType.RestaurantOrder);
foreach (var interactable in interactables) foreach (var interactable in interactables)
{ {
// 서브시스템에서 RestaurantOrderType을 가져와 비교 // 서브시스템에서 RestaurantOrderType을 가져와 비교
outInteractable = interactable as RestaurantInteractionComponent; if (interactable is not IInteractionSubsystemOwner subsystemOwner) continue;
if (outInteractable == null) continue; if (!subsystemOwner.TryGetSubsystemObject<RestaurantOrderType>(out var subsystem)) continue;
if (!outInteractable.TryGetSubsystemObject<T>(out var subsystem)) continue; if (subsystem.GetInteractionSubsystemType() == targetOrderType)
if (EqualityComparer<T>.Default.Equals(subsystem.GetInteractionSubsystemType(), targetOrderType))
{ {
// CheckCanInteract이 false면 타입만 맞으면 성공
if (!checkCanInteract)
{
return TaskStatus.Success;
}
// CheckCanInteract이 true면 실제 인터랙션 가능 여부까지 확인 // CheckCanInteract이 true면 실제 인터랙션 가능 여부까지 확인
if (interactable.CanInteract()) if (interactable.CanInteract())
{ {
outInteractable = interactable;
return TaskStatus.Success; return TaskStatus.Success;
} }
} }

View File

@ -39,14 +39,17 @@ private Task Initialize()
public override void InitializeSolvers() public override void InitializeSolvers()
{ {
var typesToSolver = RestaurantInteractionEventSolvers.TypeToSolver;
var playerSolver = RestaurantInteractionEventSolvers.TypeToPlayerSolver; var playerSolver = RestaurantInteractionEventSolvers.TypeToPlayerSolver;
foreach(var pair in playerSolver) Dictionary<InteractionType, Type> typesToSolver = new();
foreach (var typeToSolver in RestaurantInteractionEventSolvers.TypeToSolver)
{ {
typesToSolver.Remove(pair.Key); typesToSolver.Add(typeToSolver.Key, typeToSolver.Value);
}
foreach (var typeToSolver in playerSolver)
{
typesToSolver[typeToSolver.Key] = typeToSolver.Value;
} }
InitializeInteractionSolvers(typesToSolver); InitializeInteractionSolvers(typesToSolver);
InitializeInteractionSolvers(playerSolver);
} }
protected override void OnDestroy() protected override void OnDestroy()

View File

@ -6,5 +6,6 @@ namespace DDD
public interface IInteractionSubsystemOwner public interface IInteractionSubsystemOwner
{ {
public bool TryGetSubsystemObject<T>(out IInteractionSubsystemObject<T> subsystemObject) where T : Enum; public bool TryGetSubsystemObject<T>(out IInteractionSubsystemObject<T> subsystemObject) where T : Enum;
public bool TryGetSubsystemEnumType<T>(out T subsystemType) where T : Enum;
} }
} }

View File

@ -21,13 +21,7 @@ public interface IRestaurantOrderObject
public class InteractionSubsystem_Order : MonoBehaviour, IInteractionSubsystemObject<RestaurantOrderType>, IRestaurantOrderObject public class InteractionSubsystem_Order : MonoBehaviour, IInteractionSubsystemObject<RestaurantOrderType>, IRestaurantOrderObject
{ {
[FormerlySerializedAs("orderType")] [SerializeField] protected RestaurantOrderType _orderType = RestaurantOrderType.Wait; [SerializeField] private RestaurantOrderType _currentRestaurantOrderType = RestaurantOrderType.Wait;
private RestaurantOrderType _currentRestaurantOrderType;
private void Start()
{
_currentRestaurantOrderType = _orderType;
}
public bool CanInteract() public bool CanInteract()
{ {
@ -58,7 +52,6 @@ public ScriptableObject GetPayload()
public void InitializeSubsystem() public void InitializeSubsystem()
{ {
_currentRestaurantOrderType = _orderType;
} }
public RestaurantOrderType GetInteractionSubsystemType() public RestaurantOrderType GetInteractionSubsystemType()

View File

@ -185,5 +185,20 @@ public bool TryGetSubsystemObject<T>(out IInteractionSubsystemObject<T> subsyste
subsystemObject = null; subsystemObject = null;
return false; return false;
} }
public bool TryGetSubsystemEnumType<T>(out T enumValue) where T : Enum
{
foreach (var interactionSubsystemObject in _subsystems.Values)
{
if (interactionSubsystemObject is IInteractionSubsystemObject<T> subsystem)
{
enumValue = subsystem.GetInteractionSubsystemType();
return true;
}
}
enumValue = default;
return false;
}
} }
} }

View File

@ -7,8 +7,8 @@ public class RestaurantOrderSolver_Wait : MonoBehaviour, IInteractionSubsystemSo
{ {
public bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payload = null) public bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payload = null)
{ {
if (CanExecuteInteractionSubsystem(interactor, interactable, payload) == false) return false; if (CanExecuteInteractionSubsystem(interactor, interactable, payload) == false)
return false;
if (interactable is not IInteractionSubsystemOwner subsystemOwner) if (interactable is not IInteractionSubsystemOwner subsystemOwner)
return false; return false;
if (!subsystemOwner.TryGetSubsystemObject<RestaurantOrderType>(out var subsystem)) if (!subsystemOwner.TryGetSubsystemObject<RestaurantOrderType>(out var subsystem))