网站建设全程揭秘 光盘文件,网页美工设计实训报告,教做世界美食的网站,企业网站的布局类型文章目录简介实现原理Editor 编辑器简介
Unity中提供了三种类型的自动布局组件#xff0c;分别是Grid Layou Group、Horizontal Layout Group、Vertical Layout Group#xff0c;本文自定义了一个圆形的自动布局组件Circle Layout Group#xff0c;如图所示#xff1a; Ra…
文章目录简介实现原理Editor 编辑器简介
Unity中提供了三种类型的自动布局组件分别是Grid Layou Group、Horizontal Layout Group、Vertical Layout Group本文自定义了一个圆形的自动布局组件Circle Layout Group如图所示 RadiusCircle圆的半径Angle Delta两个元素之间的角度差Start Direction开始布局的方向上、下、左、右Auto Refresh是否自动刷新开启后当子物体数量发生变化时自动刷新布局Control Child Size是否控制元素的大小Child Size控制元素大小 实现原理
已知圆的中心点x0, y0半径radius 通过以下公式求得角度a的圆上的点坐标位置xy
float x x0 radius * Mathf.Cos(angle * Mathf.PI / 180f);
float y y0 radius * Mathf.Sin(angle * Mathf.PI / 180f);在这里我们的子物体元素以其父级为圆心所以不需要考虑x0y0
float x radius * Mathf.Cos(angle * Mathf.PI / 180f);
float y radius * Mathf.Sin(angle * Mathf.PI / 180f);三角函数原理 Sin正弦y对边 / radius斜边 Cos余弦x邻边/ radius斜边 以右侧为0度起点当方向为上方时加90度当方向为左侧时加180度当方向为下方时加270度并根据角度差和元素的层级顺序计算其角度。
代码实现如下
using UnityEngine;
using System.Collections.Generic;namespace SK.Framework.UI
{/// summary/// 圆形自动布局组件/// /summarypublic class CircleLayoutGroup : MonoBehaviour{//半径[SerializeField] private float radius 100f;//角度差[SerializeField] private float angleDelta 30f;//开始的方向 0-Right 1-Up 2-Left 3-Down[SerializeField] private int startDirection 0;//是否自动刷新布局[SerializeField] private bool autoRefresh true;//是否控制子物体的大小[SerializeField] private bool controlChildSize true;//子物体大小[SerializeField] private Vector2 childSize Vector2.one * 100f;//缓存子物体数量private int cacheChildCount;private void Start(){cacheChildCount transform.childCount;RefreshLayout();}private void Update(){//开启自动刷新if (autoRefresh){//检测到子物体数量变动if (cacheChildCount ! transform.childCount){//刷新布局RefreshLayout();//再次缓存子物体数量cacheChildCount transform.childCount;}} }/// summary/// 刷新布局/// /summarypublic void RefreshLayout(){//获取所有非隐藏状态的子物体ListRectTransform children new ListRectTransform();for (int i 0; i transform.childCount; i){Transform child transform.GetChild(i);if (child.gameObject.activeSelf){children.Add(child as RectTransform);}}//形成的扇形的角度 子物体间隙数量 * 角度差float totalAngle (children.Count - 1) * angleDelta;//总角度的一半float halfAngle totalAngle * 0.5f;//遍历这些子物体for (int i 0; i children.Count; i){RectTransform child children[i];/* 以右侧为0度起点 * 方向为Up时角度90 Left180 Down270* 方向为Right和Up时 倒序计算角度 * 确保层级中的子物体按照从左到右、从上到下的顺序自动布局 */float angle angleDelta * (startDirection 2 ? children.Count - 1 - i : i) - halfAngle startDirection * 90f;//计算x、y坐标float x radius * Mathf.Cos(angle * Mathf.PI / 180f);float y radius * Mathf.Sin(angle * Mathf.PI / 180f);//为子物体赋值坐标Vector2 anchorPos child.anchoredPosition;anchorPos.x x;anchorPos.y y;child.anchoredPosition anchorPos;//控制子物体大小if (controlChildSize){child.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, childSize.x);child.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, childSize.y);}}}}
}Editor 编辑器
通过上述代码可以实现Runtime运行时的布局自动刷新想要在Editor编辑环境编辑时自动刷新还需要自定义Editor编辑器代码如下
#if UNITY_EDITOR[CustomEditor(typeof(CircleLayoutGroup))]public class CircleLayoutGroupEditor : Editor{private enum Direction{Right 0,Up 1,Left 2,Down 3}private CircleLayoutGroup circleLayoutGroup;private SerializedProperty radius;private SerializedProperty angleDelta;private SerializedProperty startDirection;private SerializedProperty autoRefresh;private SerializedProperty controlChildSize;private SerializedProperty childSize;private Direction direction;private void OnEnable(){circleLayoutGroup target as CircleLayoutGroup;radius serializedObject.FindProperty(radius);angleDelta serializedObject.FindProperty(angleDelta);startDirection serializedObject.FindProperty(startDirection);autoRefresh serializedObject.FindProperty(autoRefresh);controlChildSize serializedObject.FindProperty(controlChildSize);childSize serializedObject.FindProperty(childSize);direction (Direction)startDirection.intValue;circleLayoutGroup.RefreshLayout();}public override void OnInspectorGUI(){float newRadius EditorGUILayout.FloatField(Radius, radius.floatValue);if (newRadius ! radius.floatValue){Undo.RecordObject(target, Radius);radius.floatValue newRadius;IsChanged();}float newAngleDelta EditorGUILayout.FloatField(Angle Delta, angleDelta.floatValue);if (newAngleDelta ! angleDelta.floatValue){Undo.RecordObject(target, Angle Delta);angleDelta.floatValue newAngleDelta;IsChanged();}Direction newDirection (Direction)EditorGUILayout.EnumPopup(Start Direction, direction);if (newDirection ! direction) {Undo.RecordObject(target, Start Direction);direction newDirection;startDirection.intValue (int)direction;IsChanged();}bool newAutoRefresh EditorGUILayout.Toggle(Auto Refresh, autoRefresh.boolValue);if (newAutoRefresh ! autoRefresh.boolValue){Undo.RecordObject(target, Angle Refresh);autoRefresh.boolValue newAutoRefresh;IsChanged();}bool newControlChildSize EditorGUILayout.Toggle(Control Child Size, controlChildSize.boolValue);if (newControlChildSize ! controlChildSize.boolValue){Undo.RecordObject(target, Control Child Size);controlChildSize.boolValue newControlChildSize;IsChanged();}if (controlChildSize.boolValue){Vector2 newChildSize EditorGUILayout.Vector2Field(Child Size, childSize.vector2Value);if (newChildSize ! childSize.vector2Value){Undo.RecordObject(target, Child Size);childSize.vector2Value newChildSize;IsChanged();}}}private void IsChanged(){if (GUI.changed){serializedObject.ApplyModifiedProperties();EditorUtility.SetDirty(target);circleLayoutGroup.RefreshLayout();}}}
#endif工具已上传SKFramework框架Package Manager中