using UnityEngine; using UnityEngine.Tilemaps; using UnityEditorInternal; using System.Collections.Generic; namespace UnityEditor { /// /// The Editor for a RuleOverrideTileEditor. /// [CustomEditor(typeof(RuleOverrideTile))] public class RuleOverrideTileEditor : Editor { private static class Styles { public static readonly GUIContent overrideTile = EditorGUIUtility.TrTextContent("Tile" , "The Rule Tile to override."); } /// /// 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 = Editor.CreateEditor(overrideTile.m_InstanceTile) as RuleTileEditor; m_RuleTileEditorTarget = overrideTile.m_Tile; } return m_RuleTileEditor; } } RuleTileEditor m_RuleTileEditor; RuleTile m_RuleTileEditorTarget; /// /// List of Sprites and overriding Sprites /// public List> m_Sprites = new List>(); /// /// List of GameObjects and overriding GameObjects /// public List> m_GameObjects = new List>(); private ReorderableList m_SpriteList; private ReorderableList m_GameObjectList; private int m_MissingOriginalSpriteIndex; private int m_MissingOriginalGameObjectIndex; /// /// 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; /// /// 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) { float 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) { float 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) { float height = k_SpriteElementHeight + k_PaddingBetweenRules; bool 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) { float height = k_GameObjectElementHeight + k_PaddingBetweenRules; bool 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) { bool 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; } Sprite originalSprite = m_Sprites[index].Key; Sprite 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) { bool 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; } GameObject originalGameObject = m_GameObjects[index].Key; GameObject 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(); RuleTile 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; HashSet 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() { bool 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(); RuleTile instanceTile = ScriptableObject.CreateInstance(t) as RuleTile; instanceTile.hideFlags = HideFlags.NotEditable; AssetDatabase.AddObjectToAsset(instanceTile, overrideTile); overrideTile.m_InstanceTile = instanceTile; assetChanged = true; } } if (overrideTile.m_InstanceTile) { string 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); #if UNITY_2021_1 AssetDatabase.SaveAssets(); #else AssetDatabase.SaveAssetIfDirty(overrideTile.m_InstanceTile); #endif } } /// /// 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); } } }