Unity3D轮转图

在我们制作一些场景选关或者是做一些选择类似的UI时,我们会觉得想制作一种3D效果的选择方式,3D轮转图往往就是最恰当的一种,下面我将会介绍3D轮转图的具体原理及实现:

一、效果演示

这里是一个简单的3D轮转图效果,实现了物体滑动,自动对齐,惯性,按钮点击选中

效果转速比较快,可能看不太清

效果演示​​​

二、轮转图实现思路及操作

我们要想实现3D轮转图,就要先确认实现的思路,首先,3D轮转图其实就是多个物体围绕着圆心摆放,然后我们滑动物体的时候,去改变3D轮转图下子物体的位置(localPosition)。

我们先在场景中创建一个空对象,然后创建一个名为Cyclogram的轮转图的脚本,将脚本挂载到空对象(图1),在场景中创建一个Cube,把Cube制成预制体(图2)

图1

图2

Cyclogram脚本如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class Cyclogram : MonoBehaviour
{

    //轮转图物体预制体
    public GameObject prefab;
    //轮转图物体个数
    public int num = 5;
    //轮转图半径
    public int r = 5;
    public float cutspeed = 10;//减速度
    public float moveAng = 0;//移动后的弧度
    //存放轮转图物体的集合
    List<GameObject> list = new List<GameObject>();
    //存放轮转图物体位置的集合
    List<GameObject> sorts = new List<GameObject>();
    //轮转图滑动一次的弧度
    float ang;
    Tweener tweener;
    // Start is called before the first frame update
    void Start()
    {
        //求出对应item数下的每一份的角度
        ang = 2 * Mathf.PI / num;
        OnMove();
    }
    public void OnMove()
    {
        for (int i = 0; i < num; i++)
        {
            //计算顶点位置
            //求出角度对应的X,Z轴
            float x = Mathf.Sin(ang * i + moveAng) * r;
            float z = Mathf.Cos(ang * i + moveAng) * r;
            //如果集合数小于item份数,就生成预制体
            if (list.Count<=i)
            {
                GameObject cell = Instantiate(prefab, transform);
                list.Add(cell);
                sorts.Add(cell);
            }
            //item位置
            list[i].transform.localPosition = new Vector3(x, 0, z);
        }
    }
    //滑动方法
    public void OnDrag(float dis)
    {
        tweener.Kill();
        //拖拽时移动的角度等于距离除以半径
        float dragAng = dis / r;
        //总角度减去对应移动的角度
        moveAng -= dragAng;
        OnMove();
    }
    //实现惯性方法
    public void Inertia(float dis)
    {
        //时间需要取正数,距离除去减速度就是时间
        float time = Mathf.Abs(dis / cutspeed);
        tweener=DOTween.To((a) =>
        {
            float dragAng = dis / r;
            moveAng -= dragAng;
            OnMove();
        }, dis, 0, time).OnComplete(()=>
        {
            //找的z最小的
            //当dotween结束时先将集合位置排序,确认item位置的z轴和圆心的距离,取最小值
            sorts.Sort((a, b) =>
            {
                if (a.transform.localPosition.z<b.transform.localPosition.z)
                {
                    return -1;
                }
                else if(a.transform.localPosition.z==b.transform.localPosition.z)
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            });
            //求出调整的位置和时间
            float alignAng = Mathf.Asin(sorts[0].transform.localPosition.x / r);
            float alignDis = alignAng * r;
            float alignTime = Mathf.Abs(alignDis / cutspeed);
            DOTween.To((b) =>
            {
                //通过dotween缓动效果将物体位置调整
                moveAng = b;
                OnMove();
            },moveAng,moveAng+alignAng,alignTime);
        });
    }

    public void Align(int n)
    {
        sorts.Sort((a, b) =>
        {
            if (a.transform.localPosition.z < b.transform.localPosition.z)
            {
                return -1;
            }
            else if (a.transform.localPosition.z == b.transform.localPosition.z)
            {
                return 0;
            }
            else
            {
                return 1;
            }
        });
        //找到z最小的在list里的下标
        int id = list.IndexOf(sorts[0]);
        int zheng = id - n;//正
        //反方向长度=总长度-正方向长度
        int fan = num - Mathf.Abs(zheng);//反
        //反方向正负一定与正方向相反
        fan = zheng < 0 ? fan : -fan;
        //取正反方向距离短的一个
        int interval = Mathf.Abs(zheng) < Mathf.Abs(fan) ? zheng : fan;
        float intervalAng = interval * ang;
        float alignAng = Mathf.Asin(sorts[0].transform.localPosition.x / r)+intervalAng;
        float alignDis = alignAng * r;
        float alignTime = Mathf.Abs(alignDis / cutspeed);
        DOTween.To((b) =>
        {
            moveAng = b;
            OnMove();
        }, moveAng, moveAng + alignAng, alignTime);

    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

以上代码通过简单计算获得轮转图的半径,周长,每份的弧度等数据。

注(这里在实现效果时用了DOTween效果,我用的是插件,可以手写)

这里需要再创建一个预制体脚本,实现滑动效果,创建一个名为MoveCell的脚本挂载到之前创建好的预制体上

MoveCell代码如下:

//鼠标滑动
    private void OnMouseDrag()
    {
        //世界坐标转屏幕坐标,将目标位置提取出来
        Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
        Vector3 mouse = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pos.z));
        //将鼠标拖动后的距离传给对应的拖动函数
        float dis = mouse.x - transform.position.x;
        Debug.Log(dis);
        transform.parent.GetComponent<Cyclogram>().OnDrag(dis);
    }
    //鼠标松开
    private void OnMouseUp()
    {
        Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
        Vector3 mouse = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pos.z));
        float dis = mouse.x - transform.position.x;
        Debug.Log(dis);
        transform.parent.GetComponent<Cyclogram>().Inertia(dis);
    }

 这样,一个有对齐效果和惯性效果的3D轮转图效果就实现好了。

这些代码都只是实现了基本的功能,还有很大的不足,大家参考一下就好

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值