커스터머 스폰 관련 데이터와 스테이트 분리
This commit is contained in:
parent
2102690ba0
commit
1fd5279cd2
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f5775216947354447bf06ea842721c73
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
BIN
Assets/_DDD/_Addressables/So/RestaurantData/DataObjects/RestaurantCustomerData.asset
(Stored with Git LFS)
Normal file
BIN
Assets/_DDD/_Addressables/So/RestaurantData/DataObjects/RestaurantCustomerData.asset
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e5e868658773b4297852eba8396d6f73
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
BIN
Assets/_DDD/_Addressables/So/RestaurantData/DataObjects/RestaurantPlayerData.asset
(Stored with Git LFS)
Normal file
BIN
Assets/_DDD/_Addressables/So/RestaurantData/DataObjects/RestaurantPlayerData.asset
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Assets/_DDD/_Addressables/So/RestaurantData/DataObjects/RestaurantRunData.asset
(Stored with Git LFS)
Normal file
BIN
Assets/_DDD/_Addressables/So/RestaurantData/DataObjects/RestaurantRunData.asset
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a335d9a9b913a4e8292845e8a6362dd9
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
BIN
Assets/_DDD/_Addressables/So/RestaurantData/RestaurantData.asset
(Stored with Git LFS)
BIN
Assets/_DDD/_Addressables/So/RestaurantData/RestaurantData.asset
(Stored with Git LFS)
Binary file not shown.
BIN
Assets/_DDD/_Addressables/So/RestaurantData/RestaurantPlayerData.asset
(Stored with Git LFS)
BIN
Assets/_DDD/_Addressables/So/RestaurantData/RestaurantPlayerData.asset
(Stored with Git LFS)
Binary file not shown.
@ -5,14 +5,12 @@ namespace DDD
|
|||||||
public class RestaurantNpcCharacter : RestaurantCharacter
|
public class RestaurantNpcCharacter : RestaurantCharacter
|
||||||
{
|
{
|
||||||
protected BehaviorTree _behaviorTree;
|
protected BehaviorTree _behaviorTree;
|
||||||
protected SpineController _spineController;
|
|
||||||
|
|
||||||
protected override void Awake()
|
protected override void Awake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
base.Awake();
|
||||||
|
|
||||||
_behaviorTree = GetComponent<BehaviorTree>();
|
_behaviorTree = GetComponent<BehaviorTree>();
|
||||||
_spineController = GetComponent<SpineController>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,9 +8,12 @@ public class RestaurantCharacter : MonoBehaviour, IGameCharacter, IInteractor
|
|||||||
[EnumToggleButtons, SerializeField] protected InteractionType _interactionType;
|
[EnumToggleButtons, SerializeField] protected InteractionType _interactionType;
|
||||||
|
|
||||||
RestaurantCharacterInteraction _interactionComponent;
|
RestaurantCharacterInteraction _interactionComponent;
|
||||||
|
protected SpineController _spineController;
|
||||||
|
|
||||||
protected virtual void Awake()
|
protected virtual void Awake()
|
||||||
{
|
{
|
||||||
_interactionComponent = GetComponent<RestaurantCharacterInteraction>();
|
_interactionComponent = GetComponent<RestaurantCharacterInteraction>();
|
||||||
|
_spineController = GetComponent<SpineController>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Start()
|
protected virtual void Start()
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -5,30 +9,133 @@ namespace DDD
|
|||||||
{
|
{
|
||||||
public class RestaurantRunController : FlowController
|
public class RestaurantRunController : FlowController
|
||||||
{
|
{
|
||||||
RestaurantCustomerState _restaurantCustomerStateSo;
|
private RestaurantCustomerState _restaurantCustomerStateSo;
|
||||||
|
private RestaurantRunState _restaurantRunStateSo;
|
||||||
|
private CancellationTokenSource _cts;
|
||||||
|
|
||||||
|
// Runtime dependencies used by the execution logic
|
||||||
|
private LevelDataSo _levelDataSo;
|
||||||
|
private CustomerDataSo _customerDataSo;
|
||||||
|
private CustomerPoolDataSo _customerPoolDataSo;
|
||||||
|
private ICustomerFactory _iCustomerFactory;
|
||||||
|
private ISpawnPointProvider _spawnPointProvider;
|
||||||
|
|
||||||
|
// Debug-only: last built schedule (kept in controller to keep State pure)
|
||||||
|
private SpawnSchedule _spawnSchedule;
|
||||||
|
|
||||||
public override Task InitializeController()
|
public override Task InitializeController()
|
||||||
{
|
{
|
||||||
_restaurantCustomerStateSo = RestaurantState.Instance.CustomerState;
|
_restaurantCustomerStateSo = RestaurantState.Instance.CustomerState;
|
||||||
|
_restaurantRunStateSo = RestaurantState.Instance.RunState;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task InitializeState()
|
public override Task InitializeState()
|
||||||
{
|
{
|
||||||
|
_spawnPointProvider ??= new SpawnPointProvider();
|
||||||
|
_restaurantRunStateSo.InitializeSpawnPoint(_spawnPointProvider.GetSpawnPoint());
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task OnReadyNewFlow(GameFlowState newFlowState)
|
public override async Task OnReadyNewFlow(GameFlowState newFlowState)
|
||||||
{
|
{
|
||||||
var restaurantCustomerStateHandle = _restaurantCustomerStateSo.OnReadyNewFlow(newFlowState);
|
if (newFlowState == GameFlowState.RunRestaurant)
|
||||||
await Task.WhenAll(restaurantCustomerStateHandle);
|
{
|
||||||
|
_cts?.Cancel();
|
||||||
|
_cts?.Dispose();
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
await StartSpawnLoopAsync(_cts.Token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public override async Task OnExitCurrentFlow(GameFlowState exitingFlowState)
|
|
||||||
|
public override Task OnExitCurrentFlow(GameFlowState exitingFlowState)
|
||||||
{
|
{
|
||||||
if (exitingFlowState == GameFlowState.RunRestaurant)
|
if (exitingFlowState == GameFlowState.RunRestaurant)
|
||||||
{
|
{
|
||||||
var restaurantCustomerStateHandle = _restaurantCustomerStateSo.OnExitCurrentFlow(exitingFlowState);
|
_cts?.Cancel();
|
||||||
await Task.WhenAll(restaurantCustomerStateHandle);
|
_cts?.Dispose();
|
||||||
|
_cts = null;
|
||||||
}
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartSpawnLoopAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
_iCustomerFactory ??= new CustomerFactory();
|
||||||
|
|
||||||
|
var currentGameLevel = GameState.Instance.LevelState.Level;
|
||||||
|
_levelDataSo ??= DataManager.Instance.GetDataSo<LevelDataSo>();
|
||||||
|
_customerDataSo ??= DataManager.Instance.GetDataSo<CustomerDataSo>();
|
||||||
|
_customerPoolDataSo ??= DataManager.Instance.GetDataSo<CustomerPoolDataSo>();
|
||||||
|
|
||||||
|
var currentLevelData = _levelDataSo.GetDataList().FirstOrDefault(data => data.Level == currentGameLevel);
|
||||||
|
Debug.Assert(currentLevelData != null, "currentLevelData is null");
|
||||||
|
if (currentLevelData == null) return; // 안전 가드
|
||||||
|
|
||||||
|
var normalPool = _customerPoolDataSo.GetDataById(currentLevelData.CustomerPool);
|
||||||
|
var specialPool = _customerPoolDataSo.GetDataById(currentLevelData.SpecialCustomerPool);
|
||||||
|
|
||||||
|
await RunSpawnLoopAsync(currentLevelData, normalPool, specialPool, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunSpawnLoopAsync(LevelData levelData, CustomerPoolData normalPool, CustomerPoolData specialPool, CancellationToken token)
|
||||||
|
{
|
||||||
|
var runData = RestaurantData.Instance? RestaurantData.Instance.RunData : null;
|
||||||
|
float firstDelay = Mathf.Max(0f, runData? runData.FirstSpawnDelaySeconds : 5f);
|
||||||
|
if (firstDelay > 0)
|
||||||
|
{
|
||||||
|
await Awaitable.WaitForSecondsAsync(firstDelay, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
var scheduleBuilder = CreateBuilder(levelData.SpawnType);
|
||||||
|
int randomSeed = Environment.TickCount;
|
||||||
|
|
||||||
|
SpawnSchedule MakeSchedule() => scheduleBuilder.Build(new SpawnScheduleBuildArgs
|
||||||
|
{
|
||||||
|
NormalIds = (IReadOnlyList<string>) (normalPool?.ValidCustomers) ?? Array.Empty<string>(),
|
||||||
|
SpecialIds = (IReadOnlyList<string>) (specialPool?.ValidCustomers) ?? Array.Empty<string>(),
|
||||||
|
NormalQuota = Math.Max(0, normalPool?.CustomerLimitCount ?? 0),
|
||||||
|
SpecialQuota = Math.Max(0, specialPool?.CustomerLimitCount ?? 0),
|
||||||
|
Seed = ++randomSeed
|
||||||
|
});
|
||||||
|
|
||||||
|
_spawnSchedule = MakeSchedule();
|
||||||
|
float wait = Mathf.Max(0.1f, levelData.CustomerRespawnTime);
|
||||||
|
|
||||||
|
while (token.IsCancellationRequested == false)
|
||||||
|
{
|
||||||
|
if (Application.isPlaying == false)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_spawnSchedule.TryDequeue(out var customerId) == false) break;
|
||||||
|
|
||||||
|
if (_customerDataSo.TryGetDataById(customerId, out var customerData))
|
||||||
|
{
|
||||||
|
var rotation = Quaternion.identity;
|
||||||
|
|
||||||
|
_ = _iCustomerFactory.CreateAsync(new CustomerSpawnArgs
|
||||||
|
{
|
||||||
|
CustomerData = customerData,
|
||||||
|
Position = _restaurantRunStateSo.SpawnPoint,
|
||||||
|
Rotation = rotation,
|
||||||
|
Parent = null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(wait), token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ISpawnScheduleBuilder CreateBuilder(SpawnType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
SpawnType.Random => new RandomSpawnScheduleBuilder(),
|
||||||
|
SpawnType.Regular => new RegularSpawnScheduleBuilder(),
|
||||||
|
_ => new RandomSpawnScheduleBuilder()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,7 +29,21 @@ public async Task<GameObject> CreateAsync(CustomerSpawnArgs args)
|
|||||||
{
|
{
|
||||||
if (!_customerPrefab)
|
if (!_customerPrefab)
|
||||||
{
|
{
|
||||||
_customerPrefab = await AssetManager.LoadAsset<GameObject>(DataConstants.CustomerNpcPrefab);
|
var customerDataAsset = RestaurantData.Instance ? RestaurantData.Instance.CustomerData : null;
|
||||||
|
if (customerDataAsset == null || customerDataAsset.CustomerPrefab == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[CustomerFactory] RestaurantCustomerData or its CustomerPrefab reference is not set or not loaded.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handle = customerDataAsset.CustomerPrefab.LoadAssetAsync<GameObject>();
|
||||||
|
await handle.Task;
|
||||||
|
if (handle.Result == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[CustomerFactory] Failed to load customer prefab from AssetReference.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
_customerPrefab = handle.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newCustomer = Object.Instantiate(_customerPrefab, args.Position, args.Rotation, args.Parent);
|
var newCustomer = Object.Instantiate(_customerPrefab, args.Position, args.Rotation, args.Parent);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
{
|
{
|
||||||
[CreateAssetMenu(fileName = "RestaurantControllerData", menuName = "ScriptableObjects/RestaurantControllerData", order = 0)]
|
[CreateAssetMenu(fileName = "RestaurantControllerData", menuName = "RestaurantData/RestaurantControllerData", order = 0)]
|
||||||
public class RestaurantControllerDataSo : ScriptableObject
|
public class RestaurantControllerDataSo : ScriptableObject
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.AddressableAssets;
|
||||||
|
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
[CreateAssetMenu(fileName = "RestaurantCustomerData", menuName = "RestaurantData/RestaurantCustomerData", order = 0)]
|
||||||
|
public class RestaurantCustomerData : ScriptableObject
|
||||||
|
{
|
||||||
|
[SerializeField] private AssetReferenceGameObject _customerPrefab;
|
||||||
|
|
||||||
|
public AssetReferenceGameObject CustomerPrefab => _customerPrefab;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e9cf283e323467ea2e67b94905fbfcd
|
||||||
|
timeCreated: 1755587223
|
@ -0,0 +1,15 @@
|
|||||||
|
using Sirenix.OdinInspector;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
[CreateAssetMenu(fileName = "RestaurantRunData", menuName = "RestaurantData/RestaurantRunData")]
|
||||||
|
public class RestaurantRunData : ScriptableObject
|
||||||
|
{
|
||||||
|
[Title("스폰 제어 (Persistent Data)")]
|
||||||
|
[Tooltip("플로우 시작 후 첫 손님이 등장하기까지 대기 시간(초)")]
|
||||||
|
[SerializeField] private float _firstSpawnDelaySeconds = 5f;
|
||||||
|
|
||||||
|
public float FirstSpawnDelaySeconds => _firstSpawnDelaySeconds;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d0a7d53d11af4847860694610b3138f2
|
||||||
|
timeCreated: 1755583002
|
@ -9,9 +9,13 @@ public class RestaurantData : ScriptSingleton<RestaurantData>
|
|||||||
{
|
{
|
||||||
[SerializeField] private AssetReference _restaurantPlayerData;
|
[SerializeField] private AssetReference _restaurantPlayerData;
|
||||||
[SerializeField] private AssetReference _restaurantManagementData;
|
[SerializeField] private AssetReference _restaurantManagementData;
|
||||||
|
[SerializeField] private AssetReference _restaurantRunData;
|
||||||
|
[SerializeField] private AssetReference _restaurantCustomerData;
|
||||||
|
|
||||||
public RestaurantPlayerData PlayerData { get; private set; }
|
public RestaurantPlayerData PlayerData { get; private set; }
|
||||||
public RestaurantManagementData ManagementData { get; private set; }
|
public RestaurantManagementData ManagementData { get; private set; }
|
||||||
|
public RestaurantRunData RunData { get; private set; }
|
||||||
|
public RestaurantCustomerData CustomerData { get; private set; }
|
||||||
|
|
||||||
private bool _isLoaded;
|
private bool _isLoaded;
|
||||||
|
|
||||||
@ -24,15 +28,25 @@ public async Task LoadData()
|
|||||||
|
|
||||||
var restaurantPlayerDataHandle = _restaurantPlayerData.LoadAssetAsync<RestaurantPlayerData>();
|
var restaurantPlayerDataHandle = _restaurantPlayerData.LoadAssetAsync<RestaurantPlayerData>();
|
||||||
var restaurantManagementDataHandle = _restaurantManagementData.LoadAssetAsync<RestaurantManagementData>();
|
var restaurantManagementDataHandle = _restaurantManagementData.LoadAssetAsync<RestaurantManagementData>();
|
||||||
|
var restaurantRunDataHandle = _restaurantRunData.LoadAssetAsync<RestaurantRunData>();
|
||||||
|
var restaurantCustomerDataHandle = _restaurantCustomerData.LoadAssetAsync<RestaurantCustomerData>();
|
||||||
|
|
||||||
await restaurantPlayerDataHandle.Task;
|
await Task.WhenAll(
|
||||||
await restaurantManagementDataHandle.Task;
|
restaurantPlayerDataHandle.Task,
|
||||||
|
restaurantManagementDataHandle.Task,
|
||||||
|
restaurantRunDataHandle.Task,
|
||||||
|
restaurantCustomerDataHandle.Task
|
||||||
|
);
|
||||||
|
|
||||||
PlayerData = restaurantPlayerDataHandle.Result;
|
PlayerData = restaurantPlayerDataHandle.Result;
|
||||||
ManagementData = restaurantManagementDataHandle.Result;
|
ManagementData = restaurantManagementDataHandle.Result;
|
||||||
|
RunData = restaurantRunDataHandle.Result;
|
||||||
|
CustomerData = restaurantCustomerDataHandle.Result;
|
||||||
|
|
||||||
Debug.Assert(PlayerData != null, "RestaurantPlayerData is null");
|
Debug.Assert(PlayerData != null, "RestaurantPlayerData is null");
|
||||||
Debug.Assert(ManagementData != null, "RestaurantManagementData is null");
|
Debug.Assert(ManagementData != null, "RestaurantManagementData is null");
|
||||||
|
Debug.Assert(RunData != null, "RestaurantRunData is null");
|
||||||
|
Debug.Assert(CustomerData != null, "RestaurantCustomerData is null");
|
||||||
|
|
||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
}
|
}
|
||||||
@ -43,6 +57,8 @@ private void OnDisable()
|
|||||||
|
|
||||||
_restaurantPlayerData.ReleaseAsset();
|
_restaurantPlayerData.ReleaseAsset();
|
||||||
_restaurantManagementData.ReleaseAsset();
|
_restaurantManagementData.ReleaseAsset();
|
||||||
|
_restaurantRunData.ReleaseAsset();
|
||||||
|
_restaurantCustomerData.ReleaseAsset();
|
||||||
|
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
}
|
}
|
||||||
|
@ -1,140 +1,9 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Sirenix.OdinInspector;
|
using Sirenix.OdinInspector;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
{
|
{
|
||||||
public class RestaurantCustomerState : ScriptableObject, IGameFlowHandler
|
public class RestaurantCustomerState : ScriptableObject
|
||||||
{
|
{
|
||||||
[Title("스폰 제어")]
|
|
||||||
[Tooltip("플로우 시작 후 첫 손님이 등장하기까지 대기 시간(초)")]
|
|
||||||
[SerializeField] private float _firstSpawnDelaySeconds = 5f;
|
|
||||||
|
|
||||||
[SerializeField] private Vector3 _spawnPoint = new(5f, 0f, 4f);
|
|
||||||
|
|
||||||
[Title("디버그")]
|
|
||||||
[ReadOnly, SerializeField] private SpawnSchedule _spawnSchedule;
|
|
||||||
|
|
||||||
private LevelDataSo _levelDataSo;
|
|
||||||
private CustomerDataSo _customerDataSo;
|
|
||||||
private CustomerPoolDataSo _customerPoolDataSo;
|
|
||||||
|
|
||||||
private ICustomerFactory _iCustomerFactory;
|
|
||||||
|
|
||||||
private CancellationTokenSource _spawnLoopCancellationTokenSource;
|
|
||||||
|
|
||||||
public async Task OnReadyNewFlow(GameFlowState newFlowState)
|
|
||||||
{
|
|
||||||
if (newFlowState == GameFlowState.RunRestaurant)
|
|
||||||
{
|
|
||||||
await InitializeRunRestaurant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task OnExitCurrentFlow(GameFlowState exitingFlowState)
|
|
||||||
{
|
|
||||||
if (exitingFlowState == GameFlowState.RunRestaurant)
|
|
||||||
{
|
|
||||||
_spawnLoopCancellationTokenSource?.Cancel();
|
|
||||||
_spawnLoopCancellationTokenSource?.Dispose();
|
|
||||||
_spawnLoopCancellationTokenSource = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitializeRunRestaurant()
|
|
||||||
{
|
|
||||||
_iCustomerFactory = new CustomerFactory();
|
|
||||||
|
|
||||||
var currentGameLevel = GameState.Instance.LevelState.Level;
|
|
||||||
if (_levelDataSo == null)
|
|
||||||
{
|
|
||||||
_levelDataSo = DataManager.Instance.GetDataSo<LevelDataSo>();
|
|
||||||
}
|
|
||||||
if (_customerDataSo == null)
|
|
||||||
{
|
|
||||||
_customerDataSo = DataManager.Instance.GetDataSo<CustomerDataSo>();
|
|
||||||
}
|
|
||||||
if (_customerPoolDataSo == null)
|
|
||||||
{
|
|
||||||
_customerPoolDataSo = DataManager.Instance.GetDataSo<CustomerPoolDataSo>();
|
|
||||||
}
|
|
||||||
var currentLevelData = _levelDataSo.GetDataList().FirstOrDefault(data => data.Level == currentGameLevel);
|
|
||||||
|
|
||||||
Debug.Assert(currentLevelData != null, "currentLevelData is null");
|
|
||||||
|
|
||||||
var normalPool = _customerPoolDataSo.GetDataById(currentLevelData.CustomerPool);
|
|
||||||
var specialPool = _customerPoolDataSo.GetDataById(currentLevelData.SpecialCustomerPool);
|
|
||||||
|
|
||||||
_spawnLoopCancellationTokenSource?.Cancel();
|
|
||||||
_spawnLoopCancellationTokenSource = new CancellationTokenSource();
|
|
||||||
await RunSpawnLoopAsync(currentLevelData, normalPool, specialPool, _spawnLoopCancellationTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RunSpawnLoopAsync(LevelData levelData, CustomerPoolData normalPool, CustomerPoolData specialPool, CancellationToken token)
|
|
||||||
{
|
|
||||||
if (_firstSpawnDelaySeconds > 0)
|
|
||||||
{
|
|
||||||
await Awaitable.WaitForSecondsAsync(_firstSpawnDelaySeconds, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
var scheduleBuilder = CreateBuilder(levelData.SpawnType);
|
|
||||||
int randomSeed = Environment.TickCount;
|
|
||||||
|
|
||||||
SpawnSchedule MakeSchedule() => scheduleBuilder.Build(new SpawnScheduleBuildArgs
|
|
||||||
{
|
|
||||||
NormalIds = (IReadOnlyList<string>) (normalPool?.ValidCustomers) ?? Array.Empty<string>(),
|
|
||||||
SpecialIds = (IReadOnlyList<string>) (specialPool?.ValidCustomers) ?? Array.Empty<string>(),
|
|
||||||
NormalQuota = Math.Max(0, normalPool?.CustomerLimitCount ?? 0),
|
|
||||||
SpecialQuota = Math.Max(0, specialPool?.CustomerLimitCount ?? 0),
|
|
||||||
Seed = ++randomSeed
|
|
||||||
});
|
|
||||||
|
|
||||||
_spawnSchedule = MakeSchedule();
|
|
||||||
float wait = Mathf.Max(0.1f, levelData.CustomerRespawnTime);
|
|
||||||
|
|
||||||
while (token.IsCancellationRequested == false)
|
|
||||||
{
|
|
||||||
if (Application.isPlaying == false)
|
|
||||||
{
|
|
||||||
_spawnLoopCancellationTokenSource?.Cancel();
|
|
||||||
_spawnLoopCancellationTokenSource?.Dispose();
|
|
||||||
_spawnLoopCancellationTokenSource = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_spawnSchedule.TryDequeue(out var customerId) == false) break;
|
|
||||||
|
|
||||||
if (_customerDataSo.TryGetDataById(customerId, out var customerData))
|
|
||||||
{
|
|
||||||
var rotation = Quaternion.identity;
|
|
||||||
|
|
||||||
_ = _iCustomerFactory.CreateAsync(new CustomerSpawnArgs
|
|
||||||
{
|
|
||||||
CustomerData = customerData,
|
|
||||||
Position = _spawnPoint,
|
|
||||||
Rotation = rotation,
|
|
||||||
Parent = null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(wait), token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ISpawnScheduleBuilder CreateBuilder(SpawnType type)
|
|
||||||
{
|
|
||||||
return type switch
|
|
||||||
{
|
|
||||||
SpawnType.Random => new RandomSpawnScheduleBuilder(),
|
|
||||||
SpawnType.Regular => new RegularSpawnScheduleBuilder(),
|
|
||||||
_ => new RandomSpawnScheduleBuilder()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,12 @@ namespace DDD
|
|||||||
{
|
{
|
||||||
public class RestaurantRunState : ScriptableObject
|
public class RestaurantRunState : ScriptableObject
|
||||||
{
|
{
|
||||||
|
private Vector3 _spawnPoint = new(5f, 0f, 4f);
|
||||||
|
public Vector3 SpawnPoint => _spawnPoint;
|
||||||
|
|
||||||
|
public void InitializeSpawnPoint(Vector3 spawnPoint)
|
||||||
|
{
|
||||||
|
_spawnPoint = spawnPoint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -23,7 +23,6 @@ public static class DataConstants
|
|||||||
public const string TasteDataSo = "TasteDataSo";
|
public const string TasteDataSo = "TasteDataSo";
|
||||||
public const string EnvironmentDataSo = "EnvironmentDataSo";
|
public const string EnvironmentDataSo = "EnvironmentDataSo";
|
||||||
public const string LevelDataSo = "LevelDataSo";
|
public const string LevelDataSo = "LevelDataSo";
|
||||||
public const string CustomerDataSo = "CustomerDataSo";
|
|
||||||
public const string CustomerPoolDataSo = "CustomerPoolDataSo";
|
public const string CustomerPoolDataSo = "CustomerPoolDataSo";
|
||||||
public const string UiInputBindingSo = "UiInputBindingSo";
|
public const string UiInputBindingSo = "UiInputBindingSo";
|
||||||
|
|
||||||
@ -31,8 +30,6 @@ public static class DataConstants
|
|||||||
|
|
||||||
public const string AtlasLabel = "Atlas";
|
public const string AtlasLabel = "Atlas";
|
||||||
public const string BasePropSpriteMaterial = "BasePropSpriteMaterial";
|
public const string BasePropSpriteMaterial = "BasePropSpriteMaterial";
|
||||||
|
|
||||||
public const string CustomerNpcPrefab = "CustomerNpc";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RestaurantPlayerAnimationType
|
public static class RestaurantPlayerAnimationType
|
||||||
|
Loading…
Reference in New Issue
Block a user