diff --git a/Assets/_DDD/_Addressables/AI/Customer/Subtree/CustomerDefault.asset b/Assets/_DDD/_Addressables/AI/Customer/Subtree/CustomerDefault.asset index 7529b61c5..40e84f88d 100644 --- a/Assets/_DDD/_Addressables/AI/Customer/Subtree/CustomerDefault.asset +++ b/Assets/_DDD/_Addressables/AI/Customer/Subtree/CustomerDefault.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8089b1a0f359704e721ce008d9816bc58743c7a00abdf7cc504619f18500877f -size 67585 +oid sha256:2fe64a49fbb21f1fbd05ddebda355334a5aef73bf06397fad07f7d5d204cc0d9 +size 67467 diff --git a/Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset b/Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset new file mode 100644 index 000000000..7db36239c --- /dev/null +++ b/Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e351f1463cecafcddf5c1b9d015cbacf88d141811273d7079a022a86750a443 +size 18838 diff --git a/Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset.meta b/Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset.meta new file mode 100644 index 000000000..10312054b --- /dev/null +++ b/Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 71fd4c1fc79e83a4b9af326332323c28 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_DDD/_Addressables/AI/Customer/Subtree/OrderSubtree.asset b/Assets/_DDD/_Addressables/AI/Customer/Subtree/OrderSubtree.asset index 3d590a8bc..dfc57475d 100644 --- a/Assets/_DDD/_Addressables/AI/Customer/Subtree/OrderSubtree.asset +++ b/Assets/_DDD/_Addressables/AI/Customer/Subtree/OrderSubtree.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c056ea00663888d9d498dac759d78c1cd3442253b080a0bfddac6eee22b77924 -size 22107 +oid sha256:eb82276e36ca7c53a3674df1108655ddac7cade525c3f24b0045e24f6063cc29 +size 23457 diff --git a/Assets/_DDD/_Scripts/AI/Common/IAISharedBlackboard.cs b/Assets/_DDD/_Scripts/AI/Common/IAISharedBlackboard.cs index b21a58bd7..0f477171b 100644 --- a/Assets/_DDD/_Scripts/AI/Common/IAISharedBlackboard.cs +++ b/Assets/_DDD/_Scripts/AI/Common/IAISharedBlackboard.cs @@ -1,3 +1,4 @@ +using System; using UnityEngine; namespace DDD @@ -7,11 +8,11 @@ namespace DDD /// - 다양한 캐릭터 AI에서 공통으로 참조하는 현재 인터랙션 타겟만 정의합니다. /// - 필요 시 키-값 확장을 고려하되, 현재는 최소 요구만 충족합니다. /// - public interface IAISharedBlackboard + public interface IAISharedBlackboard where T : Enum { - void SetBlackboardValue(string key, T inValue); + void SetBlackboardValue(T key, T1 inValue); - T GetBlackboardValue(string key); + T1 GetBlackboardValue(T key); } } diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/ExpressEmotion.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/ExpressEmotion.cs index e668ae601..d14f39cf4 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/ExpressEmotion.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/ExpressEmotion.cs @@ -1,7 +1,8 @@ +using System; using Opsive.BehaviorDesigner.Runtime.Tasks; -using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; using Unity.VisualScripting; using UnityEngine; +using Action = Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Action; namespace DDD.Restaurant { @@ -11,7 +12,7 @@ public enum EmotionType Satisfied, } - public class ExpressEmotion : Action + public class ExpressEmotion : Action where T : Enum { public interface IEmotionVisual { @@ -21,13 +22,14 @@ public interface IEmotionVisual } //이를 파생해서 기본값을 주거나, 바로 사용하면 될 듯 - [SerializeField] protected string _emotionBlackboardKey; + [SerializeField] protected T _emotionBlackboardKey; private IEmotionVisual _emotionVisual; public override void OnStart() { - var currentEmotion = (EmotionType)m_BehaviorTree.GetVariable(_emotionBlackboardKey).Value; + var blackboard = gameObject.GetComponent>(); + var currentEmotion = blackboard.GetBlackboardValue(_emotionBlackboardKey); _emotionVisual = gameObject.GetComponentInChildren(); if (_emotionVisual == null) diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/LookAtInteractionTarget.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/LookAtInteractionTarget.cs index 8a8aff755..a47fa2003 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/LookAtInteractionTarget.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/LookAtInteractionTarget.cs @@ -1,6 +1,7 @@ +using System; using Opsive.BehaviorDesigner.Runtime.Tasks; -using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; using UnityEngine; +using Action = Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Action; namespace DDD.Restaurant { @@ -8,7 +9,7 @@ namespace DDD.Restaurant /// 인터랙션 타겟을 바라보도록 시각(Spine/애니메이션) 컴포넌트에 위임하는 액션의 껍데기/골격. /// 실제 회전/스파인 제어 로직은 별도의 Visual 컴포넌트(예: Spine/Animation Controller)에서 구현하십시오. /// - public class LookAtInteractionTarget : Action + public abstract class LookAtInteractionTarget : Action where T : Enum { [Header("Target Settings")] [Tooltip("InteractionPoints를 사용해 가장 적절한 지점을 바라봄")] @@ -27,19 +28,21 @@ public interface ILookAtVisual } private ILookAtVisual _visual; - private GameObject _cachedTarget; + protected GameObject _cachedTarget; private bool _isLooking; private Vector3 _currentLookPosition; - public override void OnStart() + public sealed override void OnStart() { _visual = gameObject.GetComponentInParent(); _isLooking = false; - var blackboard = gameObject.GetComponent(); + var blackboard = gameObject.GetComponent>(); + InitializeBlackboard(blackboard); - _cachedTarget = blackboard.GetBlackboardValue(nameof(RestaurantCustomerBlackboardKey.CurrentTargetGameObject)); } + + protected abstract void InitializeBlackboard(IAISharedBlackboard blackboard); public override TaskStatus OnUpdate() { diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/MoveToTargetPoint.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/MoveToTargetPoint.cs index 26b4271b8..ae52d41ba 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/MoveToTargetPoint.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/MoveToTargetPoint.cs @@ -1,14 +1,15 @@ +using System; using Opsive.BehaviorDesigner.Runtime.Tasks; -using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; using Unity.VisualScripting; using UnityEngine; +using Action = Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Action; namespace DDD.Restaurant { /// /// IAiMovement를 이용해 인터랙션 타겟으로 이동하는 액션 /// - public class MoveToTargetPoint : Action + public abstract class MoveToTargetPoint : Action where T : Enum { [Header("Target Settings")] [Tooltip("InteractionPoints를 사용해 가장 가까운 지점으로 이동")] @@ -32,17 +33,19 @@ public class MoveToTargetPoint : Action private float _repathTimer; private Vector3 _currentDestination; private bool _isMoving; - private GameObject _target; + protected GameObject _target; - public override void OnStart() + public sealed override void OnStart() { _movement = gameObject.GetComponent(); _repathTimer = 0f; _isMoving = false; - var blackboard = gameObject.GetComponent(); - _target = blackboard.GetBlackboardValue(nameof(RestaurantCustomerBlackboardKey.CurrentTargetGameObject)); + var blackboard = gameObject.GetComponent>(); + InitializeBlackboard(blackboard); } + + protected abstract void InitializeBlackboard(IAISharedBlackboard blackboard); public override TaskStatus OnUpdate() { diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantExpressEmotion.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantExpressEmotion.cs new file mode 100644 index 000000000..6b6068b41 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantExpressEmotion.cs @@ -0,0 +1,7 @@ +namespace DDD.Restaurant +{ + public class RestaurantExpressEmotion : ExpressEmotion + { + + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantExpressEmotion.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantExpressEmotion.cs.meta new file mode 100644 index 000000000..38836eff8 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantExpressEmotion.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 12b0bf82aef64d9d82a77a82943d7878 +timeCreated: 1756692031 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantLookAtInteractionTarget.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantLookAtInteractionTarget.cs new file mode 100644 index 000000000..6295e8470 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantLookAtInteractionTarget.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace DDD.Restaurant +{ + public class RestaurantLookAtInteractionTarget : LookAtInteractionTarget + { + protected override void InitializeBlackboard(IAISharedBlackboard blackboard) + { + _cachedTarget = blackboard.GetBlackboardValue(RestaurantCustomerBlackboardKey.CurrentTargetGameObject); + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantLookAtInteractionTarget.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantLookAtInteractionTarget.cs.meta new file mode 100644 index 000000000..81831c3ae --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantLookAtInteractionTarget.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0d73d4725548490da952ac2079f98497 +timeCreated: 1756692248 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantMoveToTargetPoint.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantMoveToTargetPoint.cs new file mode 100644 index 000000000..67cf3c055 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantMoveToTargetPoint.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace DDD.Restaurant +{ + public class RestaurantMoveToTargetPoint : MoveToTargetPoint + { + protected override void InitializeBlackboard(IAISharedBlackboard blackboard) + { + _target = blackboard.GetBlackboardValue(RestaurantCustomerBlackboardKey.CurrentTargetGameObject); + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantMoveToTargetPoint.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantMoveToTargetPoint.cs.meta new file mode 100644 index 000000000..047b67b76 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantMoveToTargetPoint.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e02da400458a473d9131601fc09192c0 +timeCreated: 1756692564 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantSearchAndRegisterMarker.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantSearchAndRegisterMarker.cs new file mode 100644 index 000000000..f1d1074fa --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantSearchAndRegisterMarker.cs @@ -0,0 +1,7 @@ +namespace DDD.Restaurant +{ + public class RestaurantSearchAndRegisterMarker : SearchAndRegisterMarker + { + + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantSearchAndRegisterMarker.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantSearchAndRegisterMarker.cs.meta new file mode 100644 index 000000000..d94b5feda --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/RestaurantSearchAndRegisterMarker.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e62500af8d764eac9b14024f155a168f +timeCreated: 1756693121 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/SearchAndRegisterMarker.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/SearchAndRegisterMarker.cs index 7e4190980..c4fbeea14 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/SearchAndRegisterMarker.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Actions/SearchAndRegisterMarker.cs @@ -1,13 +1,14 @@ +using System; using Opsive.BehaviorDesigner.Runtime.Tasks; -using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; using UnityEngine; +using Action = Opsive.BehaviorDesigner.Runtime.Tasks.Actions.Action; namespace DDD.Restaurant { - public class SearchAndRegisterMarker : Action + public class SearchAndRegisterMarker : Action where T : Enum { [SerializeField] private PointType _pointType; - [SerializeField] private RestaurantCustomerBlackboardKey _markerBlackboardKey; + [SerializeField] private T _markerBlackboardKey; private bool _isRegistered; public override void OnStart() @@ -15,13 +16,12 @@ public override void OnStart() var environmentState = RestaurantState.Instance?.EnvironmentState; if (environmentState == null) return; var pointProviders = environmentState.GetPointProviderByType(_pointType); - var blackboard = gameObject.GetComponent(); + var blackboard = gameObject.GetComponent>(); if (blackboard == null) return; foreach (var pointProvider in pointProviders) { - if (!pointProvider.IsSupportsType(_pointType)) continue; - blackboard.SetBlackboardValue(nameof(_markerBlackboardKey), pointProvider.GetGameObject()); + blackboard.SetBlackboardValue(_markerBlackboardKey, pointProvider.GetGameObject()); _isRegistered = true; break; } diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/BlackboardSO.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/BlackboardSO.cs index 45405d752..a4c8b5323 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/BlackboardSO.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/BlackboardSO.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; using Opsive.GraphDesigner.Runtime.Variables; +using Sirenix.OdinInspector; +using Sirenix.Serialization; using UnityEngine; namespace DDD.Restaurant { public class BlackboardSo : ScriptableObject where T : Enum { + [OdinSerialize, ShowInInspector] private Dictionary _variables = new(); public SharedVariable GetVariable(T key) @@ -25,6 +28,7 @@ public void SetVariable(T key, T1 value) if (outVariable != null) { outVariable.Value = value; + _variables[key] = outVariable; } else { diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Decorator/TimeLimiter.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Decorator/TimeLimiter.cs index 7f6700017..4a4a5bd03 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Decorator/TimeLimiter.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Common/Decorator/TimeLimiter.cs @@ -238,7 +238,7 @@ public void Execute(ref DynamicBuffer branchComponents, } [NodeDescription("자식 태스크의 실행 시간을 제한합니다 (GameObject 워크플로우)")] - public class SharedTimeLimiter : DecoratorNode + public class SharedTimeLimiter : DecoratorNode { [Tooltip("최대 실행 시간(초)")] [SerializeField] protected SharedVariable _timeLimit = 30f; @@ -248,7 +248,7 @@ public class SharedTimeLimiter : DecoratorNode [Tooltip("하단 블랙보드 키에 현재 시간을 저장할 지")] [SerializeField] protected bool _isBlackboardWriteEnabled = false; - [SerializeField] protected string _blackboardKey = "CurrentTime"; + [SerializeField] protected T _blackboardKey; public SharedVariable TimeLimit { diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/IncrementOrderCount.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/IncrementOrderCount.cs new file mode 100644 index 000000000..610427fc3 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/IncrementOrderCount.cs @@ -0,0 +1,20 @@ +using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; +using UnityEngine; + +namespace DDD.Restaurant +{ + public class IncrementOrderCount : Action + { + public override void OnStart() + { + var blackboard = gameObject.GetComponent>(); + if (blackboard == null) + { + Debug.LogWarning($"블랙보드가 존재하지 않음 해시코드: {gameObject.GetHashCode()}"); + return; + } + var cumulativeOrderCount = blackboard.GetBlackboardValue(RestaurantCustomerBlackboardKey.CumulativeOrderCount); + blackboard.SetBlackboardValue(RestaurantCustomerBlackboardKey.CumulativeOrderCount, ++cumulativeOrderCount); + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/IncrementOrderCount.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/IncrementOrderCount.cs.meta new file mode 100644 index 000000000..c2be6cc90 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/IncrementOrderCount.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f3f7e66da71042a5b5a679261d1bbf0b +timeCreated: 1756700443 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/LeaveRestaurant.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/LeaveRestaurant.cs new file mode 100644 index 000000000..a81ebb464 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/LeaveRestaurant.cs @@ -0,0 +1,15 @@ +using Opsive.BehaviorDesigner.Runtime.Tasks; +using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; +using UnityEngine; + +namespace DDD.Restaurant +{ + public class LeaveRestaurant : Action + { + public override TaskStatus OnUpdate() + { + Object.Destroy(gameObject); + return TaskStatus.Running; + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/LeaveRestaurant.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/LeaveRestaurant.cs.meta new file mode 100644 index 000000000..d69d02a89 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/LeaveRestaurant.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e39046ffc9bb46d39d2a5c4a9899cd68 +timeCreated: 1756702274 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/PublishRestaurantInterruptEvent.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/PublishRestaurantInterruptEvent.cs new file mode 100644 index 000000000..834782544 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/PublishRestaurantInterruptEvent.cs @@ -0,0 +1,33 @@ +using Opsive.BehaviorDesigner.Runtime.Tasks; +using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; +using UnityEngine; + +namespace DDD.Restaurant +{ + public class PublishRestaurantInterruptEvent : Action + { + [SerializeField] private RestaurantOrderType _targetInterruptType; + public override TaskStatus OnUpdate() + { + var blackboard = gameObject.GetComponent>(); + if (blackboard == null) return TaskStatus.Failure; + + var currentTarget = blackboard.GetBlackboardValue(RestaurantCustomerBlackboardKey.CurrentTargetGameObject); + if (currentTarget == null) return TaskStatus.Failure; + + var orderObject = currentTarget.GetComponent(); + if (orderObject == null) return TaskStatus.Failure; + + RestaurantOrderInterrupt evt = new() + { + Table = currentTarget, + OrderObject = orderObject.GetOrderObjectState(), + TransitionType = _targetInterruptType + }; + + EventBus.Broadcast(evt); + + return TaskStatus.Success; + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/PublishRestaurantInterruptEvent.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/PublishRestaurantInterruptEvent.cs.meta new file mode 100644 index 000000000..df7c9d6cb --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/PublishRestaurantInterruptEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aaaf550a60264509a1ebd35506000e2b +timeCreated: 1756706679 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/SatisfactionEvaluator.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/SatisfactionEvaluator.cs index 483191762..81b2e6abc 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/SatisfactionEvaluator.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/SatisfactionEvaluator.cs @@ -14,13 +14,13 @@ public enum EvaluationStep [NodeDescription("만족도 평가 테스크")] public class SatisfactionEvaluator : Action { - [SerializeField] private string _satisfactionBlackboardKey; + [SerializeField] private RestaurantCustomerBlackboardKey _satisfactionBlackboardKey; [SerializeField] private EvaluationStep _evaluationStep; public override void OnStart() { var currentSatisfaction = CalculateSatisfaction(); - var blackboard = gameObject.GetComponent(); - blackboard.SetBlackboardValue(_satisfactionBlackboardKey, (int)currentSatisfaction); + var blackboard = gameObject.GetComponent>(); + blackboard.SetBlackboardValue(_satisfactionBlackboardKey, currentSatisfaction); } //TODO 만족도 계산? diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/StartRestaurantOrder.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/StartRestaurantOrder.cs index 805caab1b..4f2081e47 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/StartRestaurantOrder.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/StartRestaurantOrder.cs @@ -22,8 +22,8 @@ public override void OnStart() public override TaskStatus OnUpdate() { - var blackboard = gameObject.GetComponent(); - var target = blackboard?.GetBlackboardValue(nameof(RestaurantCustomerBlackboardKey.CurrentTargetGameObject)); + var blackboard = gameObject.GetComponent>(); + var target = blackboard?.GetBlackboardValue(RestaurantCustomerBlackboardKey.CurrentTargetGameObject); IInteractable currentInteractable = target?.GetComponent(); if (_targetOrderType == RestaurantOrderType.Wait) { @@ -33,8 +33,8 @@ public override TaskStatus OnUpdate() { return TaskStatus.Failure; } - var customerBlackboard = gameObject.GetComponent(); - customerBlackboard?.SetBlackboardValue(nameof(RestaurantCustomerBlackboardKey.CurrentTargetGameObject), currentInteractable.GetInteractableGameObject()); + var customerBlackboard = gameObject.GetComponent>(); + customerBlackboard?.SetBlackboardValue(RestaurantCustomerBlackboardKey.CurrentTargetGameObject, currentInteractable.GetInteractableGameObject()); } // Check order type of the current interactable @@ -70,8 +70,8 @@ public override TaskStatus OnUpdate() if (_targetOrderType == RestaurantOrderType.Busy) { - var customerBlackboard = gameObject.GetComponent(); - customerBlackboard?.SetBlackboardValue(nameof(RestaurantCustomerBlackboardKey.CurrentTargetGameObject), null); + var customerBlackboard = gameObject.GetComponent>(); + customerBlackboard?.SetBlackboardValue(RestaurantCustomerBlackboardKey.CurrentTargetGameObject, null); } return TaskStatus.Success; diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/WaitForPlayerInteraction.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/WaitForPlayerInteraction.cs index fd8338fbf..3150c0bc3 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/WaitForPlayerInteraction.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Actions/WaitForPlayerInteraction.cs @@ -16,10 +16,10 @@ public class WaitForPlayerInteraction : Action public override void OnStart() { GameObject interactionTarget = null; - if (!gameObject.TryGetComponent(out var sharedBlackboard)) return; + if (!gameObject.TryGetComponent>(out var sharedBlackboard)) return; interactionTarget = sharedBlackboard.GetBlackboardValue( - nameof(RestaurantCustomerBlackboardKey.CurrentTargetGameObject)); + RestaurantCustomerBlackboardKey.CurrentTargetGameObject); if (interactionTarget == null) { diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Conditionals/CheckCumulateOrderCount.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Conditionals/CheckCumulateOrderCount.cs new file mode 100644 index 000000000..5f6b7dc59 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Conditionals/CheckCumulateOrderCount.cs @@ -0,0 +1,54 @@ +using Opsive.BehaviorDesigner.Runtime.Tasks; +using Opsive.BehaviorDesigner.Runtime.Tasks.Conditionals; +using UnityEngine; + +namespace DDD.Restaurant +{ + public class CheckCumulateOrderCount : Conditional + { + public enum ComparisonType + { + Less, + LessOrEqual, + Equal, + NotEqual, + Greater, + GreaterOrEqual + } + + [SerializeField] private int _compareOrderCount; + [SerializeField] private ComparisonType _comparisonType; + public override TaskStatus OnUpdate() + { + var blackboard = gameObject.GetComponent>(); + if (blackboard == null) + { + Debug.LogWarning($"블랙보드가 존재하지 않음 해시코드: {gameObject.GetHashCode()}"); + return TaskStatus.Failure; + } + var cumulativeOrderCount = blackboard.GetBlackboardValue(RestaurantCustomerBlackboardKey.CumulativeOrderCount); + return Evaluate(cumulativeOrderCount) ? TaskStatus.Success : TaskStatus.Failure; + } + + private bool Evaluate(int compareValue) + { + switch (_comparisonType) + { + case ComparisonType.Less: + return compareValue < _compareOrderCount; + case ComparisonType.LessOrEqual: + return compareValue <= _compareOrderCount; + case ComparisonType.Equal: + return compareValue == _compareOrderCount; + case ComparisonType.NotEqual: + return compareValue != _compareOrderCount; + case ComparisonType.Greater: + return compareValue > _compareOrderCount; + case ComparisonType.GreaterOrEqual: + return compareValue >= _compareOrderCount; + default: + return false; + } + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Conditionals/CheckCumulateOrderCount.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Conditionals/CheckCumulateOrderCount.cs.meta new file mode 100644 index 000000000..7b8028ec7 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Conditionals/CheckCumulateOrderCount.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4b3c5ee2ca89419394819e57dbab0bcd +timeCreated: 1756701161 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardComponent.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardComponent.cs index ed6398a68..e9bda17dd 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardComponent.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardComponent.cs @@ -1,36 +1,68 @@ +using DDD.Restaurant; using Opsive.BehaviorDesigner.Runtime; using Opsive.GraphDesigner.Runtime.Variables; +using Sirenix.OdinInspector; using UnityEngine; namespace DDD { - public class CustomerBlackboardComponent : MonoBehaviour, ICustomerBlackboard, IAISharedBlackboard + public class CustomerBlackboardComponent : MonoBehaviour, ICustomerBlackboard, IAISharedBlackboard { private BehaviorTree _behaviorTree; + [SerializeField, InlineEditor] + private CustomerBlackboardSo _blackboardSo; public void InitializeWithBehaviorTree(BehaviorTree inBehaviorTree) { _behaviorTree = inBehaviorTree; - SetBlackboardValue(nameof(RestaurantCustomerBlackboardKey.SelfGameObject), gameObject); + + _blackboardSo = ScriptableObject.CreateInstance(); + _blackboardSo.SetVariable(RestaurantCustomerBlackboardKey.SelfGameObject, gameObject); + + if (!_behaviorTree) return; + _behaviorTree.SetVariableValue(nameof(BlackboardKey.RestaurantBlackboard), _blackboardSo); } public void SetCustomerData(string inCustomerDataId) { - SetBlackboardValue(nameof(RestaurantCustomerBlackboardKey.CustomerDataId), inCustomerDataId); + _blackboardSo.SetVariable(RestaurantCustomerBlackboardKey.CustomerDataId, inCustomerDataId); } - public void SetBlackboardValue(string key, T inValue) + public void SetBlackboardValue(RestaurantCustomerBlackboardKey key, T inValue) { if (!_behaviorTree) return; - _behaviorTree.SetVariableValue(key, inValue); + + var blackboardSo = + _behaviorTree.GetVariable(nameof(BlackboardKey.RestaurantBlackboard)); + if (blackboardSo == null || blackboardSo.Value == null) + { + Debug.LogWarning($"behaviorTree에 blackboardSo가 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}"); + return; + } + blackboardSo.Value.SetVariable(key, inValue); } - public T GetBlackboardValue(string key) + public T GetBlackboardValue(RestaurantCustomerBlackboardKey key) { if (!_behaviorTree) return default; - SharedVariable blackboardValue = _behaviorTree.GetVariable(key); + var blackboardSo = + _behaviorTree.GetVariable(nameof(BlackboardKey.RestaurantBlackboard)); - return blackboardValue != null ? blackboardValue.Value : default; + if (blackboardSo == null || blackboardSo.Value == null) + { + Debug.LogWarning($"behaviorTree에 blackboardSo가 존재하지 않음 오브젝트 해시코드: {gameObject.GetHashCode()}"); + return default; + } + var value = blackboardSo.Value.GetVariable(key); + return value != null ? value.Value : default; + } + + private void OnDestroy() + { + if (_blackboardSo) + { + Destroy(_blackboardSo); + } } } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardSo.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardSo.cs new file mode 100644 index 000000000..51919eaad --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardSo.cs @@ -0,0 +1,12 @@ + +using System; +using UnityEngine; + +namespace DDD.Restaurant +{ + [Serializable] + public class CustomerBlackboardSo : BlackboardSo + { + + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardSo.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardSo.cs.meta new file mode 100644 index 000000000..44c14ea18 --- /dev/null +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/CustomerBlackboardSo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b5d5fca67b054314aef1ee6f3fda2b76 +timeCreated: 1756689633 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Decorator/CustomerTimeLimiter.cs b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Decorator/CustomerTimeLimiter.cs index f3e211edb..b4a56660e 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Decorator/CustomerTimeLimiter.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/AI/Customer/Decorator/CustomerTimeLimiter.cs @@ -3,9 +3,9 @@ namespace DDD.Restaurant { - public class CustomerTimeLimiter : SharedTimeLimiter + public class CustomerTimeLimiter : SharedTimeLimiter { - private IAISharedBlackboard _blackboard; + private IAISharedBlackboard _blackboard; public override void OnStart() { base.OnStart(); diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/Interfaces/ICustomerBlackboard.cs b/Assets/_DDD/_Scripts/Restaurant/Character/Interfaces/ICustomerBlackboard.cs index 04cfef17a..768c70b7f 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/Interfaces/ICustomerBlackboard.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/Interfaces/ICustomerBlackboard.cs @@ -2,13 +2,18 @@ namespace DDD { + public enum BlackboardKey + { + RestaurantBlackboard, + } public enum RestaurantCustomerBlackboardKey { SelfGameObject, CustomerDataId, CurrentTargetGameObject, SatisfactionLevel, - OrderCount, + CumulativeOrderCount, + Remaining } public interface ICustomerBlackboard diff --git a/Assets/_DDD/_Scripts/Restaurant/Character/Npc/NpcMovement.cs b/Assets/_DDD/_Scripts/Restaurant/Character/Npc/NpcMovement.cs index 4248d0aa1..0102a617b 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Character/Npc/NpcMovement.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Character/Npc/NpcMovement.cs @@ -60,6 +60,7 @@ public bool TryMoveToPosition(Vector3 position) _iAstarAi.destination = position; PlayMove(); + _iAstarAi.SearchPath(); return true; } diff --git a/Assets/_DDD/_Scripts/Restaurant/Environment/Interactions/InteractionSubsystem_Order.cs b/Assets/_DDD/_Scripts/Restaurant/Environment/Interactions/InteractionSubsystem_Order.cs index 6b8a4ad8d..867d6abe4 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Environment/Interactions/InteractionSubsystem_Order.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Environment/Interactions/InteractionSubsystem_Order.cs @@ -2,7 +2,7 @@ using UnityEngine; using UnityEngine.Serialization; -namespace DDD +namespace DDD.Restaurant { public enum RestaurantOrderType : uint { @@ -21,6 +21,16 @@ public class RestaurantOrderObjectState public string FoodId; } + + public class RestaurantOrderInterrupt : IEvent + { + public GameObject Table; + public RestaurantOrderObjectState OrderObject; + public RestaurantOrderType TransitionType; + } + + // RestaurantOrderInterrupt 이벤트 ? OrderObject, Worker는 여기에 등록해두기. Customer가 이벤트 호출 + // InterruptResult - Clean, Dirty? public interface IRestaurantOrderObject { @@ -31,7 +41,7 @@ public interface IRestaurantOrderObject // TODO : 도중에 이탈할 경우, 순차적으로 다음 페이즈로 넘어가는 게 아니라 바로 Wait, Dirty로 전이시킬 필요가 있음 - public class InteractionSubsystem_Order : MonoBehaviour, IInteractionSubsystemObject, IRestaurantOrderObject + public class InteractionSubsystem_Order : MonoBehaviour, IInteractionSubsystemObject, IRestaurantOrderObject, IEventHandler { [SerializeField] private RestaurantOrderType _currentRestaurantOrderType = RestaurantOrderType.Wait; [SerializeField] private RestaurantOrderObjectState _orderObjectState = new(); @@ -80,6 +90,7 @@ public ScriptableObject GetPayload() public void InitializeSubsystem() { + EventBus.Register(this); } public RestaurantOrderType GetInteractionSubsystemType() @@ -110,5 +121,17 @@ public bool CanTransitionToNextPhase() { return true; } + + public void HandleEvent(RestaurantOrderInterrupt evt) + { + if (evt.Table != gameObject) return; + Debug.Log($"Order Interrupt : {evt.TransitionType}, Current State : {GetInteractionSubsystemType()}"); + SetInteractionSubsystemType(evt.TransitionType); + } + + private void OnDestroy() + { + EventBus.Unregister(this); + } } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantEnvironmentPointQueryEvent.cs b/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantEnvironmentPointQueryEvent.cs deleted file mode 100644 index 7ef165c00..000000000 --- a/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantEnvironmentPointQueryEvent.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UnityEngine; - -namespace DDD.Restaurant -{ - //public class RestaurantEnvironmentPointQueryEvent : RestaurantEventBase - //{ - // protected override bool EventSolve(GameObject causer, GameObject target, PointType eventType, ScriptableObject payload) - // { - // if (!target.TryGetComponent(out IEnvironmentPointProvider provider)) return false; - // if (!provider.IsSupportsType(eventType)) return false; - // - // return true; - // } - //} -} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantEnvironmentPointQueryEvent.cs.meta b/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantEnvironmentPointQueryEvent.cs.meta deleted file mode 100644 index 55939787b..000000000 --- a/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantEnvironmentPointQueryEvent.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 17b28cfc7a5a4d43a050d247e22004dc -timeCreated: 1756453073 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantInteractionEvents.cs b/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantInteractionEvents.cs index f33d3189c..ede0745b1 100644 --- a/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantInteractionEvents.cs +++ b/Assets/_DDD/_Scripts/Restaurant/Event/RestaurantInteractionEvents.cs @@ -24,30 +24,20 @@ public class RestaurantInteractionEvent : RestaurantEventBase protected override bool EventSolve(GameObject causer, GameObject target, InteractionType interactionType, ScriptableObject payload) { - IInteractor interactor = causer.GetComponent(); - if (interactor != null && interactor.CanSolveInteractionType(interactionType)) + var interactor = causer.GetComponent(); + if (interactor == null || !interactor.CanSolveInteractionType(interactionType)) return false; + if (!interactor.FetchSolverTypeForInteraction(interactionType, out var solverType)) return false; + // Solve event directly. 이벤트 처리는 여기서 하고, 이벤트 호출로는 이런 이벤트가 호출되었고 결과가 어떻다는 거 전파하는 식으로. + if (solverType == null) return false; + var solver = causer.GetComponent(solverType) as IInteractionSolver; + var interactable = target.GetComponent(); + if (solver is not null) { - if (interactor.FetchSolverTypeForInteraction(interactionType, out var solverType)) - { - // Solve event directly. 이벤트 처리는 여기서 하고, 이벤트 호출로는 이런 이벤트가 호출되었고 결과가 어떻다는 거 전파하는 식으로. - if (solverType != null) - { - IInteractionSolver solver = causer.GetComponent(solverType) as IInteractionSolver; - IInteractable interactable = target.GetComponent(); - if (solver is not null) - { - bool canExecute = solver.CanExecuteInteraction(interactor, interactable, payload); - return canExecute && solver.ExecuteInteraction(interactor, interactable, payload); - } - else - { - // Should not reach here! - Debug.Assert(false, "Solver Component or Interactor is null"); - } - } - } + bool canExecute = solver.CanExecuteInteraction(interactor, interactable, payload); + return canExecute && solver.ExecuteInteraction(interactor, interactable, payload); } + Debug.Assert(false, "Solver Component or Interactor is null"); return false; } }