Files
Ultimate-Fishing-Simulator-…/Assets/Scripts/Assembly-CSharp/WaterFXManager.cs
2026-03-04 09:37:33 +08:00

248 lines
5.8 KiB
C#

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<float3> Positions;
[ReadOnly]
public NativeArray<int> Hashes;
[ReadOnly]
public float3 CameraPosition;
[ReadOnly]
public float CullingDistance;
[WriteOnly]
public NativeHashMap<int, bool> ChangeMap;
public NativeHashMap<int, bool> 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<VisualEffect> _splashPool;
private List<(VisualEffect splash, float timeActive)> _activeSplashes;
private Dictionary<int, Pool<WaterDecal>> _decalPools;
private Dictionary<int, WaterFXInstance> _fxInstances;
private NativeHashMap<int, bool> _visibilityMap;
private NativeHashMap<int, bool> _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<VisualEffect>.CreateInstance(_splashPrefab, 2, null, "Splash pool");
}
private void InitializeCollections()
{
_activeSplashes = new List<(VisualEffect, float)>();
_decalPools = new Dictionary<int, Pool<WaterDecal>>();
_fxInstances = new Dictionary<int, WaterFXInstance>();
}
private void InitializeNativeCollections()
{
_visibilityMap = new NativeHashMap<int, bool>(_fxInstances.Count, Allocator.Persistent);
_changeMap = new NativeHashMap<int, bool>(_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<float3> positions = new NativeArray<float3>(_fxInstances.Count, Allocator.Persistent);
NativeArray<int> hashes = new NativeArray<int>(_fxInstances.Count, Allocator.Persistent);
int num = 0;
foreach (KeyValuePair<int, WaterFXInstance> 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<int, bool> item in _changeMap)
{
if (item.Value)
{
_fxInstances[item.Key].IsVisible = _visibilityMap[item.Key];
item.Value = false;
}
}
positions.Dispose();
hashes.Dispose();
}
public Pool<WaterDecal> GetPool(WaterDecal decalPrefab)
{
int hashCode = decalPrefab.GetHashCode();
if (!_decalPools.ContainsKey(hashCode))
{
_decalPools.Add(hashCode, Pool<WaterDecal>.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);
}
}
}