在游戏制作时,有很多圆形排列的UI,我们自己用肉眼很难发现位置的对错。
我们常使用的两个组件是:UITable、UIGrid
但是这两个都不支持圆形排列的
于是写了一种圆形排列
效果如下:
核心代码:
UICircleTable.cs
// UICircleTable.cs
using System.Collections.Generic;
using UnityEngine;
public class UICircleTable : MonoBehaviour
{
public void Refresh()
{
childTrans.Clear();
var cnt = transform.childCount;
for (int i = 0; i < cnt; ++i)
{
var c = transform.GetChild(i);
if (!hideInactive || c.gameObject.activeSelf)
childTrans.Add(c);
}
var deltaAngle = (baseOnChildCnt ? 360f / childTrans.Count : angle);
var curAngle = 0f;
foreach (var child in childTrans)
{
var formatAngle = curAngle / 180f * Mathf.PI;
var posX = radius * Mathf.Sin(formatAngle);
var posY = radius * Mathf.Cos(formatAngle);
child.localPosition = new Vector3(posX, posY, 0);
if (syncAngle)
child.localEulerAngles = new Vector3(0, 0, -curAngle);
else
child.localEulerAngles = Vector3.zero;
curAngle += deltaAngle;
}
}
private void ResetChild()
{
childTrans.Clear();
for (int i = 0, cnt = transform.childCount; i < cnt; i++)
{
Transform child = transform.GetChild(i);
if (!child.name.StartsWith(childName)) continue;
if (!hideInactive || (child.gameObject.activeSelf))
childTrans.Add(child);
}
}
private void SetChildPos()
{
int cnt = childTrans.Count;
if (cnt == 0) return;
float angle = 360f / cnt;
float angle_offset = cnt % 2 == 0 ? 0 : angle * 0.5f;
//Vector3 tran_pos = transform.localPosition;
for (int i = 0; i < cnt; i++)
{
float item_angle = (angle * i + angle_offset + startAngle) * Mathf.Deg2Rad;
float x = radius * Mathf.Sin(item_angle);
float y = GetRadiusFactor(item_angle, x);
Vector3 item_pos = new Vector3(x, y, 0);
childTrans[i].localPosition = item_pos;
}
}
[ContextMenu("ExecuteSetPos")]
public void ExecuteSetPos()
{
ResetChild();
SetChildPos();
}
private float GetRadiusFactor(float item_angle, float x)
{
float y = radius * Mathf.Cos(item_angle);
if (radius_B > 0)
{
float ellipse_y = Mathf.Sqrt(1 - x * x / (radius * radius)) * radius_B;
if (y >= 0) return ellipse_y;
else return -ellipse_y;
}
return y;
}
/// <summary>
/// 角度
/// </summary>
public float angle;
/// <summary>
/// 半径
/// </summary>
public float radius = 100;
/// <summary>
/// 是否同步子节点角度
/// </summary>
public bool syncAngle;
/// <summary>
/// 是否基于子节点平均角度
/// </summary>
public bool baseOnChildCnt;
/// <summary>
/// 是否隐藏组件参加排序
/// </summary>
public bool hideInactive = true;
public string childName = string.Empty;
public float startAngle = 0;
public float radius_B;
private List<Transform> childTrans = new List<Transform>();
}
UICircleTableInspector.cs
// UICircleTableInspector.cs
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(UICircleTable), true)]
public class UICircleTableInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
m_realTimeRefresh = GUILayout.Toggle(m_realTimeRefresh, "real time refresh");
if (m_realTimeRefresh)
{
var t = target as UICircleTable;
t.Refresh();
}
if (GUILayout.Button("Refresh"))
{
var t = target as UICircleTable;
t.Refresh();
}
}
private bool m_realTimeRefresh;
}
用法:
将UICircleTable.cs 保存到Assets/Scripts
将UICircleTableInspector.cs保存到Assets/Editor
目录中
挂载UICircleTable.cs
参数说明:
Angle:单个UI所占用的角度(360°:10个 每个被分为36° ;12个 每个被分为30°)自己计算
Radius:圆的半径
SyncAngle: 是否同步子节点角度
BaseOnChildCnt:是否基于子节点平均角度
HideInactive:是否隐藏组件参加排序
Child Name:单个名字
Start Angle:开始显示时的角度
real time refresh:是否实时刷新(调整预设的时候要勾上,这样就可以实时预览效果,Apply预设之前再把real time refresh去掉勾选即可)