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

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