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

- 고객 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")]
[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 bool _isGetInteractor;
@ -28,29 +22,55 @@ public override void OnStart()
public override TaskStatus OnUpdate()
{
// 레스토랑 주문 인터랙션 후보를 가져옴
TaskStatus targetSearchSuccess = RestaurantOrderAvailable.FindAvailableOrderInteractable(_requireCanInteract, _targetOrderType, out var
outInteractable);
if (targetSearchSuccess == TaskStatus.Failure)
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(_targetOrderType, out currentInteractable);
if (targetSearchSuccess == 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로 사용
if (!_isGetInteractor || !_interactor.CanInteractTo(outInteractable))
if (!_isGetInteractor)
{
return TaskStatus.Failure;
}
RestaurantEvents.InteractionEvent.RequestInteraction(_interactor.GetInteractorGameObject(), outInteractable.gameObject, outInteractable.GetInteractionType());
RestaurantEvents.InteractionEvent.RequestInteraction(_interactor.GetInteractorGameObject(), currentInteractable.GetInteractableGameObject(), currentInteractable.GetInteractionType());
if (_registerOnBlackboard)
{
var customerBlackboard = gameObject.GetComponent<IAISharedBlackboard>();
customerBlackboard?.SetBlackboardGameObject(nameof(RestaurantCustomerBlackboardKey.CurrentInteractionTarget), outInteractable.gameObject);
}
if (_UnregisterOnBlackboard)
if (_targetOrderType == RestaurantOrderType.Busy)
{
var customerBlackboard = gameObject.GetComponent<IAISharedBlackboard>();
customerBlackboard?.SetBlackboardGameObject(nameof(RestaurantCustomerBlackboardKey.CurrentInteractionTarget), null);

View File

@ -9,8 +9,9 @@ namespace DDD.Restaurant
public class WaitForPlayerInteraction : Action
{
[SerializeField] private RestaurantOrderType _targetOrderType;
[SerializeField] private RestaurantOrderType _nextOrderType;
private IInteractionSubsystemObject<RestaurantOrderType> _interactionSubsystem;
private bool _isGetInteractionSubsystem;
private bool _isSubsystemExist;
public override void OnStart()
{
@ -43,13 +44,19 @@ public override void OnStart()
$"[{GetType().Name}]에서 {nameof(IInteractionSubsystemObject)}를 찾을 수 없습니다: {gameObject.name}");
return;
}
_isGetInteractionSubsystem = true;
if (_interactionSubsystem?.GetInteractionSubsystemType() != _targetOrderType)
{
Debug.Log($"[{GetType().Name}] 다른 Order Type: {_interactionSubsystem?.GetInteractionSubsystemType()} != {_targetOrderType}");
return;
}
_isSubsystemExist = true;
}
public override TaskStatus OnUpdate()
{
if (!_isGetInteractionSubsystem) return TaskStatus.Failure;
if (!_isSubsystemExist) return TaskStatus.Failure;
TaskStatus result = CheckToSubsystemStatus();
if (result == TaskStatus.Success) Debug.Log($"[{GetType().Name}] Success");
@ -58,9 +65,10 @@ public override TaskStatus OnUpdate()
private TaskStatus CheckToSubsystemStatus()
{
return _interactionSubsystem.GetInteractionSubsystemType() == _targetOrderType
? TaskStatus.Running
: TaskStatus.Success;
var currentSubsystemType = _interactionSubsystem.GetInteractionSubsystemType();
if (currentSubsystemType == _nextOrderType)
return TaskStatus.Success;
return TaskStatus.Running;
}
}
}

View File

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

View File

@ -39,14 +39,17 @@ private Task Initialize()
public override void InitializeSolvers()
{
var typesToSolver = RestaurantInteractionEventSolvers.TypeToSolver;
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(playerSolver);
}
protected override void OnDestroy()

View File

@ -6,5 +6,6 @@ namespace DDD
public interface IInteractionSubsystemOwner
{
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
{
[FormerlySerializedAs("orderType")] [SerializeField] protected RestaurantOrderType _orderType = RestaurantOrderType.Wait;
private RestaurantOrderType _currentRestaurantOrderType;
private void Start()
{
_currentRestaurantOrderType = _orderType;
}
[SerializeField] private RestaurantOrderType _currentRestaurantOrderType = RestaurantOrderType.Wait;
public bool CanInteract()
{
@ -58,7 +52,6 @@ public ScriptableObject GetPayload()
public void InitializeSubsystem()
{
_currentRestaurantOrderType = _orderType;
}
public RestaurantOrderType GetInteractionSubsystemType()

View File

@ -185,5 +185,20 @@ public bool TryGetSubsystemObject<T>(out IInteractionSubsystemObject<T> subsyste
subsystemObject = null;
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)
{
if (CanExecuteInteractionSubsystem(interactor, interactable, payload) == false) return false;
if (CanExecuteInteractionSubsystem(interactor, interactable, payload) == false)
return false;
if (interactable is not IInteractionSubsystemOwner subsystemOwner)
return false;
if (!subsystemOwner.TryGetSubsystemObject<RestaurantOrderType>(out var subsystem))