using System.Collections.Generic; using UnityEditorInternal; using UnityEngine; using UnityEngine.Tilemaps; namespace UnityEditor { /// /// The Editor for a RuleOverrideTileEditor. /// [CustomEditor(typeof(RuleOverrideTile))] public class RuleOverrideTileEditor : Editor { /// /// Height for a Sprite Element /// public static float k_SpriteElementHeight = 48; /// /// Height for a GameObject Element /// public static float k_GameObjectElementHeight = 16; /// /// Padding between Rule Elements /// public static float k_PaddingBetweenRules = 4; private ReorderableList m_GameObjectList; /// /// List of GameObjects and overriding GameObjects /// public List> m_GameObjects = new(); private int m_MissingOriginalGameObjectIndex; private int m_MissingOriginalSpriteIndex; private RuleTileEditor m_RuleTileEditor; private RuleTile m_RuleTileEditorTarget; private ReorderableList m_SpriteList; /// /// List of Sprites and overriding Sprites /// public List> m_Sprites = new(); /// /// The RuleOverrideTile being edited /// public RuleOverrideTile overrideTile => target as RuleOverrideTile; /// /// The RuleTileEditor for the overridden instance of the RuleTile /// public RuleTileEditor ruleTileEditor { get { if (m_RuleTileEditorTarget != overrideTile.m_Tile) { DestroyImmediate(m_RuleTileEditor); m_RuleTileEditor = CreateEditor(overrideTile.m_InstanceTile) as RuleTileEditor; m_RuleTileEditorTarget = overrideTile.m_Tile; } return m_RuleTileEditor; } } /// /// OnEnable for the RuleOverrideTileEditor /// public virtual void OnEnable() { if (m_SpriteList == null) { m_SpriteList = new ReorderableList(m_Sprites, typeof(KeyValuePair), false, true, false, false); m_SpriteList.drawHeaderCallback = DrawSpriteListHeader; m_SpriteList.drawElementCallback = DrawSpriteElement; m_SpriteList.elementHeightCallback = GetSpriteElementHeight; } if (m_GameObjectList == null) { m_GameObjectList = new ReorderableList(m_GameObjects, typeof(KeyValuePair), false, true, false, false); m_GameObjectList.drawHeaderCallback = DrawGameObjectListHeader; m_GameObjectList.drawElementCallback = DrawGameObjectElement; m_GameObjectList.elementHeightCallback = GetGameObjectElementHeight; } } /// /// OnDisable for the RuleOverrideTileEditor /// public virtual void OnDisable() { DestroyImmediate(ruleTileEditor); m_RuleTileEditorTarget = null; } /// /// Draws the Inspector GUI for the RuleOverrideTileEditor /// public override void OnInspectorGUI() { serializedObject.UpdateIfRequiredOrScript(); DrawTileField(); DrawCustomFields(); overrideTile.GetOverrides(m_Sprites, ref m_MissingOriginalSpriteIndex); overrideTile.GetOverrides(m_GameObjects, ref m_MissingOriginalGameObjectIndex); EditorGUI.BeginChangeCheck(); m_SpriteList.DoLayoutList(); if (EditorGUI.EndChangeCheck()) { overrideTile.ApplyOverrides(m_Sprites); SaveTile(); } EditorGUI.BeginChangeCheck(); m_GameObjectList.DoLayoutList(); if (EditorGUI.EndChangeCheck()) { overrideTile.ApplyOverrides(m_GameObjects); SaveTile(); } } /// /// Draws the header for the Sprite list /// /// GUI Rect to draw the header at public void DrawSpriteListHeader(Rect rect) { var xMax = rect.xMax; rect.xMax = rect.xMax / 2.0f; GUI.Label(rect, "Original Sprite", EditorStyles.label); rect.xMin = rect.xMax; rect.xMax = xMax; GUI.Label(rect, "Override Sprite", EditorStyles.label); } /// /// Draws the header for the GameObject list /// /// GUI Rect to draw the header at public void DrawGameObjectListHeader(Rect rect) { var xMax = rect.xMax; rect.xMax = rect.xMax / 2.0f; GUI.Label(rect, "Original GameObject", EditorStyles.label); rect.xMin = rect.xMax; rect.xMax = xMax; GUI.Label(rect, "Override GameObject", EditorStyles.label); } /// /// Gets the GUI element height for a Sprite element with the given index /// /// Index of the Sprite element /// GUI element height for the Sprite element public float GetSpriteElementHeight(int index) { var height = k_SpriteElementHeight + k_PaddingBetweenRules; var isMissing = index >= m_MissingOriginalSpriteIndex; if (isMissing) height += 16; return height; } /// /// Gets the GUI element height for a GameObject element with the given index /// /// Index of the GameObject element /// GUI element height for the GameObject element public float GetGameObjectElementHeight(int index) { var height = k_GameObjectElementHeight + k_PaddingBetweenRules; var isMissing = index >= m_MissingOriginalGameObjectIndex; if (isMissing) height += 16; return height; } /// /// Draws the Sprite element for the RuleOverride list /// /// Rect to draw the Sprite Element in /// Index of the Sprite Element to draw /// Whether the Sprite Element is active /// Whether the Sprite Element is focused public void DrawSpriteElement(Rect rect, int index, bool active, bool focused) { var isMissing = index >= m_MissingOriginalSpriteIndex; if (isMissing) { EditorGUI.HelpBox(new Rect(rect.xMin, rect.yMin, rect.width, 16), "Original Sprite missing", MessageType.Warning); rect.yMin += 16; } var originalSprite = m_Sprites[index].Key; var overrideSprite = m_Sprites[index].Value; rect.y += 2; rect.height -= k_PaddingBetweenRules; rect.xMax = rect.xMax / 2.0f; using (new EditorGUI.DisabledScope(true)) { EditorGUI.ObjectField(new Rect(rect.xMin, rect.yMin, rect.height, rect.height), originalSprite, typeof(Sprite), false); } rect.xMin = rect.xMax; rect.xMax *= 2.0f; EditorGUI.BeginChangeCheck(); overrideSprite = EditorGUI.ObjectField(new Rect(rect.xMin, rect.yMin, rect.height, rect.height), overrideSprite, typeof(Sprite), false) as Sprite; if (EditorGUI.EndChangeCheck()) m_Sprites[index] = new KeyValuePair(originalSprite, overrideSprite); } /// /// Draws the GameObject element for the RuleOverride list /// /// Rect to draw the GameObject Element in /// Index of the GameObject Element to draw /// Whether the GameObject Element is active /// Whether the GameObject Element is focused public void DrawGameObjectElement(Rect rect, int index, bool active, bool focused) { var isMissing = index >= m_MissingOriginalGameObjectIndex; if (isMissing) { EditorGUI.HelpBox(new Rect(rect.xMin, rect.yMin, rect.width, 16), "Original GameObject missing", MessageType.Warning); rect.yMin += 16; } var originalGameObject = m_GameObjects[index].Key; var overrideGameObject = m_GameObjects[index].Value; rect.y += 2; rect.height -= k_PaddingBetweenRules; rect.xMax = rect.xMax / 2.0f; using (new EditorGUI.DisabledScope(true)) { EditorGUI.ObjectField(new Rect(rect.xMin, rect.yMin, rect.width, rect.height), originalGameObject, typeof(GameObject), false); } rect.xMin = rect.xMax; rect.xMax *= 2.0f; EditorGUI.BeginChangeCheck(); overrideGameObject = EditorGUI.ObjectField(new Rect(rect.xMin, rect.yMin, rect.width, rect.height), overrideGameObject, typeof(GameObject), false) as GameObject; if (EditorGUI.EndChangeCheck()) m_GameObjects[index] = new KeyValuePair(originalGameObject, overrideGameObject); } /// /// Draws a field for the RuleTile be overridden /// public void DrawTileField() { EditorGUI.BeginChangeCheck(); var tile = EditorGUILayout.ObjectField(Styles.overrideTile, overrideTile.m_Tile, typeof(RuleTile), false) as RuleTile; if (EditorGUI.EndChangeCheck()) { if (!LoopCheck(tile)) { overrideTile.m_Tile = tile; SaveTile(); } else { Debug.LogWarning("Circular tile reference"); } } bool LoopCheck(RuleTile checkTile) { if (!overrideTile.m_InstanceTile) return false; var renferenceTils = new HashSet(); Add(overrideTile.m_InstanceTile); return renferenceTils.Contains(checkTile); void Add(RuleTile ruleTile) { if (renferenceTils.Contains(ruleTile)) return; renferenceTils.Add(ruleTile); var overrideTiles = RuleTileEditor.FindAffectedOverrideTiles(ruleTile); foreach (var overrideTile in overrideTiles) Add(overrideTile.m_InstanceTile); } } } /// /// Draw editor fields for custom properties for the RuleOverrideTile /// public void DrawCustomFields() { if (ruleTileEditor) { ruleTileEditor.target.hideFlags = HideFlags.None; ruleTileEditor.DrawCustomFields(true); ruleTileEditor.target.hideFlags = HideFlags.NotEditable; } } private void SaveInstanceTileAsset() { var assetChanged = false; if (overrideTile.m_InstanceTile) if (!overrideTile.m_Tile || overrideTile.m_InstanceTile.GetType() != overrideTile.m_Tile.GetType()) { DestroyImmediate(overrideTile.m_InstanceTile, true); overrideTile.m_InstanceTile = null; assetChanged = true; } if (!overrideTile.m_InstanceTile) if (overrideTile.m_Tile) { var t = overrideTile.m_Tile.GetType(); var instanceTile = CreateInstance(t) as RuleTile; instanceTile.hideFlags = HideFlags.NotEditable; AssetDatabase.AddObjectToAsset(instanceTile, overrideTile); overrideTile.m_InstanceTile = instanceTile; assetChanged = true; } if (overrideTile.m_InstanceTile) { var instanceTileName = overrideTile.m_Tile.name + " (Override)"; if (overrideTile.m_InstanceTile.name != instanceTileName) { overrideTile.m_InstanceTile.name = instanceTileName; assetChanged = true; } } if (assetChanged) { EditorUtility.SetDirty(overrideTile.m_InstanceTile); AssetDatabase.SaveAssetIfDirty(overrideTile.m_InstanceTile); } } /// /// Saves any changes to the RuleOverrideTile /// public void SaveTile() { EditorUtility.SetDirty(target); SceneView.RepaintAll(); SaveInstanceTileAsset(); if (overrideTile.m_InstanceTile) { overrideTile.Override(); RuleTileEditor.UpdateAffectedOverrideTiles(overrideTile.m_InstanceTile); } if (ruleTileEditor && ruleTileEditor.m_PreviewTilemaps != null) foreach (var tilemap in ruleTileEditor.m_PreviewTilemaps) tilemap.RefreshAllTiles(); } /// /// Renders a static preview Texture2D for a RuleOverrideTile asset /// /// Asset path of the RuleOverrideTile /// Arrays of assets from the given Asset path /// Width of the static preview /// Height of the static preview /// Texture2D containing static preview for the RuleOverrideTile asset public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height) { if (ruleTileEditor) return ruleTileEditor.RenderStaticPreview(assetPath, subAssets, width, height); return base.RenderStaticPreview(assetPath, subAssets, width, height); } /// /// Whether the RuleOverrideTile has a preview GUI /// /// True if RuleOverrideTile has a preview GUI. False if not. public override bool HasPreviewGUI() { if (ruleTileEditor) return ruleTileEditor.HasPreviewGUI(); return false; } /// /// Updates preview settings for the RuleOverrideTile. /// public override void OnPreviewSettings() { if (ruleTileEditor) ruleTileEditor.OnPreviewSettings(); } /// /// Draws the preview GUI for the RuleTile /// /// Rect to draw the preview GUI /// The GUIStyle of the background for the preview public override void OnPreviewGUI(Rect rect, GUIStyle background) { if (ruleTileEditor) ruleTileEditor.OnPreviewGUI(rect, background); } private static class Styles { public static readonly GUIContent overrideTile = EditorGUIUtility.TrTextContent("Tile" , "The Rule Tile to override."); } } }