Customer AI 주문 서브트리 및 데코레이터 추가

레스토랑 씬용 임시 AStarPath 싱글톤 추가
This commit is contained in:
김산 2025-08-25 20:14:39 +09:00
parent 22047aeb8b
commit 24239dc138
11 changed files with 390 additions and 8 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bab8aadc83d64f64b8280e6ebb957e65
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -8,6 +8,10 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 2686192822530022837, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: m_IsTrigger
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3854744934792897056, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: m_Data.m_UniqueID
value: 2072347169
@ -1104,6 +1108,30 @@ PrefabInstance:
propertyPath: _availableInteractions
value: 2
objectReference: {fileID: 0}
- target: {fileID: 5654854357519457123, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: gravity.x
value: NaN
objectReference: {fileID: 0}
- target: {fileID: 5654854357519457123, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: gravity.y
value: NaN
objectReference: {fileID: 0}
- target: {fileID: 5654854357519457123, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: gravity.z
value: NaN
objectReference: {fileID: 0}
- target: {fileID: 5654854357519457123, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: orientation
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5654854357519457123, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: enableRotation
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5654854357519457123, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: alwaysDrawGizmos
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6336425934484470474, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: m_Materials.Array.size
value: 4
@ -1144,6 +1172,18 @@ PrefabInstance:
propertyPath: m_Name
value: CustomerNpc
objectReference: {fileID: 0}
- target: {fileID: 7545136660434259176, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: m_Constraints
value: 112
objectReference: {fileID: 0}
- target: {fileID: 7545136660434259176, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: m_IsKinematic
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8165702938223525558, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
propertyPath: graphMask.value
value: 2
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 133104368464330048, guid: ceeea618d8ee23642a0e56b3f963448c, type: 3}
m_RemovedGameObjects: []

View File

@ -11785,6 +11785,92 @@ MeshFilter:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1771012164}
m_Mesh: {fileID: 0}
--- !u!1 &1775054119
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1775054121}
- component: {fileID: 1775054120}
m_Layer: 0
m_Name: AstarPath
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1775054120
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1775054119}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 78396926cbbfc4ac3b48fc5fc34a87d1, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 1073741824
data:
dataString: UEsDBBQAAAgIAABIIewaXtoYSgIAALMEAAALACQAZ3JhcGgxLmpzb24KACAAAAAAAAEAGAAAgD7V3rGdAQCAPtXesZ0BAIA+1d6xnQF1U8tu2zAQ/BVD56awZRuOe4ydpoc8CstAgxQ9rMm1RJgiXZKy4xj59+5SjyhNeyI0s7PP0TkRBTgQAd0KpKp88mUw/Dz9NEiENcFW7g6er52zjvCUUdQ6Uy8Yw1KOO4LewUbjN1R5EZqwFlxoVW66lCU8Z9ruWTwe1t/XMsdbNHkoWBlBZVaYK2uaMmPCUKpg3VppbMBReklwaIDHD8hTh1QeWcdzBVchIXt0t3BCd2el2ioBgWox/fMXkVKVaDwhxHKhZNkC42VC/AbELne2MnLt4IDOw0ZpFU4c+qMZmuMcajyACTcO9kVWuS0I7FLaextW+LtSLsYKqzUKbiPDEJTJuZtzD26FsW3Pkn1x8kr4TKBh5vz6F5YuW1THUcHvGBgkB9AVKy5GA2YD5A0Xp3fgyQi0vwXVVpKLdWvruDv0RdznFrR/x6zROVDmH5q1w/4JQh1YW6aE/dIejYdyr2n4r2TGaLdxsxruY9UmWmIApfm6xFqzqHfUtWQqrXkuZ0O8az30MzuQ0FPzvvAb599aJ1Be8T39Ag3V+L9iNPkgadzYCMatYtRK0rqKL+yRW3yoAk2Ib3tg4p6Ou7DG1Kfu77WVNfZ5kxkEhz5k9IjiwejT41NPhoZNeA+HkrSLKjrqTZtXSr63QjKepbNhms7mczEbXc7lBU7kdLZJp7M5yOlEDJM4hTIqKNDfKb8O7WLob+7d20AZM65Q0MEG0fxsV+ngeKNeStvzgDJbmwnyhXmoc3Rj06FB02bldfztez9Fcn5NXv8AUEsDBBQAAAgIAABIIexc6sO8cwAAAIUAAAAJACQAbWV0YS5qc29uCgAgAAAAAAABABgAAIA+1d6xnQEAgD7V3rGdAQCAPtXesZ0Bq1YqSy0qzszPU7JSMNUz1rPQUVBKL0osyCgGChiBOKWZKSB2dF5pTg6Qb2xuZG5gZGRuaZlsbmhhmaKbapJiap5kZGpumZhiapJsoBQLVFVSWZDql5ibiqwzILEkIy0zLyUzL10vKDU5sbjEHWSRUmwtAFBLAQItABQAAAgIAABIIewaXtoYSgIAALMEAAALACQAAAAAAAAAAAAAAAAAAABncmFwaDEuanNvbgoAIAAAAAAAAQAYAACAPtXesZ0BAIA+1d6xnQEAgD7V3rGdAVBLAQItABQAAAgIAABIIexc6sO8cwAAAIUAAAAJACQAAAAAAAAAAAAAAJcCAABtZXRhLmpzb24KACAAAAAAAAEAGAAAgD7V3rGdAQCAPtXesZ0BAIA+1d6xnQFQSwUGAAAAAAIAAgC4AAAAVQMAAAAA
file_cachedStartup: {fileID: 0}
cacheStartup: 0
showNavGraphs: 1
showUnwalkableNodes: 1
debugMode: 4
debugFloor: 0
debugRoof: 1
manualDebugFloorRoof: 0
showSearchTree: 0
unwalkableNodeDebugSize: 0.3
logPathResults: 0
maxNearestNodeDistance: 100
scanOnStartup: 1
fullGetNearestSearch: 0
prioritizeGraphs: 0
prioritizeGraphsLimit: 1
colorSettings:
_SolidColor: {r: 0.11764706, g: 0.4, b: 0.7882353, a: 0.9}
_UnwalkableNode: {r: 1, g: 0, b: 0, a: 0.5}
_BoundsHandles: {r: 0.29, g: 0.454, b: 0.741, a: 0.9}
_ConnectionLowLerp: {r: 0, g: 1, b: 0, a: 0.5}
_ConnectionHighLerp: {r: 1, g: 0, b: 0, a: 0.5}
_MeshEdgeColor: {r: 0, g: 0, b: 0, a: 0.5}
_AreaColors: []
tagNames: []
heuristic: 2
heuristicScale: 1
threadCount: -1
maxFrameTime: 10
batchGraphUpdates: 0
graphUpdateBatchingInterval: 0.2
navmeshUpdates:
updateInterval: 0
euclideanEmbedding:
mode: 0
seed: 0
pivotPointRoot: {fileID: 0}
spreadOutCount: 10
showGraphs: 1
--- !u!4 &1775054121
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1775054119}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!4 &1784230204 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 1061695247072719575, guid: 70f56d7d65d2e7842b5bd517ae7fe7fe, type: 3}
@ -16891,3 +16977,4 @@ SceneRoots:
- {fileID: 612992553}
- {fileID: 761682093}
- {fileID: 549344125}
- {fileID: 1775054121}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f96045235fcc43c880f2e0ee857b6f2e
timeCreated: 1756111444

