Unity volume slider moves along an arc

1. Movement of the volume slider

1. When the slider is sliding, its motion trajectory is extended along the arc edge of the big circle.
2. The slider cannot slide infinitely, but has a stopper at each end. When it moves to the stopper position, it cannot move down, but Can be turned back
3. When the mouse hovers over the slider, the volume value and operation prompts are given
4. When the slider is moved, the volume value is always prompted. After stopping the movement, the volume value disappears
Please add a picture description

2. UI implementation

As shown in the figure below:
1. The movement of the slider is realized by rotating around the center of the big circle, so when sliding, the big circle is rotating, and the slider is a sub-object of the big circle. 2. The
movement of the slider is realized by moving the mouse left and right
3. The volume value is obtained by calculating the included angle
insert image description here

3. Ideas

1. What volume value does the current slider position represent eg[50%]

Calculation of the movable angle range of slider A: angle BOC
The angle calculation of the current position of slider A: angle BOA
volume = angle BOA / angle BOC

How to calculate the angle?


    /// <summary>
    /// 计算两条射线之间的夹角(AB,AC -> ∠BAC)
    /// </summary>
    /// <param name="A">原点</param>
    /// <param name="B">位置1</param>
    /// <param name="C">位置2</param>
    /// <returns>夹角(以度为单位)</returns>
    public static float GetClamAngle(Vector3 A, Vector3 B, Vector3 C)
    {
    
    
        // 计算向量 AB
        Vector3 AB = B - A;
        // 计算向量 AC
        Vector3 AC = C - A;
        // 计算 AB 和 AC 之间的夹角(以度为单位)
        float angle = Vector3.Angle(AB, AC);
        // 返回夹角
        return angle;
    }

Calculate the angle range value that can be slid

//计算滑块运行区间的总的角度范围
allAngle = GetClamAngle(O.transform.position, B.transform.position,
            C.transform.position);

insert image description here

2. When the mouse drags the slider left and right, the slider will rotate along the great circle

Acquisition of mouse x component

PointerEventData .delta.x

Realization of dragging and rotating

//拖拽中:
ObjectHandle.GetComponent<EventTrigger>().AddListener(EventTriggerType.Drag, (PointerEventData eventData) =>
{
    
    
    float direction = Mathf.Sign(eventData.delta.x);

    Quaternion rotation = Quaternion.AngleAxis(direction * rotateSpeed, Vector3.forward);
    ObjectToRotate.transform.rotation *= rotation;
});

Realization of the limit
Left upper limit: when the slider Ax <= Cx, you can no longer turn left
Lower right limit: When the slider Ay <= By, you can no longer turn down
insert image description here

//极限位控制
if (ObjectHandle.transform.position.x <= leftBlock.transform.position.x)
{
    
    
    ObjectHandle.transform.position = leftBlock.transform.position;
}
if (ObjectHandle.transform.position.y <= rightBlock.transform.position.y)
{
    
    
    ObjectHandle.transform.position = rightBlock.transform.position;
}

4. Code

using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using static txlib;

/// <summary>
/// 音量控制:拖动bar进行进行滑动,左上位置为最高音量,右下位置为最低音量
/// </summary>
public class DragVolumBar : MonoBehaviour
{
    
    
    /// <summary>
    /// 要旋转的物体
    /// </summary>
    [Header("要旋转的物体")]
    [SerializeField]
    public GameObject ObjectToRotate;

    /// <summary>
    /// 控制旋转的bar
    /// </summary>
    [Header("控制旋转的bar")]
    [SerializeField]
    public GameObject ObjectHandle;

    /// <summary>
    /// 左侧挡板
    /// </summary>
    [Header("左侧挡板")]
    [SerializeField]
    public GameObject leftBlock;

    /// <summary>
    /// 右侧挡板
    /// </summary>
    [Header("右侧挡板")]
    [SerializeField]
    public GameObject rightBlock;

    /// <summary>
    /// 旋转的速度包含方向
    /// </summary>
    [Header("旋转的速度包含方向")]
    [SerializeField] public float rotateSpeed = 10f;

