一、音量滑块的移动
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)
{
// 计算向量 AB
Vector3 AB = B - A;
// 计算向量 AC
Vector3 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)
{
// 计算向量 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
}
}