View File

@ -0,0 +1,235 @@
using Opsive.BehaviorDesigner.Runtime;
using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.BehaviorDesigner.Runtime.Tasks.Decorators;
using Opsive.GraphDesigner.Runtime;
using Opsive.Shared.Utility;
using Unity.Burst;
using Unity.Entities;
using UnityEngine;
namespace DDD
{
[NodeDescription("자식 태스크의 실행 시간을 제한합니다")]
public class TimeLimiter : ILogicNode, IParentNode, ITaskComponentData, IDecorator, ISavableTask
{
[Tooltip("The index of the node.")]
[SerializeField] ushort _Index;
[Tooltip("The parent index of the node. ushort.MaxValue indicates no parent.")]
[SerializeField] ushort _ParentIndex;
[Tooltip("The sibling index of the node. ushort.MaxValue indicates no sibling.")]
[SerializeField] ushort _SiblingIndex;
[Tooltip("최대 실행 시간(초)")]
[SerializeField] float _timeLimit = 30.0f;
[Tooltip("시간 초과 시 반환할 상태")]
[SerializeField] private TaskStatus _timeoutStatus = TaskStatus.Failure;
[Header("Debug")]
[Tooltip("로그 출력")]
[SerializeField] private bool _enableDebug = false;
private ushort _ComponentIndex;
public ushort Index
{
get => _Index;
set => _Index = value;
}
public ushort ParentIndex
{
get => _ParentIndex;
set => _ParentIndex = value;
}
public ushort SiblingIndex
{
get => _SiblingIndex;
set => _SiblingIndex = value;
}
public ushort RuntimeIndex { get; set; }
public float TimeLimit
{
get => _timeLimit;
set => _timeLimit = value;
}
public int MaxChildCount
{
get { return 1; }
}
public ComponentType Tag
{
get => typeof(TimeLimiterTag);
}
public System.Type SystemType
{
get => typeof(TimeLimiterTaskSystem);
}
public void AddBufferElement(World world, Entity entity)
{
DynamicBuffer<TimeLimiterComponent> buffer;
if (world.EntityManager.HasBuffer<TimeLimiterComponent>(entity))
{
buffer = world.EntityManager.GetBuffer<TimeLimiterComponent>(entity);
}
else
{
buffer = world.EntityManager.AddBuffer<TimeLimiterComponent>(entity);
}
buffer.Add(new TimeLimiterComponent()
{
Index = RuntimeIndex,
TimeLimit = _timeLimit,
TimeoutStatus = _timeoutStatus,
});
_ComponentIndex = (ushort)(buffer.Length - 1);
}
public void ClearBufferElement(World world, Entity entity)
{
if (world.EntityManager.HasBuffer<TimeLimiterComponent>(entity))
{
var buffer = world.EntityManager.GetBuffer<TimeLimiterComponent>(entity);
buffer.Clear();
}
}
public MemberVisibility GetSaveReflectionType(int index)
{
return MemberVisibility.None;
}
public object Save(World world, Entity entity)
{
var timeLimiterComponents = world.EntityManager.GetBuffer<TimeLimiterComponent>(entity);
var timeLimiterComponent = timeLimiterComponents[_ComponentIndex];
return timeLimiterComponent.StartTime;
}
public void Load(object saveData, World world, Entity entity)
{
var timeLimiterComponents = world.EntityManager.GetBuffer<TimeLimiterComponent>(entity);
var timeLimiterComponent = timeLimiterComponents[_ComponentIndex];
timeLimiterComponent.StartTime = (float)saveData;
timeLimiterComponents[_ComponentIndex] = timeLimiterComponent;
}
}
public struct TimeLimiterComponent : IBufferElementData
{
[Tooltip("The index of the node.")]
public ushort Index;
[Tooltip("최대 실행 시간(초)")]
public float TimeLimit;
[Tooltip("실행 시작 시간(초)")]
public float StartTime;
[Tooltip("Should the task end when the child returns failure?")]
public TaskStatus TimeoutStatus;
}
public struct TimeLimiterTag : IComponentData, IEnableableComponent { }
[DisableAutoCreation]
public partial struct TimeLimiterTaskSystem : ISystem
{
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
var query = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAllRW<TaskComponent>().WithAllRW<TimeLimiterComponent>().WithAll<TimeLimiterTag, EvaluationComponent>().Build();
state.Dependency = new TimeLimiterJob()
{
CurrentTime = (float)SystemAPI.Time.ElapsedTime
}.ScheduleParallel(query, state.Dependency);
}
[BurstCompile]
private partial struct TimeLimiterJob : IJobEntity
{
public float CurrentTime;
[BurstCompile]
public void Execute(ref DynamicBuffer<BranchComponent> branchComponents,
ref DynamicBuffer<TaskComponent> taskComponents,
ref DynamicBuffer<TimeLimiterComponent> timeLimiterComponents)
{
for (int i = 0; i < timeLimiterComponents.Length; ++i)
{
var timeLimiterComponent = timeLimiterComponents[i];
var taskComponent = taskComponents[timeLimiterComponent.Index];
var branchComponent = branchComponents[taskComponent.BranchIndex];
TaskComponent childTaskComponent;
if (taskComponent.Status == TaskStatus.Queued)
{
taskComponent.Status = TaskStatus.Running;
taskComponents[taskComponent.Index] = taskComponent;
timeLimiterComponent.StartTime = CurrentTime;
timeLimiterComponents[i] = timeLimiterComponent;
childTaskComponent = taskComponents[taskComponent.Index + 1];
childTaskComponent.Status = TaskStatus.Queued;
taskComponents[taskComponent.Index + 1] = childTaskComponent;
branchComponent.NextIndex = taskComponent.Index + 1;
branchComponents[taskComponent.BranchIndex] = branchComponent;
continue;
}
else if (taskComponent.Status != TaskStatus.Running)
{
continue;
}
if (timeLimiterComponent.StartTime >= 0f &&
CurrentTime - timeLimiterComponent.StartTime >= timeLimiterComponent.TimeLimit) {
// 시간 초과
taskComponent.Status = timeLimiterComponent.TimeoutStatus;
taskComponents[taskComponent.Index] = taskComponent;
// 자식 태스크가 실행 중이면 중단
childTaskComponent = taskComponents[taskComponent.Index + 1];
if (childTaskComponent.Status == TaskStatus.Running ||
childTaskComponent.Status == TaskStatus.Queued) {
childTaskComponent.Status = timeLimiterComponent.TimeoutStatus;
taskComponents[taskComponent.Index + 1] = childTaskComponent;
}
branchComponent.NextIndex = taskComponent.ParentIndex;
branchComponents[taskComponent.BranchIndex] = branchComponent;
continue;
}
childTaskComponent = taskComponents[taskComponent.Index + 1];
if (childTaskComponent.Status == TaskStatus.Queued ||
childTaskComponent.Status == TaskStatus.Running) {
// The child should keep running.
continue;
}
taskComponent.Status = childTaskComponent.Status;
taskComponents[taskComponent.Index] = taskComponent;
branchComponent.NextIndex = taskComponent.ParentIndex;
branchComponents[taskComponent.BranchIndex] = branchComponent;
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d5da50af45c8438eb9677fd18378f9b4
timeCreated: 1756111556

View File

@ -101,6 +101,7 @@ public bool HasReachedDestination()
public bool IsPositionMovable(Vector3 endPosition)
{
var nearestNode = AstarPath.active.GetNearest(endPosition).node;
return nearestNode != null && nearestNode.Walkable;
}

View File

@ -25,12 +25,12 @@ private void Start()
public bool CanInteract()
{
if (GetInteractionSubsystemType() == RestaurantOrderType.Wait)
{
//if (GetInteractionSubsystemType() == RestaurantOrderType.Wait)
//{
// return true;
//}
return true;
}
return false;
}
public bool OnInteracted(IInteractor interactor, ScriptableObject payloadSo = null)
{

View File

@ -1,3 +1,4 @@
using Opsive.BehaviorDesigner.Runtime.Tasks;
using UnityEngine;
namespace DDD.RestaurantOrders
@ -6,7 +7,8 @@ public class RestaurantOrderSolver_Wait : MonoBehaviour, IInteractionSubsystemSo
{
public bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
{
// TODO : DO SOMETHING!!!
if (CanExecuteInteractionSubsystem(interactor, interactable, payloadSo) == false) return false;
return true;
}