CapersProject/Assets/02.Scripts/LiquidController.cs
2024-09-09 21:27:15 +09:00

317 lines
9.5 KiB
C#

using System.Collections.Generic;
using System.Linq;
using DG.Tweening;
using Sirenix.OdinInspector;
using TMPro;
using UnityEngine;
using UnityEngine.Pool;
namespace BlueWater
{
public class LiquidController : MonoBehaviour
{
#region Variables
[Title("컴포넌트")]
[SerializeField]
private GameObject _liquidPanel;
[SerializeField]
private Renderer _renderTexture;
[SerializeField]
private Renderer _liquidRenderer;
[SerializeField]
private Collider2D _reachedCollider;
[SerializeField]
private TMP_Text _amountText;
[Title("스폰 데이터")]
[SerializeField, Required]
private Transform _spawnTransform;
[SerializeField]
private Transform _spawnLocation;
[SerializeField]
private Vector3 _pushDirection;
[SerializeField]
private float _pushPower;
[Title("액체")]
[SerializeField, Required, Tooltip("액체 프리팹")]
private Liquid _liquidObject;
[SerializeField, Tooltip("떨어지는 액체의 색상")]
private Color _liquidColor = new(1f, 0.8431373f, 0f, 1f);
[SerializeField, Tooltip("초당 생성되는 액체 수(ml)")]
private int _liquidsPerSecond = 80;
[SerializeField]
private int _maxLiquidCount = 400;
[SerializeField, Range(0f, 1f), Tooltip("목표 색상으로 변경되는데 걸리는 시간")]
private float _colorLerpSpeed = 0.5f;
[SerializeField, Range(1f, 5f), Tooltip("목표 색상 * 밝기")]
private float _colorIntensity = 2f;
[Title("오브젝트 풀링")]
[SerializeField, Tooltip("오브젝트 풀링 최대 개수")]
private int _objectPoolCount = 1000;
[Title("패널")]
[SerializeField]
private float _moveDuration = 0.5f;
private IObjectPool<Liquid> _objectPool;
private List<Liquid> _activeLiquids = new();
private Dictionary<Color, int> _colorCounts = new();
private Material _instanceMaterial;
private Tween _showTween;
private Tween _hideTween;
private bool _isPouring;
private float _startTime = float.PositiveInfinity;
private int _instanceLiquidCount;
private float _currentLiquidAmount;
private float _liquidReachedTime;
private float _timeInterval;
private Color _currentMixedColor = Color.black;
private Color _targetColor;
// Hashes
private static readonly int _liquidAmountHash = Shader.PropertyToID("_LiquidAmount");
private static readonly int _liquidColorHash = Shader.PropertyToID("_LiquidColor");
private static readonly int _renderTextureColorHash = Shader.PropertyToID("_Color");
#endregion
// Unity events
#region Unity events
private void Awake()
{
_objectPool = new ObjectPool<Liquid>(CreateObject, OnGetObject, OnReleaseObject, OnDestroyObject, maxSize: _objectPoolCount);
_hideTween = _liquidPanel.transform.DOMoveX(-150f, _moveDuration).From(-249f).Pause()
.SetAutoKill(false);
_showTween = _liquidPanel.transform.DOMoveX(-249f, _moveDuration).From(-150f).Pause()
.SetAutoKill(false);
}
private void Start()
{
TycoonEvents.OnLiquidRegionEntered += ShowPanel;
TycoonEvents.OnLiquidRegionExited += HidePanel;
_instanceMaterial = Instantiate(_liquidRenderer.material);
_liquidRenderer.material = _instanceMaterial;
_timeInterval = 1f / _liquidsPerSecond;
_instanceMaterial.SetFloat(_liquidAmountHash, 0f);
SetCurrentAmount(0f);
}
private void Update()
{
if (_isPouring)
{
if (_instanceLiquidCount >= _maxLiquidCount)
{
InActiveIsPouring();
return;
}
if (Time.time - _startTime >= _timeInterval)
{
_objectPool.Get();
if (!_colorCounts.TryAdd(_liquidColor, 1))
{
_colorCounts[_liquidColor] += 1;
}
_startTime = Time.time;
}
}
if (_liquidReachedTime + _colorLerpSpeed >= Time.time)
{
_currentMixedColor = Color.Lerp(_currentMixedColor, _targetColor, _colorLerpSpeed * Time.deltaTime);
_instanceMaterial.SetColor(_liquidColorHash, _currentMixedColor * _colorIntensity);
}
}
private void OnDestroy()
{
TycoonEvents.OnLiquidRegionEntered -= ShowPanel;
TycoonEvents.OnLiquidRegionExited -= HidePanel;
}
#endregion
// Initialize methods
#region Initialize methods
public void Initialize()
{
_instanceLiquidCount = 0;
SetCurrentAmount(0f);
_currentMixedColor = _liquidColor;
_instanceMaterial.SetColor(_liquidColorHash, _currentMixedColor * _colorIntensity);
}
#endregion
// Object pooling system
#region Object pooling system
private Liquid CreateObject()
{
var instance = Instantiate(_liquidObject, _spawnTransform.position, Quaternion.identity, _spawnLocation);
instance.SetManagedPool(_objectPool);
return instance;
}
private void OnGetObject(Liquid liquid)
{
liquid.transform.position = _spawnTransform.position;
liquid.transform.rotation = Quaternion.identity;
liquid.gameObject.SetActive(true);
_instanceLiquidCount++;
liquid.Initialize(this, _reachedCollider, _liquidColor, _pushDirection.normalized * _pushPower);
if (_renderTexture && _renderTexture.material.GetColor(_renderTextureColorHash) != _liquidColor)
{
_renderTexture.material.SetColor(_renderTextureColorHash, _liquidColor);
}
_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);
}
#endregion
// Custom methods
#region Custom methods
[Button("기본 색상")]
private void DefaultColor() => _liquidColor = new Color(1f, 0.8431373f, 0f, 1f);
/// <summary>
/// 술 제조 과정 초기화 함수
/// </summary>
public void ReleaseAllObject()
{
// 리스트 삭제는 뒤에서부터 해야 오류가 없음
for (var i = _activeLiquids.Count - 1; i >= 0; i--)
{
_activeLiquids[i].Destroy();
}
_colorCounts.Clear();
_instanceLiquidCount = 0;
SetCurrentAmount(0f);
_instanceMaterial.SetFloat(_liquidAmountHash, 0f);
}
public void ActiveIsPouring()
{
_startTime = Time.time;
_isPouring = true;
}
public void InActiveIsPouring()
{
_isPouring = false;
}
private void SetCurrentAmount(float value)
{
_currentLiquidAmount = value;
if (_amountText)
{
var percent = (int)(_currentLiquidAmount / _maxLiquidCount * 100);
_amountText.text = $"{percent}%";
}
else
{
if (_amountText.enabled)
{
_amountText.enabled = false;
}
}
}
/// <summary>
/// 사용된 색상의 비율에 맞게 색을 혼합시키는 함수
/// </summary>
private Color MixColorsByTime()
{
var totalCounts = _colorCounts.Values.Sum();
var mixedColor = Color.black;
foreach (var element in _colorCounts)
{
var color = element.Key;
var count = element.Value;
var ratio = count / (float)totalCounts;
mixedColor += color * ratio;
}
mixedColor.a = 1f;
return mixedColor;
}
/// <summary>
/// 액체가 특정 오브젝트에 충돌했을 때, 실행해야하는 과정
/// </summary>
public void OnLiquidReached()
{
_liquidReachedTime = Time.time;
SetCurrentAmount(++_currentLiquidAmount);
var liquidAmount = Mathf.Clamp(_currentLiquidAmount / _maxLiquidCount, 0f, 1f);
_instanceMaterial.SetFloat(_liquidAmountHash, liquidAmount);
_targetColor = MixColorsByTime();
if (liquidAmount >= 1f)
{
InActiveIsPouring();
}
}
public void ShowPanel()
{
_hideTween.Pause();
_showTween.Restart();
}
public void HidePanel()
{
_showTween.Pause();
_hideTween.Restart();
}
#endregion
}
}