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 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(); 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}"); } } 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 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 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(); } } }