Merge remote-tracking branch 'origin/develop' into develop
# Conflicts: # Assets/_DDD/_Addressables/AI/Customer/Subtree/ExitSubtree.asset
This commit is contained in:
commit
cd346e5019
@ -0,0 +1,93 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1001 &2029713930320939465
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3697702677815423220, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3761059052922690693, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_Sprite
|
||||
value:
|
||||
objectReference: {fileID: 21300000, guid: 42a128f365d0c4c52b151466427d1cc9, type: 3}
|
||||
- target: {fileID: 4103096974375017811, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: PointMarker Variant
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7433508832753786351, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
propertyPath: _pointType
|
||||
value: 2
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents:
|
||||
- {fileID: 7433508832753786351, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 4103096974375017811, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 157773281879251590}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
--- !u!1 &2655961481751516314 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 4103096974375017811, guid: 186d28777ccbc484780568f74c110ff7, type: 3}
|
||||
m_PrefabInstance: {fileID: 2029713930320939465}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &157773281879251590
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2655961481751516314}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8dc8393b9e144fd1a225f29babfb089d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_pointType: 2
|
||||
_maxWaitingCount: 10
|
||||
_currentWaitingCount: 0
|
||||
_waitingRangeRadius: 20
|
||||
_targetGizmoColor: {r: 1, g: 0.92156863, b: 0.015686275, a: 1}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba6905dd36b009a4aa3b6ad80520ee3c
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
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.
@ -10475,6 +10475,71 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 6689525833630355058, guid: 1adf83789591d4cef928dc9f32c49610, type: 3}
|
||||
m_PrefabInstance: {fileID: 200803425}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1001 &1664905271
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 157773281879251590, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: _maxWaitingCount
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 157773281879251590, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: _waitingRangeRadius
|
||||
value: 5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2655961481751516314, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: WaitingQueue
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 7.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 21
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3421080875838994749, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: ba6905dd36b009a4aa3b6ad80520ee3c, type: 3}
|
||||
--- !u!1001 &1672772949
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -15717,3 +15782,4 @@ SceneRoots:
|
||||
- {fileID: 549344125}
|
||||
- {fileID: 1775054121}
|
||||
- {fileID: 1367038807}
|
||||
- {fileID: 1664905271}
|
||||
|
@ -119,6 +119,19 @@ TextureImporter:
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
|
@ -119,6 +119,19 @@ TextureImporter:
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
|
@ -119,6 +119,19 @@ TextureImporter:
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
|
@ -119,6 +119,19 @@ TextureImporter:
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
|
@ -119,6 +119,19 @@ TextureImporter:
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
|
@ -18,12 +18,14 @@ public interface IEmotionVisual
|
||||
{
|
||||
bool HasEmotionAvailable(EmotionType emotionType);
|
||||
void ShowEmotion(EmotionType emotionType);
|
||||
|
||||
float GetEmotionDuration();
|
||||
void EndEmotion();
|
||||
}
|
||||
|
||||
//이를 파생해서 기본값을 주거나, 바로 사용하면 될 듯
|
||||
[SerializeField] protected T _emotionBlackboardKey;
|
||||
private IEmotionVisual _emotionVisual;
|
||||
private float _emotionDuration;
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
@ -44,10 +46,12 @@ public override void OnStart()
|
||||
}
|
||||
|
||||
_emotionVisual.ShowEmotion(currentEmotion);
|
||||
_emotionDuration = Time.time + _emotionVisual.GetEmotionDuration();
|
||||
}
|
||||
|
||||
public override TaskStatus OnUpdate()
|
||||
{
|
||||
if (Time.time < _emotionDuration) return TaskStatus.Running;
|
||||
return TaskStatus.Success;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,145 @@
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public class MoveToPoint : Action
|
||||
{
|
||||
private IAiMovement _movement;
|
||||
private Vector3 _destination;
|
||||
private bool _isInitialized;
|
||||
private bool _isMoving;
|
||||
[SerializeField] private float _stoppingDistance = 0.01f;
|
||||
|
||||
[Tooltip("디버그 선 색상")]
|
||||
[SerializeField] private Color _debugLineColor = Color.red;
|
||||
[Tooltip("타겟 위치 기즈모 색상")]
|
||||
[SerializeField] private Color _targetGizmoColor = Color.yellow;
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
if (!gameObject.TryGetComponent(out _movement))
|
||||
{
|
||||
Debug.LogError($"[{GetType().Name}] NavMeshAgent를 찾을 수 없습니다: {gameObject.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
var blackboard = gameObject.GetComponent<IAISharedBlackboard<RestaurantCustomerBlackboardKey>>();
|
||||
if (blackboard == null)
|
||||
{
|
||||
Debug.LogError($"[{GetType().Name}] Blackboard를 찾을 수 없습니다: {gameObject.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
var waitingQueue = blackboard.GetBlackboardValue<IWaitingQueue>(RestaurantCustomerBlackboardKey.WaitingQueue);
|
||||
if (waitingQueue == null)
|
||||
{
|
||||
Debug.LogError($"[{GetType().Name}] WaitingQueue를 찾을 수 없습니다: {gameObject.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
_destination = waitingQueue.GetRandomPointPosition();
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
public override TaskStatus OnUpdate()
|
||||
{
|
||||
if (!_isInitialized)
|
||||
return TaskStatus.Failure;
|
||||
|
||||
StartOrUpdateMovement();
|
||||
|
||||
return CheckMovementCompletion();
|
||||
}
|
||||
|
||||
private void StartOrUpdateMovement()
|
||||
{
|
||||
if (!_isMoving)
|
||||
{
|
||||
if (_movement.TryMoveToPosition(_destination))
|
||||
{
|
||||
_movement.EnableMove();
|
||||
_movement.PlayMove();
|
||||
_isMoving = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_movement.TryMoveToPosition(_destination);
|
||||
}
|
||||
}
|
||||
|
||||
private TaskStatus CheckMovementCompletion()
|
||||
{
|
||||
Vector3 distance2D = _destination - GetAgentPosition();
|
||||
distance2D.y = 0f;
|
||||
var distanceSqr = (distance2D).sqrMagnitude;
|
||||
var stoppingDistanceSqr = _stoppingDistance * _stoppingDistance;
|
||||
|
||||
if (distanceSqr <= stoppingDistanceSqr || _movement.HasReachedDestination())
|
||||
{
|
||||
StopMovement();
|
||||
return TaskStatus.Success;
|
||||
}
|
||||
|
||||
return TaskStatus.Running;
|
||||
}
|
||||
|
||||
private void StopMovement()
|
||||
{
|
||||
if (_movement != null && _isMoving)
|
||||
{
|
||||
_movement.StopMove();
|
||||
_movement.DisableMove();
|
||||
_isMoving = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 GetAgentPosition() =>
|
||||
_movement?.CurrentPosition ?? transform.position;
|
||||
|
||||
protected override void OnDrawGizmos()
|
||||
{
|
||||
if (!_isInitialized) return;
|
||||
|
||||
// 타겟 위치에 기즈모 그리기
|
||||
Gizmos.color = _targetGizmoColor;
|
||||
if (_isMoving && _destination != Vector3.zero)
|
||||
{
|
||||
Gizmos.DrawWireSphere(_destination, 0.5f);
|
||||
}
|
||||
|
||||
Gizmos.color = _debugLineColor;
|
||||
Vector3 targetPos = _isMoving && _destination != Vector3.zero
|
||||
? _destination
|
||||
: Vector3.zero;
|
||||
|
||||
if (targetPos != Vector3.zero)
|
||||
{
|
||||
Gizmos.DrawLine(transform.position, targetPos);
|
||||
}
|
||||
|
||||
// 현재 오브젝트 위치에 작은 기즈모 그리기
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawWireCube(transform.position, Vector3.one * 0.3f);
|
||||
}
|
||||
|
||||
protected override void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!_isInitialized) return;
|
||||
|
||||
// 선택되었을 때 추가 정보 표시
|
||||
#if UNITY_EDITOR
|
||||
Vector3 targetPos = _isMoving && _destination != Vector3.zero
|
||||
? _destination
|
||||
: Vector3.zero;
|
||||
|
||||
float distance = Vector3.Distance(transform.position, targetPos);
|
||||
UnityEditor.Handles.Label(targetPos + Vector3.up * 1f,
|
||||
$"Distance: {distance:F2}m\nStopping: {_stoppingDistance:F2}m");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17040c9d459f4976b8745dd341809e16
|
||||
timeCreated: 1756801920
|
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using Opsive.BehaviorDesigner.Runtime.Components;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks.Decorators;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public abstract class BlackboardValueExist<T, V> : DecoratorNode where T : Enum where V : class
|
||||
{
|
||||
[SerializeField] private T _blackboardKey;
|
||||
private bool _isBlackboardValueExists;
|
||||
|
||||
public sealed override void OnStart()
|
||||
{
|
||||
var blackboard = gameObject.GetComponent<IAISharedBlackboard<T>>();
|
||||
if (blackboard == null) return;
|
||||
var value = blackboard.GetBlackboardValue<V>(_blackboardKey);
|
||||
if (value == null) return;
|
||||
_isBlackboardValueExists = true;
|
||||
}
|
||||
|
||||
|
||||
public sealed override TaskStatus OnUpdate()
|
||||
{
|
||||
if (!_isBlackboardValueExists) return TaskStatus.Failure;
|
||||
// 자식(TaskComponent)은 Decorator 바로 다음 인덱스로 가정
|
||||
var taskBuffer = m_BehaviorTree.World.EntityManager.GetBuffer<TaskComponent>(m_BehaviorTree.Entity);
|
||||
|
||||
var childStatus = taskBuffer[Index + 1].Status;
|
||||
|
||||
if (childStatus == TaskStatus.Success) return TaskStatus.Success;
|
||||
return TaskStatus.Running;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5477779bedee4697adc2461e2b37d26b
|
||||
timeCreated: 1756802812
|
@ -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
|
@ -20,7 +20,7 @@ public override TaskStatus OnUpdate()
|
||||
|
||||
RestaurantOrderInterrupt evt = new()
|
||||
{
|
||||
Table = currentTarget,
|
||||
InteractableObject = currentTarget,
|
||||
OrderObject = orderObject.GetOrderObjectState(),
|
||||
TransitionType = _targetInterruptType
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ namespace DDD.Restaurant
|
||||
{
|
||||
public enum EvaluationStep
|
||||
{
|
||||
None,
|
||||
FoodSatisfactionCheck,
|
||||
FavoriteTasteCheck,
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public class WaitingQueueRegisterer : Action
|
||||
{
|
||||
private bool _isRegistered;
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
var blackboard = gameObject.GetComponent<IAISharedBlackboard<RestaurantCustomerBlackboardKey>>();
|
||||
var environmentState = RestaurantState.Instance?.EnvironmentState;
|
||||
if (environmentState == null) return;
|
||||
|
||||
var waitingAreas = environmentState.GetPointProviderByType(PointType.WaitingArea);
|
||||
foreach (var waitingArea in waitingAreas)
|
||||
{
|
||||
if (waitingArea is not IWaitingQueue waitingQueue) continue;
|
||||
if (!waitingQueue.RegisterWaitingQueue(gameObject)) continue;
|
||||
blackboard.SetBlackboardValue(RestaurantCustomerBlackboardKey.WaitingQueue, waitingQueue);
|
||||
_isRegistered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override TaskStatus OnUpdate()
|
||||
{
|
||||
return _isRegistered ? TaskStatus.Success : TaskStatus.Failure;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa4a39f9e9c8427db6abbc79077d2c3c
|
||||
timeCreated: 1756800604
|
@ -0,0 +1,21 @@
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public class WaitingQueueUnRegisterer : Action
|
||||
{
|
||||
public override void OnStart()
|
||||
{
|
||||
var blackboard = gameObject.GetComponent<IAISharedBlackboard<RestaurantCustomerBlackboardKey>>();
|
||||
if (blackboard == null)
|
||||
{
|
||||
Debug.LogWarning($"[{GetType().Name}] 블랙보드가 없습니다. 오브젝트 해시코드 : {gameObject.GetHashCode()}");
|
||||
return;
|
||||
}
|
||||
var waitingQueue = blackboard.GetBlackboardValue<IWaitingQueue>(RestaurantCustomerBlackboardKey.CumulativeOrderCount);
|
||||
if (waitingQueue == null) return;
|
||||
waitingQueue.UnregisterWaitingQueue(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a6e15a2b3f5436db9b80cb8daaee571
|
||||
timeCreated: 1756801165
|
@ -1,5 +1,6 @@
|
||||
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
@ -7,13 +8,14 @@ namespace DDD.Restaurant
|
||||
[Serializable]
|
||||
public class CustomerBlackboardSo : BlackboardSo<RestaurantCustomerBlackboardKey>
|
||||
{
|
||||
public GameObject SelfGameObject;
|
||||
public string CustomerDataId;
|
||||
public GameObject CurrentTargetGameObject;
|
||||
public EmotionType SatisfactionLevel;
|
||||
public int CumulativeOrderCount;
|
||||
public float MaxPatienceTime;
|
||||
public float RemainingPatienceTime;
|
||||
[SerializeField, ReadOnly]public GameObject SelfGameObject;
|
||||
[SerializeField, ReadOnly]public string CustomerDataId;
|
||||
[SerializeField, ReadOnly]public GameObject CurrentTargetGameObject;
|
||||
[SerializeField, ReadOnly]public EmotionType SatisfactionLevel;
|
||||
[SerializeField, ReadOnly]public int CumulativeOrderCount;
|
||||
[SerializeField, ReadOnly]public float MaxPatienceTime;
|
||||
[SerializeField, ReadOnly]public float RemainingPatienceTime;
|
||||
[SerializeField, ReadOnly]public RestaurantWaitingQueue WaitingQueue;
|
||||
|
||||
public override void SetVariable<T1>(RestaurantCustomerBlackboardKey key, T1 value)
|
||||
{
|
||||
@ -41,6 +43,9 @@ public override void SetVariable<T1>(RestaurantCustomerBlackboardKey key, T1 val
|
||||
case RestaurantCustomerBlackboardKey.RemainingPatienceTime:
|
||||
RemainingPatienceTime = (float)(object)value;
|
||||
break;
|
||||
case RestaurantCustomerBlackboardKey.WaitingQueue:
|
||||
WaitingQueue = value as RestaurantWaitingQueue;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(key), key, null);
|
||||
}
|
||||
|
@ -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)
|
@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public class CustomerTargetObjectExist : BlackboardValueExist<RestaurantCustomerBlackboardKey, GameObject>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce800e5a35634feea9723ef410098569
|
||||
timeCreated: 1756802975
|
@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public enum CharacterActionState
|
||||
{
|
||||
None = 0,
|
||||
CleaningTable = 1,
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2e8668bef454e00a5f8b8cd49919645
|
||||
timeCreated: 1756795869
|
@ -11,8 +11,13 @@ public interface IInteractionStateProvider
|
||||
float GetMovementSpeedMultiplier();
|
||||
bool IsMovementBlocked();
|
||||
}
|
||||
|
||||
public interface ICharacterActionStateProvider
|
||||
{
|
||||
CharacterActionState GetCurrentActionState();
|
||||
}
|
||||
|
||||
public class CharacterInteraction : MonoBehaviour, IInteractor, IEventHandler<RestaurantInteractionEvent>, IInteractionStateProvider
|
||||
public class CharacterInteraction : MonoBehaviour, IInteractor, IEventHandler<RestaurantInteractionEvent>, IInteractionStateProvider, ICharacterActionStateProvider
|
||||
{
|
||||
[EnumToggleButtons, SerializeField] protected InteractionType _availableInteractions;
|
||||
[SerializeField, ReadOnly] protected Collider[] _nearColliders = new Collider[10];
|
||||
@ -63,7 +68,7 @@ public IInteractable GetFocusedInteractable()
|
||||
return _nearestInteractable;
|
||||
}
|
||||
|
||||
private bool TryGetSolverFor(IInteractable interactable, out IInteractionSolver solver)
|
||||
protected bool TryGetSolverFor(IInteractable interactable, out IInteractionSolver solver)
|
||||
{
|
||||
solver = null;
|
||||
if (interactable == null) return false;
|
||||
@ -71,7 +76,7 @@ private bool TryGetSolverFor(IInteractable interactable, out IInteractionSolver
|
||||
return TryGetSolverForType(interactable.GetInteractionType(), out solver);
|
||||
}
|
||||
|
||||
private bool TryGetSolverForType(InteractionType type, out IInteractionSolver solver)
|
||||
protected bool TryGetSolverForType(InteractionType type, out IInteractionSolver solver)
|
||||
{
|
||||
if (_cachedSolvers.TryGetValue(type, out solver)) return solver != null;
|
||||
|
||||
@ -153,5 +158,17 @@ protected void InitializeInteractionSolvers(Dictionary<InteractionType, Type> ty
|
||||
public virtual bool IsInteracting() => _interactingTarget != null;
|
||||
public virtual float GetMovementSpeedMultiplier() => 1f;
|
||||
public virtual bool IsMovementBlocked() => false;
|
||||
|
||||
public CharacterActionState GetCurrentActionState()
|
||||
{
|
||||
if (IsInteracting())
|
||||
{
|
||||
if(_nearestInteractable is ICharacterActionStateProvider actionStateProvider)
|
||||
{
|
||||
return actionStateProvider.GetCurrentActionState();
|
||||
}
|
||||
}
|
||||
return CharacterActionState.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ public enum RestaurantCustomerBlackboardKey
|
||||
SatisfactionLevel,
|
||||
CumulativeOrderCount,
|
||||
MaxPatienceTime,
|
||||
RemainingPatienceTime
|
||||
RemainingPatienceTime,
|
||||
Reward,
|
||||
WaitingQueue
|
||||
}
|
||||
|
||||
public interface ICustomerBlackboard
|
||||
|
@ -11,12 +11,6 @@ public enum PlayerTaskAnimationState
|
||||
Dashing
|
||||
}
|
||||
|
||||
public enum PlayerActionAnimationState
|
||||
{
|
||||
None = 0,
|
||||
CleaningTable = 1,
|
||||
}
|
||||
|
||||
public enum PlayerDefaultAnimationState
|
||||
{
|
||||
None = 0,
|
||||
@ -31,7 +25,7 @@ public class PlayerAnimation : CharacterAnimation
|
||||
private IMovementState _movementState;
|
||||
|
||||
private PlayerTaskAnimationState _currentTaskAnimationState = PlayerTaskAnimationState.None;
|
||||
private PlayerActionAnimationState _currentActionAnimationState = PlayerActionAnimationState.None;
|
||||
private CharacterActionState _currentActionState = CharacterActionState.None;
|
||||
private PlayerDefaultAnimationState _currentDefaultAnimationState = PlayerDefaultAnimationState.None;
|
||||
|
||||
private Dictionary<PlayerTaskAnimationState, string> _taskToAnimation = new()
|
||||
@ -39,9 +33,9 @@ public class PlayerAnimation : CharacterAnimation
|
||||
{ PlayerTaskAnimationState.Dashing, "Dash" },
|
||||
};
|
||||
|
||||
private Dictionary<PlayerActionAnimationState, string> _actionToAnimation = new()
|
||||
private Dictionary<CharacterActionState, string> _actionToAnimation = new()
|
||||
{
|
||||
{ PlayerActionAnimationState.CleaningTable, "Cleaning/CleaningTable" },
|
||||
{ CharacterActionState.CleaningTable, "Cleaning/CleaningTable" },
|
||||
};
|
||||
|
||||
private Dictionary<PlayerDefaultAnimationState, string> _defaultToAnimation = new()
|
||||
@ -90,24 +84,24 @@ private void UpdateAnimations()
|
||||
// 1순위: Task 애니메이션이 재생 중이면 다른 애니메이션 처리하지 않음
|
||||
if (_currentTaskAnimationState != PlayerTaskAnimationState.None)
|
||||
{
|
||||
_currentActionAnimationState = PlayerActionAnimationState.None;
|
||||
_currentActionState = CharacterActionState.None;
|
||||
_currentDefaultAnimationState = PlayerDefaultAnimationState.None;
|
||||
return;
|
||||
}
|
||||
|
||||
// 2순위: ActionState 체크
|
||||
var desiredActionState = GetDesiredActionState();
|
||||
if (desiredActionState == PlayerActionAnimationState.None)
|
||||
if (desiredActionState == CharacterActionState.None)
|
||||
{
|
||||
_currentActionAnimationState = PlayerActionAnimationState.None;
|
||||
_currentActionState = CharacterActionState.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentActionAnimationState != desiredActionState)
|
||||
if (_currentActionState != desiredActionState)
|
||||
{
|
||||
_currentDefaultAnimationState = PlayerDefaultAnimationState.None;
|
||||
_currentActionAnimationState = desiredActionState;
|
||||
PlayActionAnimation(_currentActionAnimationState);
|
||||
_currentActionState = desiredActionState;
|
||||
PlayActionAnimation(_currentActionState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -146,25 +140,21 @@ private async Task PlayTaskAnimation(PlayerTaskAnimationState state, float durat
|
||||
_currentTaskAnimationState = PlayerTaskAnimationState.None;
|
||||
}
|
||||
|
||||
private PlayerActionAnimationState GetDesiredActionState()
|
||||
private CharacterActionState GetDesiredActionState()
|
||||
{
|
||||
if (_interactor.IsInteracting() == false) return PlayerActionAnimationState.None;
|
||||
|
||||
var interactable = _interactor.GetFocusedInteractable();
|
||||
if (interactable == null) return PlayerActionAnimationState.None;
|
||||
if (_interactor.IsInteracting() == false) return CharacterActionState.None;
|
||||
|
||||
var interactionType = interactable.GetInteractionType();
|
||||
if (interactionType == InteractionType.RestaurantTrash) // TODO : 서브시스템 연결해서 테이블청소 연결해야함
|
||||
if (_interactor is ICharacterActionStateProvider actionStateProvider)
|
||||
{
|
||||
return PlayerActionAnimationState.CleaningTable;
|
||||
return actionStateProvider.GetCurrentActionState();
|
||||
}
|
||||
|
||||
return PlayerActionAnimationState.None;
|
||||
return CharacterActionState.None;
|
||||
}
|
||||
|
||||
private void PlayActionAnimation(PlayerActionAnimationState actionAnimationState)
|
||||
private void PlayActionAnimation(CharacterActionState actionState)
|
||||
{
|
||||
if (_actionToAnimation.TryGetValue(actionAnimationState, out string animationName))
|
||||
if (_actionToAnimation.TryGetValue(actionState, out string animationName))
|
||||
{
|
||||
_spineController.PlayAnimation(animationName, true);
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ public void Initialize()
|
||||
var pointProviders = environmentState.GetPointProviderByType(PointType.Entry);
|
||||
foreach (var pointProvider in pointProviders)
|
||||
{
|
||||
if (!pointProvider.IsSupportsType(PointType.Entry)) continue;
|
||||
_spawnPoint = pointProvider.GetPosition();
|
||||
if (!pointProvider.CanProvidePoint(PointType.Entry)) continue;
|
||||
_spawnPoint = pointProvider.GetPointPosition();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,21 @@ public enum PointType
|
||||
{
|
||||
Entry,
|
||||
Exit,
|
||||
WaitingArea,
|
||||
}
|
||||
|
||||
public interface IEnvironmentPointProvider
|
||||
{
|
||||
bool IsSupportsType(PointType pointType);
|
||||
Vector3 GetPosition();
|
||||
bool CanProvidePoint(PointType pointType);
|
||||
Vector3 GetPointPosition();
|
||||
|
||||
GameObject GetGameObject();
|
||||
}
|
||||
|
||||
public interface IWaitingQueue : IEnvironmentPointProvider
|
||||
{
|
||||
Vector3 GetRandomPointPosition();
|
||||
bool RegisterWaitingQueue(GameObject waitingCustomer);
|
||||
void UnregisterWaitingQueue(GameObject waitingCustomer);
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace DDD.Restaurant
|
||||
{
|
||||
public class EnvironmentPointMarker : MonoBehaviour, IEnvironmentPointProvider
|
||||
{
|
||||
[SerializeField] PointType _pointType;
|
||||
[SerializeField] private PointType _pointType;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
@ -18,12 +18,12 @@ private void OnDisable()
|
||||
environmentState?.UnRegisterPointProvider(this);
|
||||
}
|
||||
|
||||
public bool IsSupportsType(PointType pointType)
|
||||
public bool CanProvidePoint(PointType pointType)
|
||||
{
|
||||
return _pointType == pointType;
|
||||
}
|
||||
|
||||
public Vector3 GetPosition()
|
||||
public Vector3 GetPointPosition()
|
||||
{
|
||||
return transform.position;
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public class RestaurantWaitingQueue : MonoBehaviour, IWaitingQueue
|
||||
{
|
||||
[SerializeField]
|
||||
private PointType _pointType;
|
||||
[SerializeField]
|
||||
private int _maxWaitingCount;
|
||||
[SerializeField, ReadOnly]
|
||||
private int _currentWaitingCount;
|
||||
|
||||
[SerializeField]
|
||||
private float _waitingRangeRadius = 1f;
|
||||
|
||||
[SerializeField] private Color _targetGizmoColor = Color.yellow;
|
||||
|
||||
private HashSet<GameObject> _waitingSet = new();
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var environmentState = RestaurantState.Instance?.EnvironmentState;
|
||||
environmentState?.RegisterPointProvider(this);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
var environmentState = RestaurantState.Instance?.EnvironmentState;
|
||||
environmentState?.UnRegisterPointProvider(this);
|
||||
}
|
||||
public bool CanProvidePoint(PointType pointType)
|
||||
{
|
||||
return _pointType == pointType && _currentWaitingCount < _maxWaitingCount;
|
||||
}
|
||||
|
||||
public Vector3 GetPointPosition()
|
||||
{
|
||||
return transform.position;
|
||||
}
|
||||
|
||||
public GameObject GetGameObject()
|
||||
{
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public Vector3 GetRandomPointPosition()
|
||||
{
|
||||
Vector2 randomOffset = Random.insideUnitCircle * _waitingRangeRadius;
|
||||
return transform.position + new Vector3(randomOffset.x, 0f, randomOffset.y);
|
||||
}
|
||||
|
||||
public bool RegisterWaitingQueue(GameObject waitingCustomer)
|
||||
{
|
||||
if (_currentWaitingCount > _maxWaitingCount) return false;
|
||||
_waitingSet.Add(waitingCustomer);
|
||||
_currentWaitingCount = _waitingSet.Count;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UnregisterWaitingQueue(GameObject waitingCustomer)
|
||||
{
|
||||
_waitingSet.Remove(waitingCustomer);
|
||||
_currentWaitingCount = _waitingSet.Count;
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// 타겟 위치에 기즈모 그리기
|
||||
Gizmos.color = _targetGizmoColor;
|
||||
Gizmos.DrawWireSphere(transform.position, _waitingRangeRadius);
|
||||
UnityEditor.Handles.Label(transform.position + Vector3.up * 2f,
|
||||
$"Waiting Count: {_currentWaitingCount}");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dc8393b9e144fd1a225f29babfb089d
|
||||
timeCreated: 1756799072
|
@ -10,7 +10,7 @@ namespace DDD.Restaurant
|
||||
{
|
||||
public static class RestaurantInteractionSubsystems
|
||||
{
|
||||
public static Dictionary<InteractionType, Type> TypeToSubsystem = new()
|
||||
public static readonly Dictionary<InteractionType, Type> TypeToSubsystem = new()
|
||||
{
|
||||
{InteractionType.RestaurantManagement, typeof(InteractionSubsystem_Management)},
|
||||
{InteractionType.RestaurantOrder, typeof(InteractionSubsystem_Order)},
|
||||
@ -18,8 +18,15 @@ public static class RestaurantInteractionSubsystems
|
||||
};
|
||||
}
|
||||
|
||||
public static class RestaurantInteractionActions
|
||||
{
|
||||
public static readonly Dictionary<InteractionType, CharacterActionState> TypeToAction = new()
|
||||
{
|
||||
{InteractionType.RestaurantTrash, CharacterActionState.CleaningTable},
|
||||
};
|
||||
}
|
||||
|
||||
public class RestaurantInteractionComponent : MonoBehaviour, IInteractable, IInteractionSubsystemOwner
|
||||
public class RestaurantInteractionComponent : MonoBehaviour, IInteractable, IInteractionSubsystemOwner, ICharacterActionStateProvider
|
||||
{
|
||||
// Single interaction type
|
||||
[ValueDropdown("GetAllInteractionTypes")]
|
||||
@ -239,5 +246,24 @@ public bool TryGetSubsystemEnumType<T>(out T enumValue) where T : Enum
|
||||
enumValue = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public CharacterActionState GetCurrentActionState()
|
||||
{
|
||||
if (HasSubsystem(_interactionType))
|
||||
{
|
||||
var subsystemObject = GetSubsystem(_interactionType);
|
||||
if(subsystemObject is ICharacterActionStateProvider actionStateProvider)
|
||||
{
|
||||
return actionStateProvider.GetCurrentActionState();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return RestaurantInteractionActions.TypeToAction.GetValueOrDefault(_interactionType,
|
||||
CharacterActionState.None);
|
||||
}
|
||||
|
||||
return CharacterActionState.None;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4cc0ab3a0d5b47e2b46992a16ecbbc5a
|
||||
timeCreated: 1756798057
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public class RestaurantTrashSolver : MonoBehaviour, IInteractionSolver
|
||||
public class RestaurantTrashSolver : RestaurantBaseSolver
|
||||
{
|
||||
public bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payload = null)
|
||||
public override bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payload = null)
|
||||
{
|
||||
var carrier = interactor?.GetInteractorGameObject()?.GetComponent<ICarrier>();
|
||||
if (carrier == null)
|
||||
@ -14,7 +14,7 @@ public bool ExecuteInteraction(IInteractor interactor, IInteractable interactabl
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null,
|
||||
public override bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null,
|
||||
ScriptableObject payload = null)
|
||||
{
|
||||
// Check carrying state
|
||||
@ -25,7 +25,7 @@ public bool CanExecuteInteraction(IInteractor interactor = null, IInteractable i
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanSolveInteraction(IInteractor interactor, IInteractable interactable)
|
||||
public override bool CanSolveInteraction(IInteractor interactor, IInteractable interactable)
|
||||
{
|
||||
return true;
|
||||
}
|
@ -2,9 +2,20 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public abstract class RestaurantSubsystemSolver<T> : MonoBehaviour, IInteractionSolver where T : Enum
|
||||
public abstract class RestaurantBaseSolver : MonoBehaviour, IInteractionSolver
|
||||
{
|
||||
public abstract bool ExecuteInteraction(IInteractor interactor, IInteractable interactable,
|
||||
ScriptableObject payload = null);
|
||||
|
||||
public abstract bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null,
|
||||
ScriptableObject payload = null);
|
||||
|
||||
public abstract bool CanSolveInteraction(IInteractor interactor, IInteractable interactable);
|
||||
}
|
||||
|
||||
public abstract class RestaurantSubsystemSolver<T> : RestaurantBaseSolver where T : Enum
|
||||
{
|
||||
private Dictionary<T, IInteractionSubsystemSolver<T>> _solvers = new();
|
||||
|
||||
@ -18,19 +29,19 @@ private void Awake()
|
||||
_solvers.Add(subsystemSolverType.Key, solver);
|
||||
}
|
||||
}
|
||||
public bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payload = null)
|
||||
public override bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payload = null)
|
||||
{
|
||||
return TryGetSolver(interactable, out var solver) &&
|
||||
solver.ExecuteInteractionSubsystem(interactor, interactable, payload);
|
||||
}
|
||||
|
||||
public bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null, ScriptableObject payload = null)
|
||||
public override bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null, ScriptableObject payload = null)
|
||||
{
|
||||
return TryGetSolver(interactable, out var solver) &&
|
||||
solver.CanExecuteInteractionSubsystem(interactor, interactable, payload);
|
||||
}
|
||||
|
||||
public bool CanSolveInteraction(IInteractor interactor, IInteractable interactable)
|
||||
public override bool CanSolveInteraction(IInteractor interactor, IInteractable interactable)
|
||||
{
|
||||
if (interactable is IInteractionSubsystemOwner subsystemOwner)
|
||||
{
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 889bc878e70343cab7416f799f767451
|
||||
timeCreated: 1756798128
|
@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
public abstract class InteractionSubsystemBase : MonoBehaviour, ICharacterActionStateProvider
|
||||
{
|
||||
public abstract CharacterActionState GetCurrentActionState();
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9f52d1cd4bd4917a884b6e3bc1152e7
|
||||
timeCreated: 1756798490
|
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
[Flags]
|
||||
public enum RestaurantCookType : uint
|
||||
@ -9,7 +9,7 @@ public enum RestaurantCookType : uint
|
||||
StartCooking = 0,
|
||||
}
|
||||
|
||||
public class InteractionSubsystem_Cook : MonoBehaviour, IInteractionSubsystemObject<RestaurantCookType>
|
||||
public class InteractionSubsystem_Cook : InteractionSubsystemBase, IInteractionSubsystemObject<RestaurantCookType>
|
||||
{
|
||||
[SerializeField] protected RestaurantCookType _cookType = RestaurantCookType.StartCooking;
|
||||
|
||||
@ -42,5 +42,10 @@ public string GetCurrentSubsystemTypeName()
|
||||
{
|
||||
return _cookType.ToString();
|
||||
}
|
||||
|
||||
public override CharacterActionState GetCurrentActionState()
|
||||
{
|
||||
return CharacterActionState.None;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
namespace DDD.Restaurant
|
||||
{
|
||||
[Flags]
|
||||
public enum RestaurantManagementType : uint
|
||||
@ -10,7 +10,7 @@ public enum RestaurantManagementType : uint
|
||||
RunRestaurant = 1,
|
||||
}
|
||||
|
||||
public class InteractionSubsystem_Management : MonoBehaviour, IInteractionSubsystemObject<RestaurantManagementType>
|
||||
public class InteractionSubsystem_Management : InteractionSubsystemBase, IInteractionSubsystemObject<RestaurantManagementType>
|
||||
{
|
||||
[SerializeField] protected RestaurantManagementType _managementType = RestaurantManagementType.OpenManagementUi;
|
||||
|
||||
@ -48,5 +48,10 @@ public ScriptableObject GetPayload()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override CharacterActionState GetCurrentActionState()
|
||||
{
|
||||
return CharacterActionState.None;
|
||||
}
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@ public class RestaurantOrderObjectState
|
||||
|
||||
public class RestaurantOrderInterrupt : IEvent
|
||||
{
|
||||
public GameObject Table;
|
||||
public GameObject InteractableObject;
|
||||
public RestaurantOrderObjectState OrderObject;
|
||||
public RestaurantOrderType TransitionType;
|
||||
}
|
||||
@ -47,7 +47,7 @@ public interface IRestaurantOrderObject
|
||||
|
||||
// TODO : 도중에 이탈할 경우, 순차적으로 다음 페이즈로 넘어가는 게 아니라 바로 Wait, Dirty로 전이시킬 필요가 있음
|
||||
|
||||
public class InteractionSubsystem_Order : MonoBehaviour, IInteractionSubsystemObject<RestaurantOrderType>, IRestaurantOrderObject, IEventHandler<RestaurantOrderInterrupt>
|
||||
public class InteractionSubsystem_Order : InteractionSubsystemBase, IInteractionSubsystemObject<RestaurantOrderType>, IRestaurantOrderObject, IEventHandler<RestaurantOrderInterrupt>
|
||||
{
|
||||
[SerializeField] private RestaurantOrderType _currentRestaurantOrderType = RestaurantOrderType.Wait;
|
||||
[SerializeField] private RestaurantOrderObjectState _orderObjectState = new();
|
||||
@ -106,6 +106,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;
|
||||
}
|
||||
|
||||
@ -131,7 +138,7 @@ public bool CanTransitionToNextPhase()
|
||||
|
||||
public void HandleEvent(RestaurantOrderInterrupt evt)
|
||||
{
|
||||
if (evt.Table != gameObject) return;
|
||||
if (evt.InteractableObject != gameObject) return;
|
||||
Debug.Log($"Order Interrupt : {evt.TransitionType}, Current State : {GetInteractionSubsystemType()}");
|
||||
SetInteractionSubsystemType(evt.TransitionType);
|
||||
}
|
||||
@ -140,5 +147,15 @@ private void OnDestroy()
|
||||
{
|
||||
EventBus.Unregister(this);
|
||||
}
|
||||
|
||||
public override CharacterActionState GetCurrentActionState()
|
||||
{
|
||||
if (_currentRestaurantOrderType == RestaurantOrderType.Dirty)
|
||||
{
|
||||
return CharacterActionState.CleaningTable;
|
||||
}
|
||||
|
||||
return CharacterActionState.None;
|
||||
}
|
||||
}
|
||||
}
|
@ -83,7 +83,7 @@ public List<IEnvironmentPointProvider> GetPointProviderByType(PointType pointTyp
|
||||
_registeredPointProviders.RemoveAll(item => item == null || (item as UnityEngine.Object) == null);
|
||||
foreach (var provider in _registeredPointProviders)
|
||||
{
|
||||
if (!provider.IsSupportsType(pointType)) continue;
|
||||
if (!provider.CanProvidePoint(pointType)) continue;
|
||||
result.Add(provider);
|
||||
}
|
||||
return result;
|
||||
|
@ -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