    /// <summary>
    /// 用于显示音量值的text
    /// </summary>
    [Header("用于显示音量值的text")]
    [SerializeField]
    public TMP_Text textVolume;

    /// <summary>
    /// 音量大小
    /// </summary>
    public static float volume;

    /// <summary>
    /// 滑块滑动时的角度区间范围
    /// </summary>
    private float allAngle;

    /// <summary>
    /// 计算两条射线之间的夹角(AB,AC -> ∠BAC)
    /// </summary>
    /// <param name="A">原点</param>
    /// <param name="B">位置1</param>
    /// <param name="C">位置2</param>
    /// <returns>夹角(以度为单位)</returns>
    public static float GetClamAngle(Vector3 A, Vector3 B, Vector3 C)
    {
    
    
        // 计算向量 AB
        Vector3 AB = B - A;
        // 计算向量 AC
        Vector3 AC = C - A;
        // 计算 AB 和 AC 之间的夹角(以度为单位)
        float angle = Vector3.Angle(AB, AC);
        // 返回夹角
        return angle;
    }

    // Start is called before the first frame update
    void Start()
    {
    
    
        textVolume.gameObject.SetActive(false);

        //计算滑块运行区间的总的角度范围
        allAngle = GetClamAngle(ObjectToRotate.transform.position, leftBlock.transform.position,
            rightBlock.transform.position);

        if (!ObjectHandle.GetComponent<EventTrigger>()) ObjectHandle.AddComponent<EventTrigger>();

        #region 音量滑块拖动
        //开始拖拽:
        ObjectHandle.GetComponent<EventTrigger>().AddListener(EventTriggerType.BeginDrag, async (PointerEventData eventData) =>
        {
    
    
            textVolume.gameObject.SetActive(true);
        });

        //拖拽中:
        ObjectHandle.GetComponent<EventTrigger>().AddListener(EventTriggerType.Drag, (PointerEventData eventData) =>
        {
    
    
            float direction = Mathf.Sign(eventData.delta.x);

            Quaternion rotation = Quaternion.AngleAxis(direction * rotateSpeed, Vector3.forward);
            ObjectToRotate.transform.rotation *= rotation;

            //极限位控制
            if (ObjectHandle.transform.position.x <= leftBlock.transform.position.x)
            {
    
    
                ObjectHandle.transform.position = leftBlock.transform.position;
            }
            if (ObjectHandle.transform.position.y <= rightBlock.transform.position.y)
            {
    
    
                ObjectHandle.transform.position = rightBlock.transform.position;
            }

            var angle = GetClamAngle(ObjectToRotate.transform.position, ObjectHandle.transform.position,rightBlock.transform.position);
            volume = angle / allAngle;
            Debug.Log($"总角度:{
      
      allAngle},当前角度:{
      
      angle} ,声音值:{
      
      (int)(100 * volume)}");
            textVolume.text = $"{
      
      (int)(100 * volume)}%";
        });

        //结束拖拽:延缓隐藏音量值
        ObjectHandle.GetComponent<EventTrigger>().AddListener(EventTriggerType.EndDrag, async (PointerEventData eventData) =>
        {
    
    
            await UniTask.Delay(TimeSpan.FromSeconds(0.2f),cancellationToken:this.GetCancellationTokenOnDestroy());
            textVolume.gameObject.SetActive(false);
        });
        #endregion

        #region 音量滑块鼠标悬停时,显示音量
        ObjectHandle.GetComponent<EventTrigger>().AddListener(EventTriggerType.PointerEnter, async (PointerEventData eventData) =>
        {
    
    
            textVolume.gameObject.SetActive(true);
            var angle = GetClamAngle(ObjectToRotate.transform.position, ObjectHandle.transform.position, rightBlock.transform.position);
            volume = angle / allAngle;
            textVolume.text = $"{
      
      (int)(100 * volume)}% <color=blue>鼠标左右拖拽滑块来调节音量</color>";
        });
        #endregion
    }
}

Guess you like

Origin blog.csdn.net/dzj2021/article/details/130362734
arc