Unity音量滑块沿弧形移动

news2024/11/27 2:46:31

一、音量滑块的移动

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
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/460540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JMeter 获取登录接口的token

1、登录接口为POST请求方式&#xff0c;添加请求登录接口的消息体数据 添加HTTP信息头管理器&#xff0c;配置content-type值为application/json 2、给登录接口“添加监听器-查看结果树”和“后置处理器-正则表达式处理器” 先运行一次登录接口&#xff0c;通过查看结果树返回内…

C++三大特性—继承 “访问控制”

本文主要阐述关于C继承中基类与派生类之间的访问关系 继承方式与访问方式 继承定义格式&#xff1a; 派生类可以继承定义在基类的成员&#xff0c;但是派生类的成员函数不一定有权访问从基类继承来的成员    访问限定符的作用&#xff1a;控制派生类从基类继承而来的成员是否…

Matlab——逻辑回归(原理、代码)

对于一个机器学习方法&#xff0c;通常由模型、策略和算法3个要素构成。 模型是假设空间的形式&#xff0c;如是线性函数还是条件概率&#xff1b;策略是判断模型好坏的数学表达式&#xff0c;将学习问题转化为优化问题&#xff0c;一般策略对应一个代价函数&#xff08;Cost F…

SQL优化(2):主键优化

在上一小节&#xff0c;我们提到&#xff0c;主键顺序插入的性能是要高于乱序插入的。 这一小节&#xff0c;就来介绍一下具体的 原因&#xff0c;然后再分析一下主键又该如何设计。 1 数据组织方式 在InnoDB存储引擎中&#xff0c;表数据都是根据主键顺序组织存放的&#xf…

数据结构——求二叉树的属性

数据结构——求二叉树的属性 一、对称性101. 对称二叉树1.递归2.迭代3.同类题&#xff1a; 二、深度104. 二叉树的最大深度1.递归1&#xff09;后序1&#xff09;前序 2.迭代&#xff08;层序&#xff09; 559. N 叉树的最大深度1.递归&#xff08;深度优先&#xff09;2.迭代&…

MATLAB实现OCR识别数字和字符

OCR也叫做光学字符识别&#xff0c;是计算机视觉研究领域的分支之一。它是利用光学技术和计算机技术把印在或写在纸上的文字读取出来&#xff0c;并转换成一种计算机能够接受、人又可以理解的格式。 MATLAB实现OCR识别数字和字符&#xff0c;涉及灰度转换、中值滤波、二值化处…

【教学类-34-05】拼图(彩色图片+凹凸拼)3*4格子(中班主题《个别化拼图》偏美术)

图片展示&#xff1a; 背景需求&#xff1a; 最近班级孩子得了传染病&#xff0c;来了2位孩子。 我觉得&#xff1a;人少的话&#xff0c;孩子们就有充足的时间去拼那些带有凹凸槽的自制彩色图形拼图。 难点&#xff1a; 1、从直线剪切&#xff08;方形拼图&#xff09;转…

[JAVA编] 一编让你搞定多态

目录 1. 多态概念 2. 多态的体现和实现条件 3. 重写 4. 引用类型转换 4.1向上转型 4.2向下转型 5. 多态的好处 1.多态的概念 什么是多态? 多态是继封装, 继承之后, 面向对象的三大特性 在生活中,比如跑的动作,猫,狗和大象,跑起来都不一样.再比如飞…

【cmd命令】MySQL服务器无法启动

winR 输入services.msc 我发现我电脑上有两个mysql名&#xff0c;上面一个&#xff08;MySQL&#xff09;处于停止运行状态 下面一个&#xff08;MySQL)处于运行状态 如果要使用上面一个的服务器&#xff0c;就要把下面一个的服务器关闭&#xff0c;然后启动上面的服务器…

elastic-job 搭建——应用于企业级项目

1. &#x1f4c2; 技术方案 方案介绍 ElasticJob 是面向互联网生态和海量任务的分布式调度解决方案。 它通过弹性调度、资源管控、以及作业治理的功能&#xff0c;打造一个适用于互联网场景的分布式调度解决方案&#xff0c;并通过开放的架构设计&#xff0c;提供多元化的作业…

你知道渲染农场是什么原理吗?它是如何工作的?

