AI 비헤이비어 트리 액션 및 컨디셔널 구현
RestaurantOrderAvailable, StartRestaurantOrder, MoveToInteractionTarget
This commit is contained in:
parent
8f56d99bf7
commit
4ef63ec9a9
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.
3
Assets/_DDD/_Scripts/AI.meta
Normal file
3
Assets/_DDD/_Scripts/AI.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c31792c3491f44878a9a5e8ee59504cf
|
||||||
|
timeCreated: 1755770768
|
3
Assets/_DDD/_Scripts/AI/Common.meta
Normal file
3
Assets/_DDD/_Scripts/AI/Common.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 49c654aa3aa94cb9928e2d161cad789a
|
||||||
|
timeCreated: 1755770768
|
15
Assets/_DDD/_Scripts/AI/Common/IAISharedBlackboard.cs
Normal file
15
Assets/_DDD/_Scripts/AI/Common/IAISharedBlackboard.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 공용 AI 블랙보드 인터페이스.
|
||||||
|
/// - 다양한 캐릭터 AI에서 공통으로 참조하는 현재 인터랙션 타겟만 정의합니다.
|
||||||
|
/// - 필요 시 키-값 확장을 고려하되, 현재는 최소 요구만 충족합니다.
|
||||||
|
/// </summary>
|
||||||
|
public interface IAISharedBlackboard
|
||||||
|
{
|
||||||
|
void SetCurrentInteractionTarget(GameObject targetGameObject);
|
||||||
|
GameObject GetCurrentInteractionTarget();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 62510fba6cb047419ca463dc523ae536
|
||||||
|
timeCreated: 1755770768
|
3
Assets/_DDD/_Scripts/RestaurantCharacter/AI/Common.meta
Normal file
3
Assets/_DDD/_Scripts/RestaurantCharacter/AI/Common.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cfc8e456b2134c4a87b9fcd0d385cf1d
|
||||||
|
timeCreated: 1755767029
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ab824a41c52d4cca8cafb1fc96d5d8e7
|
||||||
|
timeCreated: 1755769401
|
@ -0,0 +1,165 @@
|
|||||||
|
using System;
|
||||||
|
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||||
|
using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// IAiMovement를 이용해 인터랙션 타겟(주로 RestaurantInteractionComponent가 붙은 오브젝트)으로 이동하는 액션.
|
||||||
|
/// - 타겟은 우선 블랙보드(IRestaurantCustomerBlackboard)에서 가져오고, 없으면 Interactor의 FocusedInteractable에서 가져옵니다.
|
||||||
|
/// - 타겟에 RestaurantInteractionComponent가 있다면 가장 가까운 InteractionPoint를 목적지로 사용합니다.
|
||||||
|
/// </summary>
|
||||||
|
public class MoveToInteractionTarget : Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Action
|
||||||
|
{
|
||||||
|
[Header("Target")]
|
||||||
|
[Tooltip("타겟에 RestaurantInteractionComponent가 있을 때 InteractionPoints를 사용해 가장 가까운 지점으로 이동합니다.")]
|
||||||
|
[SerializeField] private bool _useInteractionPoints = true;
|
||||||
|
[Tooltip("타겟이 없을 때 즉시 실패할지 여부")]
|
||||||
|
[SerializeField] private bool _failIfNoTarget = true;
|
||||||
|
|
||||||
|
[Header("Movement")]
|
||||||
|
[Tooltip("목적지에 도달했다고 간주할 거리")]
|
||||||
|
[SerializeField] private float _stoppingDistance = 0.5f;
|
||||||
|
[Tooltip("이동 중 목적지를 재계산하는 주기(초). 0 이하면 재계산하지 않음")]
|
||||||
|
[SerializeField] private float _repathInterval = 0.5f;
|
||||||
|
|
||||||
|
private IAiMovement _movement;
|
||||||
|
private float _repathTimer;
|
||||||
|
private Vector3 _currentDestination;
|
||||||
|
private bool _started;
|
||||||
|
private GameObject _resolvedTarget;
|
||||||
|
|
||||||
|
public override void OnStart()
|
||||||
|
{
|
||||||
|
_movement = gameObject.GetComponentInParent<IAiMovement>();
|
||||||
|
_repathTimer = 0f;
|
||||||
|
_started = false;
|
||||||
|
_resolvedTarget = ResolveTargetFromContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override TaskStatus OnUpdate()
|
||||||
|
{
|
||||||
|
if (_movement == null)
|
||||||
|
{
|
||||||
|
return TaskStatus.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 매 프레임 타겟이 갱신될 수 있으므로 재해결 (필요 시 비용 줄일 수 있음)
|
||||||
|
_resolvedTarget = _resolvedTarget ?? ResolveTargetFromContext();
|
||||||
|
|
||||||
|
if (_resolvedTarget == null)
|
||||||
|
{
|
||||||
|
if (_failIfNoTarget) return TaskStatus.Failure;
|
||||||
|
return TaskStatus.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 타겟이 파괴되었는지 확인
|
||||||
|
if (_resolvedTarget == null || (_resolvedTarget as UnityEngine.Object) == null)
|
||||||
|
{
|
||||||
|
return TaskStatus.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 목적지 계산 및 이동 시작/유지
|
||||||
|
_repathTimer -= Time.deltaTime;
|
||||||
|
if (!_started || _repathInterval > 0f && _repathTimer <= 0f)
|
||||||
|
{
|
||||||
|
_currentDestination = GetBestDestination(_resolvedTarget);
|
||||||
|
_repathTimer = _repathInterval;
|
||||||
|
|
||||||
|
if (!_started)
|
||||||
|
{
|
||||||
|
if (!_movement.TryMoveToPosition(_currentDestination))
|
||||||
|
{
|
||||||
|
return TaskStatus.Failure;
|
||||||
|
}
|
||||||
|
_movement.EnableMove();
|
||||||
|
_movement.PlayMove();
|
||||||
|
_started = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 이동 중 목적지 갱신 시도
|
||||||
|
_movement.TryMoveToPosition(_currentDestination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 도달 판정
|
||||||
|
var sqrDist = (GetAgentPosition() - _currentDestination).sqrMagnitude;
|
||||||
|
if (sqrDist <= _stoppingDistance * _stoppingDistance || _movement.HasReachedDestination())
|
||||||
|
{
|
||||||
|
_movement.StopMove();
|
||||||
|
_movement.DisableMove();
|
||||||
|
return TaskStatus.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskStatus.Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEnd()
|
||||||
|
{
|
||||||
|
// 액션 종료 시 이동 중지(안전장치)
|
||||||
|
if (_movement != null)
|
||||||
|
{
|
||||||
|
_movement.StopMove();
|
||||||
|
_movement.DisableMove();
|
||||||
|
}
|
||||||
|
_resolvedTarget = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GameObject ResolveTargetFromContext()
|
||||||
|
{
|
||||||
|
// 1) 공용 블랙보드에서 가져오기
|
||||||
|
var sharedBlackboard = gameObject.GetComponentInParent<IAISharedBlackboard>();
|
||||||
|
var bbTarget = sharedBlackboard?.GetCurrentInteractionTarget();
|
||||||
|
if (bbTarget != null) return bbTarget;
|
||||||
|
|
||||||
|
// 2) Interactor의 포커싱 대상에서 가져오기
|
||||||
|
var interactor = gameObject.GetComponentInParent<IInteractor>();
|
||||||
|
var focused = interactor?.GetFocusedInteractable();
|
||||||
|
if (focused != null)
|
||||||
|
{
|
||||||
|
return focused.GetInteractableGameObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3 GetAgentPosition()
|
||||||
|
{
|
||||||
|
return _movement != null ? _movement.CurrentPosition : transform.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3 GetBestDestination(GameObject target)
|
||||||
|
{
|
||||||
|
if (!_useInteractionPoints)
|
||||||
|
{
|
||||||
|
return target.transform.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.TryGetComponent<RestaurantInteractionComponent>(out var ric))
|
||||||
|
{
|
||||||
|
var points = ric.GetInteractionPoints();
|
||||||
|
if (points != null && points.Length > 0)
|
||||||
|
{
|
||||||
|
var from = GetAgentPosition();
|
||||||
|
float bestSqr = float.MaxValue;
|
||||||
|
Vector3 best = target.transform.position;
|
||||||
|
for (int i = 0; i < points.Length; i++)
|
||||||
|
{
|
||||||
|
var p = points[i];
|
||||||
|
var sqr = (p - from).sqrMagnitude;
|
||||||
|
if (sqr < bestSqr)
|
||||||
|
{
|
||||||
|
bestSqr = sqr;
|
||||||
|
best = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target.transform.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d97cd08353334cb698807d4b526d01b6
|
||||||
|
timeCreated: 1755769413
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6f1cc55df1da4604a2a7f445527710de
|
||||||
|
timeCreated: 1755765247
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6777cf4d7cf4408fafe4bc9097c32b01
|
||||||
|
timeCreated: 1755767888
|
@ -0,0 +1,60 @@
|
|||||||
|
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||||
|
using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
public class StartRestaurantOrder : Action
|
||||||
|
{
|
||||||
|
[Tooltip("상호작용할 RestaurantOrderType")]
|
||||||
|
[SerializeField] private RestaurantOrderType _targetOrderType = RestaurantOrderType.Wait;
|
||||||
|
[Tooltip("실제 상호작용 가능 여부를 확인하고 수행합니다")]
|
||||||
|
[SerializeField] private bool _requireCanInteract = true;
|
||||||
|
[Tooltip("성공 시 블랙보드에 현재 인터랙션 대상을 등록합니다")]
|
||||||
|
[SerializeField] private bool _registerOnBlackboard = true;
|
||||||
|
|
||||||
|
public override TaskStatus OnUpdate()
|
||||||
|
{
|
||||||
|
// TODO : 아래 타겟 찾기가 RestaurantOrderAvailable과 동일해야 함, 동일하면 중복될 필요 없으니 스태틱 유틸 함수정도로 만들어서 공유하기.
|
||||||
|
// 레스토랑 주문 인터랙션 후보를 가져옴
|
||||||
|
TaskStatus targetSearchSuccess = RestaurantOrderAvailable.FindAvailableOrderInteractable(_requireCanInteract, _targetOrderType, out var
|
||||||
|
outInteractable);
|
||||||
|
if (targetSearchSuccess == TaskStatus.Failure)
|
||||||
|
{
|
||||||
|
return TaskStatus.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : 아래 상호작용 수행 로직이 우리 프로젝트의 권장하는 방식이 아님. 플레이어가 오브젝트에 인터랙션하는 것과 비슷한 흐름으로 NPC가 오브젝트에 인터랙션하게 만들 것.
|
||||||
|
// 상호작용 수행: 액션이 붙은 에이전트를 Interactor로 사용
|
||||||
|
var interactor = gameObject.GetComponentInParent<IInteractor>();
|
||||||
|
if (interactor == null)
|
||||||
|
{
|
||||||
|
return TaskStatus.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
var interacted = outInteractable.OnInteracted(interactor);
|
||||||
|
if (!interacted)
|
||||||
|
{
|
||||||
|
return TaskStatus.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_registerOnBlackboard)
|
||||||
|
{
|
||||||
|
// 공용 블랙보드 우선
|
||||||
|
var shared = gameObject.GetComponentInParent<IAISharedBlackboard>();
|
||||||
|
if (shared != null)
|
||||||
|
{
|
||||||
|
shared.SetCurrentInteractionTarget(outInteractable.gameObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 하위 호환: 고객 전용 블랙보드 지원
|
||||||
|
var customerBb = gameObject.GetComponentInParent<IRestaurantCustomerBlackboard>();
|
||||||
|
customerBb?.SetCurrentInteractionTarget(outInteractable.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskStatus.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 84b6c26acd1e41afa6b07ed4c6caf860
|
||||||
|
timeCreated: 1755767930
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c4f20081a3974c08b60b9efdbe1568a7
|
||||||
|
timeCreated: 1755765256
|
@ -0,0 +1,73 @@
|
|||||||
|
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||||
|
using Opsive.BehaviorDesigner.Runtime.Tasks.Conditionals;
|
||||||
|
using UnityEngine;
|
||||||
|
using DDD;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
public class RestaurantOrderAvailable : Conditional
|
||||||
|
{
|
||||||
|
[Tooltip("검사할 RestaurantOrderType")]
|
||||||
|
[SerializeField] private RestaurantOrderType _targetOrderType = RestaurantOrderType.Wait;
|
||||||
|
[Tooltip("인터랙션 가능 여부까지 확인할지 선택")]
|
||||||
|
[SerializeField] private bool _checkCanInteract = true;
|
||||||
|
|
||||||
|
public RestaurantOrderType TargetOrderType
|
||||||
|
{
|
||||||
|
get => _targetOrderType;
|
||||||
|
set => _targetOrderType = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CheckCanInteract
|
||||||
|
{
|
||||||
|
get => _checkCanInteract;
|
||||||
|
set => _checkCanInteract = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override TaskStatus OnUpdate()
|
||||||
|
{
|
||||||
|
TaskStatus targetSearchSuccess = FindAvailableOrderInteractable(_checkCanInteract, _targetOrderType, out var
|
||||||
|
outInteractable);
|
||||||
|
return targetSearchSuccess;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TaskStatus FindAvailableOrderInteractable(bool checkCanInteract, RestaurantOrderType targetOrderType, out RestaurantInteractionComponent outInteractable)
|
||||||
|
{
|
||||||
|
outInteractable = null;
|
||||||
|
|
||||||
|
var environmentState = RestaurantState.Instance?.EnvironmentState;
|
||||||
|
if (environmentState == null)
|
||||||
|
{
|
||||||
|
return TaskStatus.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
var interactables = environmentState.GetInteractablesByType(InteractionType.RestaurantOrder);
|
||||||
|
|
||||||
|
foreach (var interactable in interactables)
|
||||||
|
{
|
||||||
|
// 서브시스템에서 RestaurantOrderType을 가져와 비교
|
||||||
|
outInteractable = interactable as RestaurantInteractionComponent;
|
||||||
|
if (outInteractable == null) continue;
|
||||||
|
if (!outInteractable.TryGetSubsystemObject<RestaurantOrderType>(out var subsystem)) continue;
|
||||||
|
|
||||||
|
if (subsystem.GetInteractionSubsystemType() == targetOrderType)
|
||||||
|
{
|
||||||
|
// CheckCanInteract이 false면 타입만 맞으면 성공
|
||||||
|
if (!checkCanInteract)
|
||||||
|
{
|
||||||
|
return TaskStatus.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckCanInteract이 true면 실제 인터랙션 가능 여부까지 확인
|
||||||
|
if (interactable.CanInteract())
|
||||||
|
{
|
||||||
|
return TaskStatus.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskStatus.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bef354fd7cae4273b4cb4e7ff9e270f5
|
||||||
|
timeCreated: 1755765519
|
@ -0,0 +1,39 @@
|
|||||||
|
using Opsive.BehaviorDesigner.Runtime;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
public class RestaurantCustomerBlackboardComponent : MonoBehaviour, IRestaurantCustomerBlackboard, IAISharedBlackboard
|
||||||
|
{
|
||||||
|
private Subtree _subtree;
|
||||||
|
private GameObject _currentInteractionTarget;
|
||||||
|
|
||||||
|
public void InitializeWithBehaviorTree(Subtree subtree)
|
||||||
|
{
|
||||||
|
_subtree = subtree;
|
||||||
|
if (_subtree != null)
|
||||||
|
{
|
||||||
|
_subtree.SetVariableValue(nameof(RestaurantCustomerBlackboardKey.SelfGameObject), gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCustomerData(CustomerData inCustomerData)
|
||||||
|
{
|
||||||
|
if (_subtree == null) return;
|
||||||
|
_subtree.SetVariableValue(nameof(RestaurantCustomerBlackboardKey.CustomerData), inCustomerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCurrentInteractionTarget(GameObject targetGameObject)
|
||||||
|
{
|
||||||
|
_currentInteractionTarget = targetGameObject;
|
||||||
|
if (_subtree == null) return;
|
||||||
|
_subtree.SetVariableValue(nameof(RestaurantCustomerBlackboardKey.CurrentInteractionTarget), targetGameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject GetCurrentInteractionTarget()
|
||||||
|
{
|
||||||
|
// 캐시 우선 반환. 필요 시 Subtree에서 직접 조회하도록 확장 가능.
|
||||||
|
return _currentInteractionTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
using Opsive.BehaviorDesigner.Runtime;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace DDD
|
|
||||||
{
|
|
||||||
public class RestaurantCustomerBlackboardComponent : MonoBehaviour, IRestaurantCustomerBlackboard
|
|
||||||
{
|
|
||||||
private Subtree _subtree;
|
|
||||||
|
|
||||||
public void InitializeWithBehaviorTree(Subtree subtree)
|
|
||||||
{
|
|
||||||
_subtree = subtree;
|
|
||||||
_subtree.SetVariableValue(nameof(RestaurantCustomerBlackboardKey.SelfGameObject), gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetCustomerData(CustomerData inCustomerData)
|
|
||||||
{
|
|
||||||
_subtree.SetVariableValue(nameof(RestaurantCustomerBlackboardKey.CustomerData), inCustomerData);;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,18 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
{
|
{
|
||||||
public enum RestaurantCustomerBlackboardKey
|
public enum RestaurantCustomerBlackboardKey
|
||||||
{
|
{
|
||||||
SelfGameObject,
|
SelfGameObject,
|
||||||
CustomerData,
|
CustomerData,
|
||||||
|
CurrentInteractionTarget,
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IRestaurantCustomerBlackboard
|
public interface IRestaurantCustomerBlackboard
|
||||||
{
|
{
|
||||||
void SetCustomerData(CustomerData inCustomerData);
|
void SetCustomerData(CustomerData inCustomerData);
|
||||||
|
void SetCurrentInteractionTarget(GameObject targetGameObject);
|
||||||
|
GameObject GetCurrentInteractionTarget();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
{
|
{
|
||||||
[Flags]
|
|
||||||
public enum RestaurantOrderType : uint
|
public enum RestaurantOrderType : uint
|
||||||
{
|
{
|
||||||
Wait = 0u,
|
Wait = 0u,
|
||||||
@ -35,11 +34,9 @@ public bool CanInteract()
|
|||||||
|
|
||||||
public bool OnInteracted(IInteractor interactor, ScriptableObject payloadSo = null)
|
public bool OnInteracted(IInteractor interactor, ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
if (GetInteractionSubsystemType() == RestaurantOrderType.Wait)
|
// 간단한 상태 전이: 현재 상태에서 다음 상태로 이동
|
||||||
{
|
var prev = currentRestaurantOrderType;
|
||||||
// DO WAIT CUSTOMER
|
currentRestaurantOrderType = GetNextState(prev);
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,5 +49,19 @@ public RestaurantOrderType GetInteractionSubsystemType()
|
|||||||
{
|
{
|
||||||
return currentRestaurantOrderType;
|
return currentRestaurantOrderType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RestaurantOrderType GetNextState(RestaurantOrderType state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case RestaurantOrderType.Wait: return RestaurantOrderType.Reserved;
|
||||||
|
case RestaurantOrderType.Reserved: return RestaurantOrderType.Order;
|
||||||
|
case RestaurantOrderType.Order: return RestaurantOrderType.Serve;
|
||||||
|
case RestaurantOrderType.Serve: return RestaurantOrderType.Busy;
|
||||||
|
case RestaurantOrderType.Busy: return RestaurantOrderType.Dirty;
|
||||||
|
case RestaurantOrderType.Dirty: return RestaurantOrderType.Wait;
|
||||||
|
default: return RestaurantOrderType.Wait;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -30,14 +30,31 @@ public class RestaurantInteractionComponent : MonoBehaviour, IInteractable, IInt
|
|||||||
|
|
||||||
private Dictionary<InteractionType, IInteractionSubsystemObject> _subsystems = new();
|
private Dictionary<InteractionType, IInteractionSubsystemObject> _subsystems = new();
|
||||||
|
|
||||||
private void Start()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
|
// Register this interactable to environment state
|
||||||
|
var environmentState = RestaurantState.Instance?.EnvironmentState;
|
||||||
|
environmentState?.RegisterInteractable(this);
|
||||||
|
|
||||||
if (autoInitialize)
|
if (autoInitialize)
|
||||||
{
|
{
|
||||||
InitializeInteraction(_interactionType);
|
InitializeInteraction(_interactionType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
var environmentState = RestaurantState.Instance?.EnvironmentState;
|
||||||
|
environmentState?.UnregisterInteractable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
// 보수적으로 Start에서도 등록 시도 (OnEnable 시점에 EnvironmentState가 없었을 경우 대비)
|
||||||
|
var environmentState = RestaurantState.Instance?.EnvironmentState;
|
||||||
|
environmentState?.RegisterInteractable(this);
|
||||||
|
}
|
||||||
|
|
||||||
private static IEnumerable GetAllInteractionTypes()
|
private static IEnumerable GetAllInteractionTypes()
|
||||||
{
|
{
|
||||||
return System.Enum.GetValues(typeof(InteractionType))
|
return System.Enum.GetValues(typeof(InteractionType))
|
||||||
|
@ -21,5 +21,54 @@ public class RestaurantEnvironmentState : ScriptableObject
|
|||||||
{
|
{
|
||||||
public List<RestaurantPropLocation> Props = new List<RestaurantPropLocation>();
|
public List<RestaurantPropLocation> Props = new List<RestaurantPropLocation>();
|
||||||
public List<RestaurantPropLocation> Objects = new List<RestaurantPropLocation>();
|
public List<RestaurantPropLocation> Objects = new List<RestaurantPropLocation>();
|
||||||
|
|
||||||
|
// 인터랙션 가능한 객체(IInteractable)를 관리하기 위한 리스트 (런타임 전용)
|
||||||
|
private readonly List<IInteractable> _registeredInteractables = new List<IInteractable>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 인터랙션 가능한 객체를 등록합니다
|
||||||
|
/// </summary>
|
||||||
|
public void RegisterInteractable(IInteractable interactable)
|
||||||
|
{
|
||||||
|
if (interactable == null) return;
|
||||||
|
if (_registeredInteractables.Contains(interactable)) return;
|
||||||
|
_registeredInteractables.Add(interactable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 인터랙션 가능한 객체를 해제합니다
|
||||||
|
/// </summary>
|
||||||
|
public void UnregisterInteractable(IInteractable interactable)
|
||||||
|
{
|
||||||
|
if (interactable == null) return;
|
||||||
|
_registeredInteractables.Remove(interactable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 특정 InteractionType에 해당하는 인터랙션 객체들을 반환합니다
|
||||||
|
/// </summary>
|
||||||
|
public List<IInteractable> GetInteractablesByType(InteractionType interactionType)
|
||||||
|
{
|
||||||
|
var result = new List<IInteractable>();
|
||||||
|
// null 또는 Destroyed 오브젝트 정리
|
||||||
|
_registeredInteractables.RemoveAll(item => item == null || (item as UnityEngine.Object) == null);
|
||||||
|
foreach (var interactable in _registeredInteractables)
|
||||||
|
{
|
||||||
|
if (interactable.GetInteractionType() == interactionType)
|
||||||
|
{
|
||||||
|
result.Add(interactable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 모든 등록된 인터랙션 객체들을 반환합니다
|
||||||
|
/// </summary>
|
||||||
|
public List<IInteractable> GetAllInteractables()
|
||||||
|
{
|
||||||
|
_registeredInteractables.RemoveAll(item => item == null || (item as UnityEngine.Object) == null);
|
||||||
|
return new List<IInteractable>(_registeredInteractables);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user