当前位置: 首页 > news >正文

Unity 带阻尼感的转盘

通过上下滑动,实现带自动回正带阻尼感的转盘,上图所示的正方向在9点钟方向,即正西方向

实现过程,找到一个1:1的方形图片,制作一个圆环,我这里是将圆环分成了14份,代码里可以调整,添加代码,手动调整动态参数即可。

直接上代码:

using UnityEngine;
using UnityEngine.EventSystems;

public class LotteryWheel : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
{
    [Header("转盘")]
    public RectTransform wheel;

    [Header("旋转参数")]
    [Tooltip("手势滑动灵敏度,值越大旋转越明显")]
    public float swipeSensitivity = 0.5f;
    [Tooltip("惯性减速速率(角度/秒²)")]
    public float deceleration = 500f;
    [Tooltip("回弹对齐的旋转速度(角度/秒)")]
    public float snapSpeed = 300f;

    [Header("分段设置")]
    public int segmentCount = 14; // 分成14份

    [Header("回正偏移设置")]
    [Tooltip("回正时的目标偏移角度,此处为-90°表示最终使分段中心与-90°对齐")]
    public float snapOffsetAngle = -90f;

    public AudioClip audioClip;

    // 内部状态
    private float currentVelocity = 0f; // 当前旋转速度(角度/秒)
    private bool isDragging = false;    // 是否处于拖拽手势中
    private bool isSnapping = false;    // 是否正在自动回弹对齐
    private Vector2 lastPointerPosition; // 记录上一次手指位置

    // 用于记录上一次经过的分段中心,防止重复打印日志
    private int lastLoggedSegment = -1;

    void Start()
    {
        // 根据转盘初始角度计算当前分段中心索引
        float currentAngle = wheel.eulerAngles.z;
        float segmentAngle = 360f / segmentCount;
        float relativeAngle = (currentAngle - snapOffsetAngle + 360f) % 360f;
        lastLoggedSegment = Mathf.RoundToInt(relativeAngle / segmentAngle) % segmentCount;
    }


    void Update()
    {
        // 非拖拽且未处于回弹对齐状态时,利用惯性旋转并逐渐减速
        if (!isDragging && !isSnapping)
        {
            if (Mathf.Abs(currentVelocity) > 0.01f)
            {
                float deltaAngle = currentVelocity * Time.deltaTime;
                wheel.Rotate(0, 0, deltaAngle);
                currentVelocity = Mathf.MoveTowards(currentVelocity, 0, deceleration * Time.deltaTime);

                // 当速度足够小时,进入回弹对齐状态
                if (Mathf.Abs(currentVelocity) < 0.1f)
                {
                    currentVelocity = 0;
                    isSnapping = true;
                }
            }
        }

        // 回弹对齐阶段:计算目标角度(基于偏移)
        if (isSnapping)
        {
            float currentAngle = wheel.eulerAngles.z;
            float segmentAngle = 360f / segmentCount;
            // 以 snapOffsetAngle 为参考,计算最近的分段中心角度
            float targetAngle = Mathf.Round((currentAngle - snapOffsetAngle) / segmentAngle) * segmentAngle + snapOffsetAngle;
            float newAngle = Mathf.MoveTowardsAngle(currentAngle, targetAngle, snapSpeed * Time.deltaTime);
            wheel.eulerAngles = new Vector3(0, 0, newAngle);

            if (Mathf.Abs(Mathf.DeltaAngle(newAngle, targetAngle)) < 0.1f)
            {
                isSnapping = false;
                // 计算当前分段编号,归一化取模,确保编号在0~segmentCount-1内
                int segmentIndex = Mathf.RoundToInt((targetAngle - snapOffsetAngle) / segmentAngle);
                segmentIndex = (segmentIndex % segmentCount + segmentCount) % segmentCount;
                int number = segmentIndex + 1;
                Debug.Log("当前编号: " + number);
            }
        }

        // Add detection
        SlidingAreaDetection();
    }

