using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.Pool; namespace BlueWater { public class LiquidController : MonoBehaviour { [SerializeField] private Renderer _renderTexture; [SerializeField] private Renderer _liquidRenderer; [SerializeField, Required] private Liquid _liquidObject; [SerializeField, Required] private Transform _spawnTransform; [SerializeField] private Transform _spawnLocation; [SerializeField] private Collider2D _reachedCollider; [SerializeField] private int _objectPoolCount = 1000; [SerializeField] private Color _liquidColor = new(0f, 0.7294118f, 1f, 1f); [SerializeField, Tooltip("1초에 차는 %")] private float _pouringRate = 20f; [SerializeField] private int _liquidsPerSecond = 100; [SerializeField, Range(0f, 100f)] private float _currentLiquidAmount; private IObjectPool _objectPool; private List _activeLiquids = new(); private Dictionary _colorTimes = new(); private Material _instanceMaterial; private bool _isPouring; private float _startTime = float.PositiveInfinity; private float _timeInterval; private float _liquidPerObject; // Hashes private static readonly int _liquidAmountHash = Shader.PropertyToID("_LiquidAmount"); private static readonly int _liquidColorHash = Shader.PropertyToID("_LiquidColor"); private void Awake() { _objectPool = new ObjectPool(CreateObject, OnGetObject, OnReleaseObject, OnDestroyObject, maxSize: _objectPoolCount); } private void Start() { _instanceMaterial = Instantiate(_liquidRenderer.material); _liquidRenderer.material = _instanceMaterial; _timeInterval = 1f / _liquidsPerSecond; _liquidPerObject = _pouringRate / _liquidsPerSecond; _instanceMaterial.SetFloat(_liquidAmountHash, 0f); } private void Update() { if (_isPouring) { if (_currentLiquidAmount >= 100f) { InActiveIsPouring(); return; } if (Time.time - _startTime >= _timeInterval) { _objectPool.Get(); if (_colorTimes.ContainsKey(_liquidColor)) { _colorTimes[_liquidColor] += _timeInterval; } else { _colorTimes[_liquidColor] = _timeInterval; } _startTime = Time.time; } } } private Liquid CreateObject() { var instance = Instantiate(_liquidObject, _spawnTransform.position, Quaternion.identity, _spawnLocation); instance.SetManagedPool(_objectPool); instance.Initialize(this, _reachedCollider, _liquidColor); return instance; } private void OnGetObject(Liquid liquid) { liquid.transform.position = _spawnTransform.position; liquid.transform.rotation = Quaternion.identity; liquid.Initialize(this, _reachedCollider, _liquidColor); if (_renderTexture && _renderTexture.material.GetColor("_Color") != _liquidColor) { _renderTexture.material.SetColor("_Color", _liquidColor); } liquid.gameObject.SetActive(true); _activeLiquids.Add(liquid); } private void OnReleaseObject(Liquid liquid) { liquid.gameObject.SetActive(false); _activeLiquids.Remove(liquid); } private void OnDestroyObject(Liquid liquid) { Destroy(liquid.gameObject); _activeLiquids.Remove(liquid); } private Color MixColorsByTime() { var totalTime = _colorTimes.Values.Sum(); // 혼합된 색상 초기화 (검은색) var mixedColor = Color.black; // 색상 혼합 foreach (var element in _colorTimes) { var color = element.Key; var time = element.Value; var ratio = time / totalTime; mixedColor += color * ratio; } mixedColor.a = 1f; return mixedColor; } [Button("기본 색상")] private void DefaultColor() => _liquidColor = new Color(0f, 0.7294118f, 1f, 1f); public void ReleaseAllObject() { // 뒤에서부터 Remove해야 오류가 없음 for (var i = _activeLiquids.Count - 1; i >= 0; i--) { _activeLiquids[i].Destroy(); } _colorTimes.Clear(); _currentLiquidAmount = 0f; _instanceMaterial.SetFloat(_liquidAmountHash, 0f); } public void ActiveIsPouring() { _startTime = Time.time; _isPouring = true; } public void InActiveIsPouring() { _isPouring = false; } public void OnLiquidReached() { // 컵에 채워진 액체의 양을 증가시킴 _currentLiquidAmount += _liquidPerObject; _currentLiquidAmount = Mathf.Clamp(_currentLiquidAmount, 0f, 100f); var liquidAmount = _currentLiquidAmount * 0.01f; _instanceMaterial.SetFloat(_liquidAmountHash, liquidAmount); _instanceMaterial.SetColor(_liquidColorHash, MixColorsByTime()); // 액체가 100%에 도달하면 pouring을 멈춤 if (_currentLiquidAmount >= 100f) { InActiveIsPouring(); } } } }