using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.Pool; using Object = UnityEngine.Object; using Random = UnityEngine.Random; namespace DanieleMarotta.RiversongCodeShowcase { [Service(typeof(IRawResourceVisualizationCollection))] public class RawResourceRenderingSystem : GameSystem, IInitializable, IDisposable, IRawResourceVisualizationCollection, IOnWorldGenerationCompletedCallback { private const int MaxCreatedPerUpdate = 16; [InjectService] private IScene _scene; [InjectService] private IGameDatabase _gameDatabase; [InjectService] private ISignalBus _signalBus; [InjectService] private World _world; [InjectService] private GameConfig _config; private Dictionary _visualizationsById = new(); public RawResourceRenderingSystem(IServiceLocator serviceLocator) : base(serviceLocator) { } public UniTask InitializeAsync() { _signalBus.Subscribe(OnWorldGenerationCompleted); _signalBus.Subscribe(OnCollectDeletedGameObjects); _signalBus.Subscribe(OnRawResourcesRemoved); return UniTask.CompletedTask; } public void Dispose() { _signalBus.Unsubscribe(OnWorldGenerationCompleted); _signalBus.Unsubscribe(OnCollectDeletedGameObjects); _signalBus.Unsubscribe(OnRawResourcesRemoved); } private void OnWorldGenerationCompleted(WorldGenerationCompletedSignal signal) { signal.Callbacks.Add(this); } public async UniTask OnWorldGenerationCompletedAsync(World world) { await CreateVisualizationsAsync(); } private async UniTask CreateVisualizationsAsync() { var visualizationCount = 0; foreach (var resourceNode in _world.RawResources) { var definition = _gameDatabase.WithId(resourceNode.DefinitionId); var rotation = definition.RandomRotation ? Quaternion.Euler(0, Random.Range(0, 360), 0) : resourceNode.Orientation.ToQuaternion(); var scale = Vector3.one * Random.Range(definition.ScaleRange.x, definition.ScaleRange.y); GameObject prefab; if (definition.Prefab.IsValid()) prefab = (GameObject)definition.Prefab.Asset; else prefab = await definition.Prefab.LoadAssetAsync(); var visualization = Object.Instantiate(prefab, _scene.SceneFolders.RawResources); visualization.transform.SetPositionAndRotation(resourceNode.Position, rotation); visualization.transform.localScale = Vector3.Scale(visualization.transform.localScale, scale); _visualizationsById.Add(resourceNode.Id, visualization); if (++visualizationCount % MaxCreatedPerUpdate == 0) await UniTask.NextFrame(); } } private void OnCollectDeletedGameObjects(CollectDeletedGameObjectsSignal signal) { if ((signal.Filter & DeletedGameObjectsFilter.RawResources) == 0) return; using var resourceNodesScope = ListPool<(int, bool)>.Get(out var resourceNodeIds); _world.RawResources.GetResourceNodes(signal.Rect, _config.GeneralSettings.BaseElevation, resourceNodeIds); foreach (var (id, canBeDeleted) in resourceNodeIds) { if (!canBeDeleted) continue; var visualization = _visualizationsById[id]; signal.GameObjects.Add(visualization.gameObject); } } private void OnRawResourcesRemoved(RawResourcesRemovedSignal signal) { foreach (var resourceNode in signal.ResourceNodes) { if (!_visualizationsById.Remove(resourceNode.Id, out var visualization)) return; _ = DestroyVisualizationAsync(visualization); } } private async UniTask DestroyVisualizationAsync(GameObject visualization) { if (visualization.TryGetComponent(out var animation)) await animation.PlayAsync(); Object.Destroy(visualization); } public bool TryGetVisualization(int id, out GameObject visualization) { return _visualizationsById.TryGetValue(id, out visualization); } } }