using System.Collections.Generic; using Assets.Code.Scripts; using Obvious.Soap; using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering.HighDefinition; using UnityEngine.VFX; public sealed class WaterFXManager : MonoBehaviour, IBatchUpdate { [BurstCompile(CompileSynchronously = true)] private struct CalculateFXVisibilityJob : IJobFor { [ReadOnly] public NativeArray Positions; [ReadOnly] public NativeArray Hashes; [ReadOnly] public float3 CameraPosition; [ReadOnly] public float CullingDistance; [WriteOnly] public NativeHashMap ChangeMap; public NativeHashMap VisibilityMap; public void Execute(int i) { float num = math.distance(CameraPosition, Positions[i]); int key = Hashes[i]; if (VisibilityMap[key]) { if (num > CullingDistance) { VisibilityMap[key] = false; ChangeMap[key] = true; } } else if (num <= CullingDistance) { VisibilityMap[key] = true; ChangeMap[key] = true; } } } [SerializeField] private IntVariable _waterQuality; [SerializeField] private UpdateManager.UpdateMode _updateMode = UpdateManager.UpdateMode.Always; [SerializeField] private Transform _playerCameraTransform; public float MaxSpawnDistanceFromCamera = 20f; [SerializeField] private VisualEffect _splashPrefab; private const float VFX_DISABLE_DELAY = 0.2f; private static WaterFXManager _instance; private Camera _camera; private Pool _splashPool; private List<(VisualEffect splash, float timeActive)> _activeSplashes; private Dictionary> _decalPools; private Dictionary _fxInstances; private NativeHashMap _visibilityMap; private NativeHashMap _changeMap; public static WaterFXManager GetInstance() { return _instance; } public void SetCamera(Camera camera) { _camera = camera; } public void AddFXInstance(int hash, WaterFXInstance instance) { if (_changeMap.IsCreated && _visibilityMap.IsCreated) { _fxInstances.Add(hash, instance); _visibilityMap.Add(hash, item: false); _changeMap.Add(hash, item: false); } } public void RemoveFXInstance(int id) { if (_changeMap.IsCreated && _visibilityMap.IsCreated) { _fxInstances.Remove(id); _visibilityMap.Remove(id); _changeMap.Remove(id); } } private void Awake() { _instance = this; _camera = Camera.main; InitializePools(); InitializeCollections(); InitializeNativeCollections(); } private void InitializePools() { _splashPool = Pool.CreateInstance(_splashPrefab, 2, null, "Splash pool"); } private void InitializeCollections() { _activeSplashes = new List<(VisualEffect, float)>(); _decalPools = new Dictionary>(); _fxInstances = new Dictionary(); } private void InitializeNativeCollections() { _visibilityMap = new NativeHashMap(_fxInstances.Count, Allocator.Persistent); _changeMap = new NativeHashMap(_fxInstances.Count, Allocator.Persistent); } private void Start() { if (_playerCameraTransform == null) { _playerCameraTransform = _camera.transform; } UpdateManager.RegisterSlicedUpdate(this, _updateMode); } private void OnDestroy() { UpdateManager.DeregisterSlicedUpdate(this); Dispose(); } private void Dispose() { _visibilityMap.Dispose(); _changeMap.Dispose(); } public void BatchUpdate() { UpdateSplashes(); if (_waterQuality.Value != 0 && _changeMap.IsCreated && _visibilityMap.IsCreated) { UpdateDecals(); } } private void UpdateSplashes() { if (_activeSplashes == null) { return; } for (int num = _activeSplashes.Count - 1; num >= 0; num--) { if (_activeSplashes[num].timeActive < 0.2f) { _activeSplashes[num] = (_activeSplashes[num].splash, _activeSplashes[num].timeActive + Time.deltaTime); } else if (!_activeSplashes[num].splash.HasAnySystemAwake()) { _splashPool.Reclaim(_activeSplashes[num].splash); _activeSplashes.RemoveAt(num); } } } private void UpdateDecals() { NativeArray positions = new NativeArray(_fxInstances.Count, Allocator.Persistent); NativeArray hashes = new NativeArray(_fxInstances.Count, Allocator.Persistent); int num = 0; foreach (KeyValuePair fxInstance in _fxInstances) { positions[num] = fxInstance.Value.transform.position; hashes[num] = fxInstance.Key; num++; } new CalculateFXVisibilityJob { Positions = positions, Hashes = hashes, CameraPosition = _playerCameraTransform.position, CullingDistance = MaxSpawnDistanceFromCamera, ChangeMap = _changeMap, VisibilityMap = _visibilityMap }.Schedule(_fxInstances.Count, default(JobHandle)).Complete(); foreach (KVPair item in _changeMap) { if (item.Value) { _fxInstances[item.Key].IsVisible = _visibilityMap[item.Key]; item.Value = false; } } positions.Dispose(); hashes.Dispose(); } public Pool GetPool(WaterDecal decalPrefab) { int hashCode = decalPrefab.GetHashCode(); if (!_decalPools.ContainsKey(hashCode)) { _decalPools.Add(hashCode, Pool.CreateInstance(decalPrefab, 2, null, $"Water decal pool {_decalPools.Count + 1}")); } return _decalPools[hashCode]; } public void CreateSplash(Vector3 position, float scaleMultiplier) { if (!(Vector3.Distance(position, _playerCameraTransform.position) > MaxSpawnDistanceFromCamera)) { VisualEffect instance = _splashPool.GetInstance(); _activeSplashes.Add((instance, 0f)); instance.transform.position = position; instance.transform.rotation = Quaternion.identity; instance.transform.localScale = Vector3.one * scaleMultiplier; instance.gameObject.SetActive(value: true); } } }