// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Utilities { /// /// A basic grid layout for game objects of a consistent size /// /// [ExecuteAlways] [AddComponentMenu("Scripts/MRTK/SDK/TileGridObjectCollection")] public class TileGridObjectCollection : MonoBehaviour { /// /// structure elements of a grid layout /// public enum GridDivisions { Rows, Columns }; /// /// How many columns should the grid have /// [Tooltip("How many rows max in each column")] [SerializeField] protected int Columns = 4; /// /// Tile size in pixels /// [Tooltip("The size of the tile or grid cell")] [SerializeField] protected Vector3 TileSize = new Vector3(0.1f, 0.1f, 0); /// /// The space between tiles in pixels /// [Tooltip("Additional space between tiles")] [SerializeField] protected Vector3 Gutters = new Vector3(0.005f, 0.005f, 0.005f); /// /// The layout direction as a normalized vector /// [Tooltip("The direction the layout should be distributed - normalized vector")] [SerializeField] protected Vector3 LayoutDireciton = new Vector3(1, -1, 0); /// /// The starting position of the grid - an offset value /// [Tooltip("The starting position offset")] [SerializeField] protected Vector3 StartPosition = Vector3.zero; /// /// Will the grid be centered or start in the top corner /// [Tooltip("Should the layout center itself on the startPosition?")] [SerializeField] protected bool Centered = false; /// /// The depth or z uses the rows if true or columns if false /// [Tooltip("Depth values (z) is applied using rows or columns position")] [SerializeField] protected GridDivisions DepthCalculatedBy = GridDivisions.Rows; /// /// Should this update during run-time /// [Tooltip("Should the update run in Edit Mode only or should this be responsive during run-time?")] [SerializeField] protected bool OnlyInEditMode = false; protected Vector3 offSet; protected bool editorUpdated = false; /// /// Load the settings of the grid with code /// /// the amount of columns /// grid tile size in pixels /// gutter size in pixels /// normalized vector flow direction /// start position offset /// center the grid or layout from edge public virtual void ConfigureGrid(int columns, Vector3 tileSize, Vector3 gutters, Vector3 layouDirection, Vector3 startPosition, bool centered) { Columns = columns; TileSize = tileSize; Gutters = gutters; LayoutDireciton = layouDirection; StartPosition = startPosition; Centered = centered; } protected virtual void OnValidate() { editorUpdated = true; } protected virtual void Start() { editorUpdated = true; } /// /// Set the item position by index /// public virtual Vector3 GetListPosition(int index) { int column = index % Columns; int row = Columns > 0 ? Mathf.FloorToInt(index / Columns) : index; Vector3 size = Vector3.Scale(TileSize + Gutters, LayoutDireciton); float xPos = size.x * column; float yPos = size.y * row; float zPos = DepthCalculatedBy == GridDivisions.Rows ? size.z * row : size.z * column; return new Vector3(xPos, yPos, zPos); } protected virtual void Update() { // restrict update unless we need this to be responsive if ((Application.isPlaying || !OnlyInEditMode) || editorUpdated) { int childCount = transform.childCount; if (Centered) { offSet = GetListPosition(Mathf.CeilToInt(childCount / Columns) * Columns - 1) * -0.5f + Vector3.Scale(TileSize, LayoutDireciton) * -0.5f; } else { offSet = Vector3.zero; } for (int i = 0; i < childCount; i++) { Transform item = transform.GetChild(i); item.localPosition = StartPosition + offSet + (Vector3.Scale(TileSize, LayoutDireciton) * 0.5f) + GetListPosition(i); } editorUpdated = false; } } } }