3D轮转图实现

3D轮转图主要有两种实现模式,第一种是以摄像机第三人称观察整个3D轮转图结构,

第二种是摄像机在轮转图的圆心观察3D轮转图,两者的区别主要在于当拖动鼠标时,第一种方式当鼠标向左划,轮转图做顺时针移动,第二种方式则作逆时针移动,其次就是摄像机所处位置不同

第一步,首先创建一个空物体,空物体作为生成的轮转图item项的父对象

第二步创建一个预制体,该预制体就是3D轮转图上的Item

接下来首先写轮转图的框架:

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class Cyclogram : MonoBehaviour
{
    //纹理数组
    public Texture[] textures;
    //预制体
    public GameObject prefab;
    //确定轮转图item的个数
    public int num;
    //轮转图半径
    public float r;
    //轮转图减速度
    public float dec = 5f;
    // Start is called before the first frame update
    //每一份的角度
    float ang;
    //预制体的集合
    List<GameObject> list = new List<GameObject>();
    //对应预制体的transform集合,主要是为了取位置
    List<Transform> sorts = new List<Transform>();
    void Start()
    {
        //求出对应item数下的每一份的角度
        ang = 2 * Mathf.PI / num;
        Move();
    }
    float allAng = 0;//默认总角度为0
    public void OnDrag(float dis)
    {

        //拖拽时移动的角度等于距离除以半径
        float moveAng = dis / r;
        //总角度减去对应移动的角度
        allAng -= moveAng;
        Move();
    }
    //迟缓效果
    public void Inertia(float dis)
    {
        //时间需要取正数,距离除去减速度就是时间
        float time = Mathf.Abs(dis / dec);
        //手写Dotween,主要参数时回调函数,起始位置,结束位置,时间
        DT.To((a) =>
        {
            OnDrag(a);
        }, dis, 0, time).OnComplete(() =>
        {
            //当dotween结束时先将集合位置排序,确认item位置的z轴和圆心的距离,取最小值
            sorts.Sort((a, b) =>
            {
                if (a.position.z < b.position.z)
                {
                    return -1;
                }
                else if (a.position.z == b.position.z)
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            });
            //求出调整的位置和时间
            float aligning = Mathf.Asin(sorts[0].position.x / r);
            float aligningTime = Mathf.Abs(aligning * r / dec);
            DT.To((a) =>
            {
                //通过dotween缓动效果将物体位置调整
                allAng = a;
                Move();
            }, aligning, allAng + aligning, aligningTime);
        });
    }
    private void Move()
    {
        for (int i = 0; i < num; i++)
        {
            //求出角度对应的X,Z轴
            float x = Mathf.Sin(ang * i + allAng) * r;
            float z = Mathf.Cos(ang * i + allAng) * r;
            //如果集合数小于item份数,就生成预制体
            if (list.Count <= i)
            {
                GameObject sphere = Instantiate(prefab);
                sphere.transform.parent = transform;
                sphere.GetComponent<Item>().cyclogram = this;
                sphere.GetComponent<MeshRenderer>().material.mainTexture = textures[i];
                list.Add(sphere);
                sorts.Add(sphere.transform);
            }
            //安排item位置
            list[i].transform.localPosition = new Vector3(x, 0, z);
            //当前的弧度转换为度
            list[i].transform.localEulerAngles = Vector3.up * ((i * ang + allAng) * Mathf.Rad2Deg);
        }
    }
    // Update is called once per frame
    void Update()
    {

    }
}

上述代码的主要作用是用来生成预制体,并且实现缓动和拖动效果,而具体效果的执行需要用到具体的预制体,预制体代码如下:

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

public class Item : MonoBehaviour
{
    public Cyclogram cyclogram;
    // Start is called before the first frame update
    void Start()
    {

    }

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

    }
    private void OnMouseDrag()
    {
        //世界坐标转屏幕坐标,将目标位置提取出来
        Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
        Vector3 next = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pos.z));
        //将鼠标拖动后的距离传给对应的拖动函数
        float dis = next.x - transform.position.x;
        //调用轮转图的拖拽方法
        cyclogram.OnDrag(dis);
    }
    private void OnMouseUp()//鼠标抬起时处理缓动效果
    {
        Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
        Vector3 next = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pos.z));
        float dis = next.x - transform.position.x;
        //调用轮转图的缓动方法
        cyclogram.Inertia(dis);
    }
}

最后就是缓动效果中调用的手写Dotween了,代码如下:

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

public class DT : MonoBehaviour
{
    public Action<float> action;
    public float begin;
    public float end;
    public float time;
    public GameObject dt;
    float nowtime;

    public Action complete;
    public static DT To(Action<float> action, float begin, float end, float time)
    {
        //创建一个名为DT的空物体,将创过来的值接住
        GameObject dt = new GameObject("DT");
        DT dowteen = dt.AddComponent<DT>();
        dowteen.action = action;
        dowteen.begin = begin;
        dowteen.end = end;
        dowteen.time = time;
        dowteen.nowtime = Time.time;
        dowteen.dt = dt;
        return dowteen;
    }
    // Update is called once per frame
    void Update()
    {
        //在Update每帧调用,用起点和终点的差值确认item的位置
        if (Time.time - nowtime < time)
        {
            float t = Time.time - nowtime;
            float p = t / time;
            float a = begin * (1 - p) + end * p;
            action(a);
        }
        else
        {
            action(end);
            //如果有后续的Complete函数,则继续执行
            if (complete != null)
            {
                complete();
            }
            Destroy(gameObject);//删除空物体
        }
    }
    public  void OnComplete(Action complete)
    {
        this.complete = complete;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值