    // Add detection: Print logs when the turntable turns over a new segment center
    private void SlidingAreaDetection()
    {
        // 获取当前转盘角度(0~360°),计算相对于 snapOffsetAngle 的归一化角度
        float currentAngle = wheel.eulerAngles.z;
        float segmentAngle = 360f / segmentCount;
        float relativeAngle = (currentAngle - snapOffsetAngle + 360f) % 360f;
        // 通过四舍五入得到当前经过的分段中心索引
        int currentCenterIndex = Mathf.RoundToInt(relativeAngle / segmentAngle) % segmentCount;

        if (currentCenterIndex != lastLoggedSegment)
        {
            // 此处打印日志,后续可在此处接入音效播放
            //Debug.Log("经过分段中心, 当前分段: " + (currentCenterIndex + 1));
            if (audioClip != null && audioClip.length > 0)
            {
                GetComponent<AudioSource>().PlayOneShot(audioClip);
            }
            lastLoggedSegment = currentCenterIndex;
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        isDragging = true;
        isSnapping = false; // 中断回弹对齐
        lastPointerPosition = eventData.position;
        currentVelocity = 0;
    }

    public void OnDrag(PointerEventData eventData)
    {
        Vector2 pointerDelta = eventData.position - lastPointerPosition;
        // 根据手指垂直位移决定旋转方向:上滑(delta.y正)顺时针旋转,反之逆时针旋转
        float rotationDelta = -pointerDelta.y * swipeSensitivity;
        wheel.Rotate(0, 0, rotationDelta);
        currentVelocity = -pointerDelta.y * swipeSensitivity / Time.deltaTime;
        lastPointerPosition = eventData.position;
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        isDragging = false;
        if (Mathf.Abs(currentVelocity) < 10f)
        {
            currentVelocity = 0;
            isSnapping = true;
        }
    }
}

 有点像手机闹钟,设置时间时的丝滑感觉。

下面的代码是将所划分的区域段数实时的展示在Scene界面,可用可不用,需要自取

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShowRotaryWheel : MonoBehaviour
{
    public int sectorCount = 14; // 区域块数量
    
#if UNITY_EDITOR

    // 在类末尾添加以下方法:
    private void OnDrawGizmos()
    {

        if (sectorCount < 1) return;

        RectTransform rt = GetComponent<RectTransform>();
        if (!rt) return;

        float radius = rt.rect.width / 2 * rt.lossyScale.x;
        Vector3 center = rt.position;
        float sectionAngle = 360f / sectorCount;

        Gizmos.color = Color.yellow;

        for (int i = 0; i < sectorCount; i++)
        {
            float angle = i * sectionAngle;
            Vector3 direction = Quaternion.Euler(0, 0, angle) * Vector3.up;
            Gizmos.DrawLine(center, center + direction * radius);
        }
    }
#endif
}

如果有帮到你还望给个三连吧,感谢您的支持。

@Liam

相关文章:

  • 数智读书笔记系列015 探索思维黑箱:《心智社会:从细胞到人工智能,人类思维的优雅解读》读书笔记
  • Openlayer+天地图+山东天地图
  • Html5学习教程,从入门到精通, HTML5超链接应用的详细语法知识点和案例代码(18)
  • uni-app+vue3学习随笔
  • 深度学习PyTorch之数据加载DataLoader
  • KafkaRocketMQ
  • AI智能导航站HTML5自适应源码帝国cms7.5模板
  • word甲烷一键下标
  • 08 HarmonyOS NEXT 仿uv-ui Tag组件开发教程系列(二)
  • 【Java学习】包装类
  • 【商城实战(20)】商品管理功能深化实战
  • 【理想解法学习笔记】
  • 计算机操作系统
  • docker企业级事例部署phpmyadmin和MySQL
  • win10电脑鼠标速度突然变的很慢?
  • 【Go语言圣经1.1】
  • Linux 常用测试网络带宽命令
  • 八股打卡(七)
  • 密码学 网络安全 科普 网络安全密码技术
  • 【vllm】Qwen2.5-VL-72B-AWQ 部署记录
  • 杭州网站建设专家/营销技巧在线完整免费观看
  • 博物馆网站建设/网络营销软文范文
  • 金钟街网站建设/市场营销师报名官网
  • 邳州城乡建设局网站/怎么把产品推广到各大平台
  • java网站搭建教程/网络推广软文怎么写
  • 什么网站专做外贸/今日足球赛事数据