【Unity通用組件】抽獎轉盤組件

     通用的轉盤實現,包括對抽到後轉盤中獎品背景,文字修改。調用OnStartSpin()進行開始,此例子爲測試,所以是完全在所有獎品中隨機停止,使用時可根據情況傳入EndAngle信息進行最終的停止位置設置;

     角度修正參數:因爲轉盤獎品份數不同,最終指針指向位置可能有偏差,此參數進行調整最終指向獎品正中心。

     

using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class SpinWheelCom : MonoBehaviour
{
    public enum SpinWheelStateType
    {
        None, //待機狀態
        Start, //加速階段
        Prepared, //等待數據階段
        End, //減速階段
    }
    [Header("最大速度")]
    public int Velocity = 2500;
    [Header("轉動轉盤對象")]
    public Transform NodeTran;
    [Header("轉動總時間")]
    public float TotalTime = 3f;
    [Header("減速前最短持續時間")]
    public float MinTime = 3.0f;

    [Header("轉到該獎勵時背景")]
    public Sprite SelectAwardAngleBg;
    [Header("正常靜止時獎品文字顏色")]
    public Color awardTextNormalColor;
    [Header("轉到該獎品時文字顏色")]
    public Color SelectAwardTextColor;
    [Header("所有獎品的獎品文字")]
    public TextMeshProUGUI[] AwardItemTexts;
    [Header("所有獎品背景")]
    public Image[] AwardItemBgs;
    [Header("角度修正")]
    public int ModifiedAngle = 280;
    
    //加速持續時間
    private readonly float AcceleateTime = 1f;
    private SpinWheelStateType _spinWheelStateType;

    private float _endAngle = 0f;
    public float EndAngle
    {
        get
        {
            return _endAngle;
        }
        set
        {
            _endAngle = Mathf.Abs(value);
            _endAngle = _endAngle % 360; //將角度限定在[0, 360]這個區間
            _endAngle = ModifiedAngle - _endAngle - 360 * 2; //多N圈並取反,圈數能使減速階段變得更長,顯示更自然,逼真
        }
    }
    //角度緩存
    private float _tmpAngle = 0f;
    // 時間統計
    private float _time;
    // 速度變化因子
    private float _factor;
    //被抽中的獎品位置
    private int _selectAwardIndex;

    public UnityAction EndSpinHandler;
    void Awake()
    {
        _spinWheelStateType = SpinWheelStateType.None;
    }

    void Update()
    {
        if (_spinWheelStateType == SpinWheelStateType.None)
        {
            return;
        }

        _time += Time.deltaTime;
        if (_spinWheelStateType == SpinWheelStateType.End) //減速
        {
            //通過差值運算實現精準地旋轉到指定角度(球型插值無法實現大於360°的計算)
            float k = 2f; //如果嫌減速太慢,可以加個係數修正一下
            _tmpAngle = Mathf.Lerp(_tmpAngle, EndAngle, Time.deltaTime * k);

            //這裏只存在一個方向的旋轉,所以不存在歐拉角萬向節的問題,所以使用歐拉角和四元數直接賦值都是可以的
            NodeTran.eulerAngles = new Vector3(0, 0, _tmpAngle);

            if (0.1f >= Mathf.Abs(_tmpAngle - EndAngle))
            {
                NodeTran.eulerAngles = new Vector3(0, 0, EndAngle);

                _spinWheelStateType = SpinWheelStateType.None;

                //設置對應獎勵背景白色,然後彈出獎勵彈窗
                StopSpin();
            }
        }
        else //加速
        {
            //利用一個速度因子實現變加速的過程
            _factor = _time / AcceleateTime;
            _factor = _factor > 1 ? 1 : _factor;
            NodeTran.Rotate(Vector3.back, _factor * Velocity * Time.deltaTime, Space.Self);
        }

        //當收到數據之後並且旋轉了一定時間後開始減速
        if (_spinWheelStateType == SpinWheelStateType.Prepared && _time > MinTime)
        {
            //TODO播放聲音
            _spinWheelStateType = SpinWheelStateType.End;
            _tmpAngle = GetCurClockwiseAngle();
        }
    }
    /// <summary>
    /// 將當前指針的歐拉角轉換成順時針統計角度
    /// </summary>
    /// <returns></returns>
    private float GetCurClockwiseAngle()
    {
        //由於讀取到的值是[0, 180] U [-180, 0],左邊由0至180遞增,右邊由180轉變成-180,然後遞增至0,所以需要轉相應的轉換
        return (-1) * (360 - NodeTran.eulerAngles.z) % 360;
    }

    /// <summary>
    /// 開始轉動 //傳入獎品信息(帶停止角度)
    /// </summary>
    public void OnStartSpin()
    {
        if(_spinWheelStateType == SpinWheelStateType.None)
        {
            //Test 隨機停止
            int index = UnityEngine.Random.Range(0, AwardItemBgs.Length);
            float angle = index * AwardItemBgs.Length / 360f;
            //

            EndAngle = angle;
            _spinWheelStateType = SpinWheelStateType.Start;

            //Total後自動停止
            StartCoroutine(CountDown(TotalTime, () =>
            {
                _spinWheelStateType = SpinWheelStateType.Prepared;
            }));
        }
    }
    /// <summary>
    /// 停止轉動
    /// </summary>
    private void StopSpin()
    {
        ChangeSelectedAwardBg(_selectAwardIndex);

        //TODO 處理中獎
    }

    public void ChangeSelectedAwardBg(int index)
    {
        if (AwardItemBgs.Length > 0 && index < AwardItemBgs.Length)
        {
            Sprite lastSprite = AwardItemBgs[index].sprite;
            AwardItemBgs[index].sprite = SelectAwardAngleBg;

            AwardItemTexts[index].color = SelectAwardTextColor;

            //1.5s後自動停止
            StartCoroutine(CountDown(1.5f, () =>
            {
                AwardItemBgs[index].sprite = lastSprite;
                AwardItemTexts[index].color = awardTextNormalColor;
            }));
        }
    }
    
    IEnumerator CountDown(float time,UnityAction action)
    {
        yield return new WaitForSeconds(time);
        action?.Invoke();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章