525 lines
18 KiB
C#
525 lines
18 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using BlueWater.Items;
|
|
using BlueWater.Tycoons;
|
|
using DG.Tweening;
|
|
using Sirenix.OdinInspector;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.Pool;
|
|
using UnityEngine.UI;
|
|
|
|
namespace BlueWater
|
|
{
|
|
public class LiquidController : MonoBehaviour
|
|
{
|
|
#region Variables
|
|
|
|
[Title("컴포넌트")]
|
|
[SerializeField]
|
|
private GameObject _liquidPanel;
|
|
|
|
[SerializeField]
|
|
private GameObject _shaker;
|
|
|
|
[SerializeField]
|
|
private Renderer _renderTexture;
|
|
|
|
[SerializeField]
|
|
private Renderer _liquidRenderer;
|
|
|
|
[SerializeField]
|
|
private Collider2D _reachedCollider;
|
|
|
|
[SerializeField]
|
|
private TMP_Text _amountText;
|
|
|
|
[SerializeField]
|
|
private Image _completeCocktailImage;
|
|
|
|
[SerializeField]
|
|
private TMP_Text _completeText;
|
|
|
|
[Title("스폰 데이터")]
|
|
[SerializeField, Required]
|
|
private Transform _spawnTransform;
|
|
|
|
[SerializeField]
|
|
private Transform _spawnLocation;
|
|
|
|
[SerializeField]
|
|
private Vector3 _pushDirection;
|
|
|
|
[SerializeField]
|
|
private float _pushPower;
|
|
|
|
[Title("Liquid / Garnish")]
|
|
[SerializeField, Required, Tooltip("원액 프리팹")]
|
|
private Liquid _liquidObject;
|
|
|
|
[SerializeField, Required, Tooltip("가니쉬 프리팹")]
|
|
private Garnish _garnishObject;
|
|
|
|
[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;
|
|
|
|
[SerializeField]
|
|
private Vector3 endPosition = new(-230f, 23f, 0f);
|
|
|
|
private Barrel _currentBarrel;
|
|
private IObjectPool<Liquid> _liquidObjectPool;
|
|
private IObjectPool<Garnish> _garnishObjectPool;
|
|
private List<Liquid> _activeLiquidDatas = new();
|
|
private List<Garnish> _activeGarnishDatas = new();
|
|
private Dictionary<LiquidData, int> _liquidDataCounts = new(7);
|
|
private Material _instanceMaterial;
|
|
private Tween _showTween;
|
|
private Tween _hideTween;
|
|
|
|
private bool _isShowingPanel;
|
|
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");
|
|
|
|
#endregion
|
|
|
|
// Unity events
|
|
#region Unity events
|
|
|
|
private void Awake()
|
|
{
|
|
_liquidObjectPool = new ObjectPool<Liquid>(CreateLiquidObject, OnGetLiquidObject, OnReleaseLiquidObject, OnDestroyLiquidObject, maxSize: _objectPoolCount);
|
|
_garnishObjectPool = new ObjectPool<Garnish>(CreateGarnishObject, OnGetGarnishObject, OnReleaseGarnishObject, OnDestroyGarnishObject, maxSize: _objectPoolCount);
|
|
|
|
_hideTween = _liquidPanel.transform.DOMoveX(endPosition.x + 100f, _moveDuration).Pause()
|
|
.SetAutoKill(false);
|
|
|
|
_showTween = _liquidPanel.transform.DOMoveX(endPosition.x, _moveDuration).Pause()
|
|
.SetAutoKill(false);
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
EventManager.OnLiquidRegionEntered += ShowPanel;
|
|
EventManager.OnLiquidRegionExited += HidePanel;
|
|
EventManager.OnCocktailDiscarded += ReleaseAllObject;
|
|
EventManager.OnPlaceOnServingTable += ReleaseAllObject;
|
|
LiquidIngredient.OnReachedTarget += OnTargetReached;
|
|
Barrel.OnBarrelInteracted += HandleBarrelInteraction;
|
|
Barrel.OnBarrelCancelInteracted += HandleBarrelCancelInteraction;
|
|
|
|
_instanceMaterial = Instantiate(_liquidRenderer.material);
|
|
_liquidRenderer.material = _instanceMaterial;
|
|
|
|
_instanceMaterial.SetFloat(LiquidAmountHash, 0f);
|
|
_timeInterval = 1f / _liquidsPerSecond;
|
|
_shaker.SetActive(true);
|
|
_amountText.enabled = true;
|
|
_completeCocktailImage.enabled = false;
|
|
_completeText.enabled = false;
|
|
_instanceLiquidCount = 0;
|
|
SetCurrentAmount(0f);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (_isPouring)
|
|
{
|
|
var currentBarrel = _currentBarrel;
|
|
|
|
// 술이 완성되었을 때
|
|
if (_instanceLiquidCount >= _maxLiquidCount)
|
|
{
|
|
StartCoroutine(nameof(CompleteCocktail));
|
|
return;
|
|
}
|
|
|
|
if (Time.time - _startTime >= _timeInterval)
|
|
{
|
|
switch (currentBarrel.GetLiquidData().Type)
|
|
{
|
|
case LiquidType.None:
|
|
Debug.LogError("원액 종류 None 오류");
|
|
break;
|
|
case LiquidType.Liquid:
|
|
_liquidObjectPool.Get();
|
|
break;
|
|
case LiquidType.Garnish:
|
|
_garnishObjectPool.Get();
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
if (!_liquidDataCounts.TryAdd(currentBarrel.GetLiquidData(), 1))
|
|
{
|
|
_liquidDataCounts[currentBarrel.GetLiquidData()] += 1;
|
|
}
|
|
|
|
currentBarrel.Consume(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()
|
|
{
|
|
EventManager.OnLiquidRegionEntered -= ShowPanel;
|
|
EventManager.OnLiquidRegionExited -= HidePanel;
|
|
EventManager.OnCocktailDiscarded -= ReleaseAllObject;
|
|
EventManager.OnPlaceOnServingTable -= ReleaseAllObject;
|
|
|
|
LiquidIngredient.OnReachedTarget -= OnTargetReached;
|
|
Barrel.OnBarrelInteracted -= HandleBarrelInteraction;
|
|
Barrel.OnBarrelCancelInteracted -= HandleBarrelCancelInteraction;
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Object pooling system
|
|
#region Object pooling system
|
|
|
|
// 원액 오브젝트 풀
|
|
private Liquid CreateLiquidObject()
|
|
{
|
|
var instance = Instantiate(_liquidObject, _spawnTransform.position, Quaternion.identity, _spawnLocation);
|
|
instance.SetManagedPool(_liquidObjectPool);
|
|
return instance;
|
|
}
|
|
|
|
private void OnGetLiquidObject(Liquid liquid)
|
|
{
|
|
_instanceLiquidCount++;
|
|
var liquidColor = _currentBarrel.GetLiquidData().Color;
|
|
liquid.Initialize(_spawnTransform.position, Quaternion.identity, _reachedCollider, _pushDirection.normalized * _pushPower, liquidColor);
|
|
_activeLiquidDatas.Add(liquid);
|
|
}
|
|
|
|
private void OnReleaseLiquidObject(Liquid liquid)
|
|
{
|
|
liquid.gameObject.SetActive(false);
|
|
_activeLiquidDatas.Remove(liquid);
|
|
}
|
|
|
|
private void OnDestroyLiquidObject(Liquid liquid)
|
|
{
|
|
Destroy(liquid.gameObject);
|
|
_activeLiquidDatas.Remove(liquid);
|
|
}
|
|
|
|
// 가니쉬 오브젝트 풀
|
|
private Garnish CreateGarnishObject()
|
|
{
|
|
var instance = Instantiate(_garnishObject, _spawnTransform.position, Quaternion.identity, _spawnLocation);
|
|
instance.SetManagedPool(_garnishObjectPool);
|
|
return instance;
|
|
}
|
|
|
|
private void OnGetGarnishObject(Garnish garnish)
|
|
{
|
|
_instanceLiquidCount++;
|
|
var liquidSprite = _currentBarrel.GetLiquidData().Sprite;
|
|
garnish.Initialize(_spawnTransform.position, Quaternion.identity, _reachedCollider, _pushDirection.normalized * _pushPower, liquidSprite);
|
|
_activeGarnishDatas.Add(garnish);
|
|
}
|
|
|
|
private void OnReleaseGarnishObject(Garnish garnish)
|
|
{
|
|
garnish.gameObject.SetActive(false);
|
|
_activeGarnishDatas.Remove(garnish);
|
|
}
|
|
|
|
private void OnDestroyGarnishObject(Garnish garnish)
|
|
{
|
|
Destroy(garnish.gameObject);
|
|
_activeGarnishDatas.Remove(garnish);
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Custom methods
|
|
#region Custom methods
|
|
|
|
/// <summary>
|
|
/// 술 제조 과정 초기화 함수
|
|
/// </summary>
|
|
public void ReleaseAllObject()
|
|
{
|
|
// 리스트 삭제는 뒤에서부터 해야 오류가 없음
|
|
for (var i = _activeLiquidDatas.Count - 1; i >= 0; i--)
|
|
{
|
|
_activeLiquidDatas[i].Destroy();
|
|
}
|
|
|
|
_liquidDataCounts.Clear();
|
|
_instanceLiquidCount = 0;
|
|
_instanceMaterial.SetFloat(LiquidAmountHash, 0f);
|
|
SetCurrentAmount(0f);
|
|
HidePanel();
|
|
}
|
|
|
|
public void HandleBarrelInteraction(Barrel barrel)
|
|
{
|
|
_currentBarrel = barrel;
|
|
if (_instanceLiquidCount == 0)
|
|
{
|
|
ShowPanelFast();
|
|
_shaker.SetActive(true);
|
|
_amountText.enabled = true;
|
|
_completeCocktailImage.enabled = false;
|
|
_completeText.enabled = false;
|
|
_currentMixedColor = barrel.GetLiquidData().Color;
|
|
_instanceMaterial.SetColor(LiquidColorHash, _currentMixedColor * _colorIntensity);
|
|
EventManager.OnCocktailStarted?.Invoke();
|
|
}
|
|
|
|
_startTime = Time.time;
|
|
_isPouring = true;
|
|
}
|
|
|
|
public void HandleBarrelCancelInteraction()
|
|
{
|
|
_isPouring = false;
|
|
}
|
|
|
|
// public void ActiveIsPouring(Barrel barrel)
|
|
// {
|
|
// _currentBarrel = barrel;
|
|
// if (_instanceLiquidCount == 0)
|
|
// {
|
|
// ShowPanelFast();
|
|
// _shaker.SetActive(true);
|
|
// _amountText.enabled = true;
|
|
// _completeCocktailImage.enabled = false;
|
|
// _completeText.enabled = false;
|
|
// _currentMixedColor = _currentBarrel.GetLiquidData().Color;
|
|
// _instanceMaterial.SetColor(LiquidColorHash, _currentMixedColor * _colorIntensity);
|
|
// EventManager.OnCocktailStarted?.Invoke();
|
|
// }
|
|
//
|
|
// _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 IEnumerator CompleteCocktail()
|
|
{
|
|
HandleBarrelCancelInteraction();
|
|
|
|
yield return new WaitUntil(() => _currentLiquidAmount >= _maxLiquidCount);
|
|
|
|
var currentCocktailIngredients = new List<CocktailIngredient>(7);
|
|
foreach (var element in _liquidDataCounts)
|
|
{
|
|
var idx = element.Key.Idx;
|
|
var amount = element.Value;
|
|
currentCocktailIngredients.Add(new CocktailIngredient(idx, amount));
|
|
}
|
|
|
|
// ItemManager를 통해 모든 CocktailData 가져오기
|
|
var cocktailDatas = ItemManager.Instance.CocktailDataSo.GetData();
|
|
CocktailData matchingCocktail = null;
|
|
|
|
// 모든 칵테일 데이터를 순회하면서 조건에 맞는 칵테일 찾기
|
|
foreach (var cocktailData in cocktailDatas.Values)
|
|
{
|
|
var validIngredients = cocktailData.GetValidIngredients();
|
|
|
|
// 조건 1: 재료 개수 동일 체크
|
|
if (validIngredients.Count != currentCocktailIngredients.Count)
|
|
continue;
|
|
|
|
var allIngredientsMatch = true;
|
|
|
|
// 현재 음료 재료를 하나씩 validIngredients와 비교
|
|
foreach (var currentIngredient in currentCocktailIngredients)
|
|
{
|
|
// 동일한 Idx를 가진 재료가 있는지 찾음
|
|
var matchingValidIngredient = validIngredients.FirstOrDefault(ingredient => ingredient.Idx == currentIngredient.Idx);
|
|
|
|
// 만약 Idx가 일치하는 재료가 없으면 조건 불충족
|
|
if (matchingValidIngredient == null)
|
|
{
|
|
allIngredientsMatch = false;
|
|
break;
|
|
}
|
|
|
|
// 조건 2: Amount 값이 RatioRange에 따른 오차 범위 내에 있는지 체크
|
|
var validAmount = matchingValidIngredient.Amount;
|
|
//var maxLiquidCount = cocktailData.GetCocktailAmount(validIngredients);
|
|
var range = _maxLiquidCount / 100 * cocktailData.RatioRange;
|
|
var minAmount = validAmount - range;
|
|
var maxAmount = validAmount + range;
|
|
|
|
if (currentIngredient.Amount < minAmount || currentIngredient.Amount > maxAmount)
|
|
{
|
|
allIngredientsMatch = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 조건이 모두 만족하면 매칭되는 칵테일을 찾음
|
|
if (allIngredientsMatch)
|
|
{
|
|
matchingCocktail = cocktailData;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 조건에 만족하는 칵테일이 없음
|
|
if (matchingCocktail == null)
|
|
{
|
|
matchingCocktail = ItemManager.Instance.CocktailDataSo.GetDataByIdx("Cocktail000");
|
|
_completeText.text = "실패";
|
|
}
|
|
else
|
|
{
|
|
_completeText.text = $"성공!\n{matchingCocktail.Name}";
|
|
}
|
|
|
|
// TODO : 음료 제조 성공, 실패 연출 추가
|
|
_shaker.SetActive(false);
|
|
_amountText.enabled = false;
|
|
_completeCocktailImage.sprite = matchingCocktail.Sprite;
|
|
_completeCocktailImage.enabled = true;
|
|
_completeText.enabled = true;
|
|
|
|
// 1. 플레이어 음료 들기
|
|
EventManager.OnCocktailCompleted?.Invoke(matchingCocktail);
|
|
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
HidePanel();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 사용된 색상의 비율에 맞게 색을 혼합시키는 함수
|
|
/// </summary>
|
|
private Color MixColorsByTime()
|
|
{
|
|
var totalCounts = _liquidDataCounts.Values.Sum();
|
|
|
|
var mixedColor = Color.black;
|
|
|
|
foreach (var element in _liquidDataCounts)
|
|
{
|
|
var color = element.Key.Color;
|
|
var count = element.Value;
|
|
var ratio = count / (float)totalCounts;
|
|
|
|
mixedColor += color * ratio;
|
|
}
|
|
|
|
mixedColor.a = 1f;
|
|
|
|
return mixedColor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 액체가 특정 오브젝트에 충돌했을 때, 실행해야하는 과정
|
|
/// </summary>
|
|
public void OnTargetReached()
|
|
{
|
|
_liquidReachedTime = Time.time;
|
|
SetCurrentAmount(++_currentLiquidAmount);
|
|
var liquidAmount = Mathf.Clamp(_currentLiquidAmount / _maxLiquidCount, 0f, 1f);
|
|
_instanceMaterial.SetFloat(LiquidAmountHash, liquidAmount);
|
|
_targetColor = MixColorsByTime();
|
|
|
|
if (liquidAmount >= 1f)
|
|
{
|
|
HandleBarrelCancelInteraction();
|
|
}
|
|
}
|
|
|
|
public void ShowPanelFast()
|
|
{
|
|
if (_isShowingPanel) return;
|
|
|
|
_liquidPanel.transform.position = endPosition;
|
|
_liquidPanel.SetActive(true);
|
|
_isShowingPanel = true;
|
|
_hideTween.Pause();
|
|
_showTween.Pause();
|
|
}
|
|
|
|
public void ShowPanel()
|
|
{
|
|
if (_isShowingPanel || _instanceLiquidCount <= 0) return;
|
|
|
|
_isShowingPanel = true;
|
|
_hideTween.Pause();
|
|
_showTween.Restart();
|
|
}
|
|
|
|
public void HidePanel()
|
|
{
|
|
if (!_isShowingPanel) return;
|
|
|
|
_isShowingPanel = false;
|
|
_showTween.Pause();
|
|
_hideTween.Restart();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |