这里使用平面2d来模拟一个3d转换,原理很简单,假设有一个圆形,所有的物体都平均的分布在这个圆环轨迹上面。这个圆环轨迹就是所有物体运行的轨道,是个垂直于你(摄像机)的方向,在靠近摄像机的位置(实际是居中位置),物体尺寸越大,越靠后和靠近两边,物体尺寸越小。
生成的子物体,上面挂上这个脚本。
using System;
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class RotationDiagramItem : MonoBehaviour,IDragHandler,IEndDragHandler
{
public int PosId;
private Action _moveAction;
private Image _image;
private float _offsetX;
private float _aniTime = 1;
private Image Image
{
get
{
if (_image == null)
_image = GetComponent<Image>();
return _image;
}
}
private RectTransform _rect;
private 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 OnDrag(PointerEventData eventData)
{
_offsetX += eventData.delta.x;
}
public void OnEndDrag(PointerEventData eventData)
{
_moveAction(_offsetX);
_offsetX = 0;
}
public void AddMoveListener(Action<float> onMove)
{
_moveAction = onMove;
}
public void ChangeId(int symbol,int totalItemNum)
{
int id = PosId;
id += symbol;
if (id < 0)
{
id += totalItemNum;
}
PosId = id%totalItemNum;
}
}
using System.Collections;
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 ScaleTimesMin;
public float ScaleTimesMax;
private List<RotationDiagramItem> _items;
private List<ItemPosData> _posData;
// Start is called before the first frame update
void Start()
{
_items = new List<RotationDiagramItem>();
_posData = new List<ItemPosData>();
CreateItem();
CalulateData();
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(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 CalulateData()
{
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, ScaleTimesMax, ScaleTimesMin);
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)
{
Debug.LogError("当前比例必须是0-1的值");
return 0;
}
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)
{
Debug.LogError("当前比例必须是0-1的值");
return 0;
}
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;
}
父物体挂上这个脚本。然后需要在父物体的脚本上面设定调整脚本的参数,运行即可。