【U3D/UGUI】3.2DImage制作仿3D轮转图

自我介绍

广东双非一本的大三小白,计科专业,想在制作毕设前夯实基础,毕设做出一款属于自己的游戏!

2DImage制作仿3D轮转图

这一章其实不难,涉及的数学知识也非常简单,但是实现过程中有很多收获!

主要的还是两个脚本:

  • RotationDiagram2D.cs 这是需要挂载到一个物体上的
  • RotationDiagramItem.cs 并不需要挂载到任何物体上,由 RotationDiagram2D 组件自动生成gameobject对象并挂载

RotationDiagram2D.cs

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class RotationDiagram2D : MonoBehaviour
{
    public Vector2 ItemSize;
    public Sprite[] ItemSprites;
    public float Offset;
    public float ScaleTimeMin;
    public float ScaleTimeMax;

    List<RotationDiagramItem> _items = new List<RotationDiagramItem>();
    List<ItemPosData> _posData = new List<ItemPosData>();

    private void Start()
    {
        CreateItem();
        CaculateData();
        SetItemData();
    }

    private GameObject CreateTemplate()
    {
        GameObject item = new GameObject("Template");
        item.AddComponent<RectTransform>().sizeDelta = ItemSize;
        item.AddComponent<Image>();
        item.AddComponent<RotationDiagramItem>();
        return item;
    }

    private void CreateItem()
    {
        GameObject template = CreateTemplate();
        RotationDiagramItem itemTemp = null;
        foreach (Sprite sprite in ItemSprites)
        {
            itemTemp = Instantiate(template).GetComponent<RotationDiagramItem>();
            itemTemp.SetParent(this.transform);
            itemTemp.SetSprite(sprite);
            itemTemp.AddMoveListener(Change);
            _items.Add(itemTemp);
        }

        Destroy(template);
    }

    private void Change(float offsetX)
    {
        int symbol = offsetX > 0 ? 1 : -1;
        Change(symbol);
    }

    private void Change(int symbol)
    {
        foreach (RotationDiagramItem item in _items)
        {
            item.ChangeId(symbol, _items.Count);
        }

        for (int i = 0; i < _posData.Count; i++)
        {
            _items[i].SetPosData(_posData[_items[i].PosId]);
        }
    }

    private void CaculateData()
    {
        List<ItemData> itemDatas = new List<ItemData>();

        float length = (ItemSize.x + Offset) * _items.Count;
        float radioOffset = 1 / (float)_items.Count;

        float radio = 0;
        for (int i = 0; i < _items.Count; i++)
        {
            ItemData itemData = new ItemData();
            itemData.PosId = i;
            itemDatas.Add(itemData);

            _items[i].PosId = i;

            ItemPosData data = new ItemPosData();
            data.X = GetX(radio, length);
            data.ScaleTimes = GetScaleTimes(radio, ScaleTimeMax, ScaleTimeMin);

            radio += radioOffset;
            _posData.Add(data);
        }

        itemDatas = itemDatas.OrderBy(u => _posData[u.PosId].ScaleTimes).ToList();

        for (int i = 0; i < itemDatas.Count; i++)
        {
            _posData[itemDatas[i].PosId].Order = i;
        }
    }

    private void SetItemData()
    {
        for (int i = 0; i < _posData.Count; i++)
        {
            _items[i].SetPosData(_posData[i]);
        }
    }

    private float GetX(float radio, float length)
    {
        if (radio > 1 || radio < 0) throw new System.Exception("当前比例必须是[0,1]");
        if (radio >= 0 && radio < 0.25f)
        {
            return length * radio;
        }
        else if (radio >= 0.25f && radio < 0.75f)
        {
            return length * (0.5f - radio);
        }
        else
        {
            return length * (radio - 1);
        }
    }

    public float GetScaleTimes(float radio, float max, float min)
    {
        if (radio > 1 || radio < 0) throw new System.Exception("当前比例必须是[0,1]");

        float scaleOffset = (max - min) / 0.5f;
        if (radio < 0.5f)
        {
            return max - scaleOffset * radio;
        }
        else
        {
            return max - scaleOffset * (1 - radio);
        }
    }

}

public class ItemPosData
{
    public float X;
    public float ScaleTimes;
    public int Order;
}

public struct ItemData
{
    public int PosId;
    public int OrderId;
}

RotationDiagramItem.cs

using DG.Tweening;
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class RotationDiagramItem : MonoBehaviour, IDragHandler, IEndDragHandler
{
    public int PosId;

    private Action<float> _moveAction;
    private float _offsetX;
    private float _aniTime = 1;
    private Image _image;
    public Image Image
    {
        get
        {
            if (_image == null) _image = GetComponent<Image>();
            return _image;
        }
    }

    private RectTransform _rect;
    public RectTransform Rect
    {
        get
        {
            if (_rect == null) _rect = GetComponent<RectTransform>();
            return _rect;
        }
    }

    public void SetParent(Transform parent)
    {
        transform.SetParent(parent);
    }

    public void SetSprite(Sprite sprite)
    {
        Image.sprite = sprite;
    }

    public void SetPosData(ItemPosData data)
    {
        Rect.DOAnchorPos(Vector2.right * data.X, _aniTime);
        Rect.DOScale(Vector3.one * data.ScaleTimes, _aniTime);
        StartCoroutine(Wait(data));
    }

    private IEnumerator Wait(ItemPosData data)
    {
        yield return new WaitForSeconds(_aniTime * 0.5f);
        transform.SetSiblingIndex(data.Order);
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        _moveAction?.Invoke(_offsetX);
        _offsetX = 0;
    }

    public void OnDrag(PointerEventData eventData)
    {
        _offsetX += eventData.delta.x;
    }

    public void AddMoveListener(Action<float> onMove)
    {
        _moveAction += onMove;
    }

    public void ChangeId(int symbol, int totalItemNum)
    {
        int id = this.PosId;
        id += symbol;
        if (id < 0)
        {
            id += totalItemNum;
        }
        this.PosId = id % totalItemNum;
    }
}

笔记:

  • 解决层级关系:
    • 利用 transform.SetSiblingIndex(data.Order); 来修改父物体下子物体的位置
    • 排序(获取父物体下子物体的order)则用到 itemDatas.OrderBy(u => _posData[u.PosId].ScaleTimes).ToList();
  • 本来 ItemPosData 是用struct的,但是用结构体的话 _posData[index].Order = i; 会报错,所以后面改成了class,原因:
    • 具体原因参考:https://www.cnblogs.com/timeObjserver/p/5931299.html
    • 简单来说:因为结构体是值类型(存储在栈上),List[]内部调用的是 public T this[int index] { get; set; } (get方法的调用是使用栈来进行的)返回的是值的临时拷贝,所以修改(= i)的内容也仅仅只是在临时拷贝里进行修改,并无任何意义,编译器自动报错

如果想要ItemPosData保持struct,也不是没有办法

//_posData[itemDatas[i].PosId].Order = i;       //报错

var tmp = _posData[itemDatas[i].PosId];
tmp.Order = i;
_posData[itemDatas[i].PosId] = tmp;

测试效果

在这里插入图片描述

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值