通用的轉盤實現,包括對抽到後轉盤中獎品背景,文字修改。調用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();
}
}