Files
M-Gameton-06/Gameton-06/Assets/Gameton/Scripts/UI/RouletteSpin/Roulette.cs
2025-07-03 18:54:16 +09:00

137 lines
5.4 KiB
C#

using UnityEngine;
using UnityEngine.Events;
using System.Collections;
using UniRx;
using System.Collections.Generic;
namespace TON
{
// View
public class Roulette : MonoBehaviour
{
[SerializeField] private Transform piecePrefab; // 룰렛에 표시되는 정보 프리팹
[SerializeField] private Transform linePrefab; // 정보들을 구분하는 선 프리팹
[SerializeField] private Transform pieceParent; // 정보들이 배치되는 부모 Transform
[SerializeField] private Transform lineParent; // 선들이 배치되는 부모 Transform
[SerializeField] public RoulettePieceData[] roulettePieceData;
[SerializeField] private int spinDuration; // 회전 시간
[SerializeField] private Transform spinningRoulette; // 실제 회전하는 회전판 Transfrom
[SerializeField] private AnimationCurve spinningCurve; // 회전 속도 제어를 위한 그래프
private float pieceAngle; // 정보 하나가 배치되는 각도
private float halfPieceAngle; // 정보 하나가 배치되는 각도의 절반 크기
private float halfPieceAngleWithPaddings; // 선의 굴기를 고려한 padding이 포함된 절반 크기
private int accumulatedWeight; // 가중치 계산을 위한 변수
private bool isSpinning = false; // 현재 회전중인지
private int selectedIndex = 0; // 룰렛에서 선택된 아이템
private RoulettePresenter presenter;
private List<RoulettePiece> pieceUIs = new();
private void Awake()
{
pieceAngle = 360 / roulettePieceData.Length;
halfPieceAngle = pieceAngle * 0.5f;
halfPieceAngleWithPaddings = halfPieceAngle - (halfPieceAngle * 0.25f);
SpawnPiecesAndLines();
CalculateWeightsAndIndices();
}
private void SpawnPiecesAndLines()
{
for (int i = 0; i < presenter.RoulettePieces.Count; ++i)
{
Transform piece = Instantiate(piecePrefab, pieceParent.position, Quaternion.identity, pieceParent);
var pieceUI = piece.GetComponent<RoulettePiece>();
pieceUI.Setup(presenter.RoulettePieces[i].Model);
piece.RotateAround(pieceParent.position, Vector3.back, (pieceAngle * i));
pieceUIs.Add(pieceUI);
Transform line = Instantiate(linePrefab, lineParent.position, Quaternion.identity, lineParent);
line.RotateAround(lineParent.position, Vector3.back, (pieceAngle * i) + halfPieceAngle);
}
}
private void CalculateWeightsAndIndices()
{
for (int i = 0; i < roulettePieceData.Length; ++i)
{
roulettePieceData[i].index = i;
// 예외 처리, 혹시라도 chance값이 0 이하면 1로 설정
if (roulettePieceData[i].chance <= 0)
{
roulettePieceData[i].chance = 1;
}
accumulatedWeight += roulettePieceData[i].chance;
roulettePieceData[i].weight = accumulatedWeight;
Debug.Log($"({roulettePieceData[i].index}) {roulettePieceData[i].description}:{roulettePieceData[i].weight}");
}
}
private int GetRandomIndex()
{
int weight = Random.Range(0, accumulatedWeight);
for (int i = 0; i < roulettePieceData.Length; ++i)
{
if (roulettePieceData[i].weight > weight)
{
return i;
}
}
return 0;
}
public void Bind(RoulettePresenter presenter)
{
this.presenter = presenter;
pieceAngle = 360f / roulettePieceData.Length;
halfPieceAngle = pieceAngle * 0.5f;
halfPieceAngleWithPaddings = halfPieceAngle - (halfPieceAngle * 0.25f);
SpawnPiecesAndLines();
}
public void Spin(System.Action<RoulettePiecePresenter> onEnd)
{
if (presenter.IsSpinning.Value) return;
presenter.Spin(selectedPresenter =>
{
int selectedIndex = selectedPresenter.Index.Value;
float angle = pieceAngle * selectedIndex;
float leftOffset = (angle - halfPieceAngleWithPaddings) % 360;
float rightOffset = (angle + halfPieceAngleWithPaddings) % 360;
float randomAngle = Random.Range(leftOffset, rightOffset);
int rotateSpeed = 2;
float targetangle = (randomAngle + 360 * spinDuration * rotateSpeed);
StartCoroutine(OnSpin(targetangle, () =>
{
presenter.IsSpinning.Value = false;
onEnd?.Invoke(selectedPresenter);
}));
});
}
private System.Collections.IEnumerator OnSpin(float end, System.Action onEnd)
{
float current = 0;
float percent = 0;
while (percent < 1)
{
current = Time.deltaTime;
percent += current / spinDuration;
float z = Mathf.Lerp(0, end, spinningCurve.Evaluate(percent));
spinningRoulette.rotation = Quaternion.Euler(0, 0, z);
yield return null;
}
onEnd?.Invoke();
}
}
}