我们知道&#xff0c;仅靠一台计算机几乎是不能达到专业渲染集群的处理能力的。所以现在&#xff0c; 允许将很多台计算机或是处理器进行连接&#xff0c;再将连接后的机器作为一个总平台来处理不同的渲染需求&#xff0c;这样的设置&#xff0c;就被称之为渲染农场。 渲染农…

年轻人“赶烤”淄博,文旅业如何借势?

​&#xff08;图片来源于网络&#xff0c;侵删&#xff09; 文 | 螳螂观察 作者 | 易不二 从“更适合中国宝宝体质的TACO”在社交媒体爆火&#xff0c;到全国人民为之“赶烤”&#xff0c;淄博凭借独树一帜的烧烤文化&#xff0c;已经站上了文旅业回暖的潮头。 今年五一假期…

FPGA目前就业形势咋样?来听听业内工程师的看法

看到网上有一个问题很火&#xff1a;2023了&#xff0c;FPGA目前就业形势咋样?很多同学也对这个方向比较感兴趣&#xff0c;下面就来一起了解一下吧。 FPGA岗位有哪些&#xff1f; 从芯片设计流程来看&#xff0c;FPGA岗位可以分四类 产品开发期&#xff1a;FPGA系统架构师 …

基于FFmpeg倒放功能的实现-----命令行和API调用实现方法

来源:微信公众号「编程学习基地」 文章目录 FFmpeg API调用reverse滤镜实现视频倒放ffmpeg命令行实现方法FFmpeg 过滤器 调用API实现方法完整代码贴上运行FFmpeg API调用reverse滤镜实现视频倒放 ffmpeg命令行实现方法 ffmpeg -i bigbuckbunny_480x272.h265 -filter_comple…

vue页面内嵌iframe使用postMessage进行数据交互(postMessage跨域通信)

什么是postMessage postMessage是html5引入的API,它允许来自不同源的脚本采用异步方式进行有效的通信,可以实现跨文本文档,多窗口,跨域消息传递.多用于窗口间数据通信,这也使它成为跨域通信的一种有效的解决方案. vue父页面&#xff08;嵌入iframe的页面&#xff09; 在vue中…

【文心一言】文心一言最近这么火,它到底是什么

前言 文心一言&#xff08;英文名&#xff1a;ERNIE Bot&#xff09;是百度全新一代知识增强大语言模型&#xff0c;文心大模型家族的新成员&#xff0c;能够与人对话互动&#xff0c;回答问题&#xff0c;协助创作&#xff0c;高效便捷地帮助人们获取信息、知识和灵感。文心一…

网工容易混淆的三种网线类型:直连线、交叉线和反转线

网线是计算机网络中最常见的传输介质之一&#xff0c;它能够将数据快速而可靠地传输到各个网络设备中。在实际的网络应用中&#xff0c;我们常常需要使用到不同类型的网线&#xff0c;包括直连线、交叉线和反转线。本文将介绍这三种网线的定义、应用场景和注意事项。 直连线 …

Tailwind CSS入门(二)——基本介绍和特性

上一篇文章简要的介绍了原子类CSS&#xff0c;以及个人对语义化、原子化的一些经验和理解。从这篇文章开始&#xff0c;正式开始分享Tailwind CSS的特性、使用和技巧。 Tailwind CSS是一个为快速开发而精心设计的原子类CSS框架&#xff0c;在此我们将搭建一个Vite项目来配合讲…

力扣---LeetCode21. 合并两个有序链表(链表经典题)

文章目录 前言21. 合并两个有序链表链接&#xff1a;方法一&#xff1a;取小尾插1.1代码&#xff1a;1.2 流程图&#xff1a;1.3 注意&#xff1a; 方法二&#xff1a;带哨兵位2.1代码&#xff1a;2.2流程图&#xff1a; 总结 前言 焦虑不会消除明天的悲伤 只会让你今天的力量…

springboot概述

脚手架: 因为创建的为web项目&#xff0c;有这两个文件夹 在idea中也可以使用脚手架 会直接或间接包含依赖 启动类 单一模块: 启动类要放在根包下边&#xff0c;其他的业务放在根包或者根包的子包 多个模块: restController包含controller且每个方法都包含responseBody注解&…