一、音量滑块的移动
1、滑块在滑动的时候,其运动轨迹沿着大圆的弧边展开
2、滑块不能无限滑动,而是两端各有一个挡块,移动到挡块位置,则不能往下移动,但可以折回
3、鼠标悬停滑块时,给出音量值和操作提示
4、移动滑块的时候,始终提示音量的值,停止移动后,音量值消失
二、UI实现
如下图所示:
1、滑块的移动是围绕大圆的圆心旋转实现的,所以滑动的时候,是大圆在旋转,滑块是大圆的子物体
2、滑块移动是通过鼠标左右移动来实现的
3、音量值是通过夹角计算来获取的
三、思路
1、当前滑块位置代表多大的音量值 e.g.[50%]
滑块A可移动的角度范围计算:角度BOC
滑块A当前位置的角度计算:角度BOA
volume = 角度BOA / 角度BOC
如何计算角度?
/// <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){// 计算向量 ABVector3 AB = B - A;// 计算向量 ACVector3 AC = C - A;// 计算 AB 和 AC 之间的夹角(以度为单位)float angle = Vector3.Angle(AB, AC);// 返回夹角return angle;}
计算可以滑动的角度范围值
//计算滑块运行区间的总的角度范围
allAngle = GetClamAngle(O.transform.position, B.transform.position,C.transform.position);
2、鼠标左右拖拽滑块的时候,滑块沿着大圆进行旋转
鼠标x分量的获取
PointerEventData .delta.x
拖拽旋转的实现
//拖拽中:
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;
});
限位的实现
左上限位:滑块A.x <= C.x的时候,不能再往左转
右下限位:滑块A.y <= B.y的时候,不能再往下转
//极限位控制
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;
}
四、代码
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){// 计算向量 ABVector3 AB = B - A;// 计算向量 ACVector3 AC = C - A;// 计算 AB 和 AC 之间的夹角(以度为单位)float angle = Vector3.Angle(AB, AC);// 返回夹角return angle;}// Start is called before the first frame updatevoid 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}
}