Unity3D结合网格绘制一个弧形轮转图

早在之前简介了一下绘制的原理,通过网格可以绘制一些简单的图形,但是上手较难,将轮转图和通过顶点绘制弧形网格结合就是本次的目的.

首先需要搞定绘制弧形的网格,代码和注释如下:

float n = 15; //定义一个变量n表示图形的个数
float l; //定义一个变量l表示周长
float r; //定义一个变量r表示半径

void Start()
{
    
    
    l = 150 * n; //计算周长
    r = l / (2 * Mathf.PI); //计算半径
    float ang = 2 * Mathf.PI / n; //计算角度
    float sunang = 2 * Mathf.PI / (n * 10); 
    VertexHelper vh = new VertexHelper(); //创建顶点帮助器

    for (int i = -5; i <= 5; i++) 
    {
    
    
        float x = Mathf.Sin(i * sunang) * r - Mathf.Sin(0) * r; //计算x坐标
        float z = Mathf.Cos(i * sunang) * r - Mathf.Cos(0) * r; //计算z坐标
        float y = 113; //可替换
        float y0 = -113; 

        float uvx = (float)(i + 5) / 10; //计算纹理坐标x值

        vh.AddVert(new Vector3(x, y, z), Color.white, new Vector2(uvx, 1)); 
        vh.AddVert(new Vector3(x, y0, z), Color.white, new Vector2(uvx, 0)); 

        if (i < 5) 
        {
    
    
            int j = i + 5; 
            vh.AddTriangle(j * 2 + 1, j * 2, (j + 1) * 2); 
            vh.AddTriangle(j * 2 + 1, (j + 1) * 2, (j + 1) * 2 + 1); 
        }
    }

    Mesh mesh = new Mesh();
    vh.FillMesh(mesh); //填充到网格

    GetComponent<MeshFilter>().mesh = mesh; //用于显示模型
    GetComponent<MeshCollider>().sharedMesh = mesh; 
}

首先是确立绘制图形的总个数,其中的 l 半径,y和y0都是根据当时图片的分辨率等比计算的结果,可以替换,绘制的顶点数也可是根据所需图形的顶点制定.
这个脚本也可以单独显示,可以看到生成好的一片弧形Mash网格
解决好了独立的一个弧形网格就该使用轮转图了,轮转图和轮播图不同,轮播图更多用于网页上图片的循环展示

将上文生成的一片弧形Mash挂载到预制体上,同时需要响应轮转图的事件,这里可以使用Unity的事件系统 IDragHandler, IBeginDragHandler, IEndDragHandler或者是OnMouseDrag() OnMouseUp()来解决,事件系统也可以用于3D轮转图,而OnMouse拖拽事件的触发条件比较宽松,两个方法各有优势

在这里挂载上的脚本采用OnMouse()方法

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);

    }

在这也使用了世界坐标专屏幕坐标的方法获取鼠标位置,再传给轮转图的移动方法和移动之后的处理

最后是Control脚本

public Texture[] textures; // 纹理数组

public GameObject prefab; 

public int num; // 数量

public float r; // 半径
float ang;
public float dec = 5f; 
List<GameObject> list = new List<GameObject>(); 
List<Transform> sortList = new List<Transform>(); 

void Start()
{
    
    
    ang = 2 * Mathf.PI / num; // 根据数量计算角度
    Move(); 
  
}

float allAng = 0;

public void OnDrag(float dis)
{
    
    
    float moveang = dis / r; 
    allAng -= moveang; 
    Move(); 
    
}

public void Move()
{
    
    
    for (int i = 0; i < num; i++)
    {
    
    
        float x = Mathf.Sin(i * ang + allAng) * r; 
        float z = Mathf.Cos(i * ang + allAng) * r; // 根据角度和半径计算z坐标值
        if (list.Count <= i)
        {
    
    
            GameObject sphere = Instantiate(prefab); 
            sphere.transform.parent = transform; 
            sphere.GetComponent<CyclogramItem>().cyclogram = this; // 获取并设置CyclogramItem组件的cyclogram属性为当前对象
            
            sphere.GetComponent<MeshRenderer>().material.mainTexture = textures[i];
            list.Add(sphere); 
            sortList.Add(sphere.transform); 
        }
        list[i].transform.localPosition = new Vector3(x, 0, z); /
        list[i].transform.localEulerAngles = Vector3.up * ((i * ang + allAng) * Mathf.Rad2Deg); 
        
    }
  

public void Inertia(float dis)
{
    
    
    float time = Mathf.Abs(dis / dec); // 计算惯性
    DT.To((a) =>
    {
    
    
        OnDrag(a);
        
    }, dis, 0, time).OnComplete(() =>
    {
    
    
        sortList.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(sortList[0].localPosition.x / r); // 对齐
        float aligntimer = Mathf.Abs(aligning * r / 200);
        DT.To((a) =>
        {
    
    
            allAng = a;
            Move();
            
        }, allAng, allAng + aligning, aligntimer).OnComplete(() =>
        {
    
    
         
            //这一部分可以添加对齐后的代码,比如对齐后会显示角色立绘,角色的详情面板以及模型等,
            //可以使用脚本也可以直接添加

        });
    });
}

代码中的对齐和偏移都是自制类Dotween类实现的,也可以直接使用Dotween动画插件进行动画效果,避免重复造轮子,-——
以下为自制DotWeen的部分应用,定义了动画的开始,结束时间,对应触发的委托和计时

 public Action<float> action;
    public float bagin;
    public float end;
    public float time;
    public Action complete;

    float nowtime;
    public static DotweenHuan To(Action<float> action, float bagin, float end, float time)
    {
    
    
        GameObject dt = new GameObject("DotweenHuan");
        DotweenHuan dotween = dt.AddComponent<DotweenHuan>();
        dotween.action = action;
        dotween.bagin = bagin;
        dotween.end = end;
        dotween.time = time;
        dotween.nowtime = Time.time;

        return dotween;
    }

    private void Update()
    {
    
    
        if (Time.time - nowtime < time)
        {
    
    
            float t = Time.time - nowtime;
            float p = t / time;
            float a = bagin * (1 - p) + end * p;
            action(a);

        }
        else
        {
    
    
            action(end);
            if (complete !=null)
            {
    
    
                complete();
            }
            Destroy(gameObject);
        }
    }

    public void OnComplete(Action complete)
    {
    
    
        this.complete = complete;
    }

完成以上准备后就可以实现对应开篇提及效果,提及效果因为OnMouse等原因并不完美,可以改进不完美的轮转图

猜你喜欢

转载自blog.csdn.net/m0_57252175/article/details/132310158