435 lines
16 KiB
C#
435 lines
16 KiB
C#
|
using UnityEngine;
|
|||
|
using UnityEngine.Tilemaps;
|
|||
|
using UnityEditorInternal;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
namespace UnityEditor
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// The Editor for a RuleOverrideTileEditor.
|
|||
|
/// </summary>
|
|||
|
[CustomEditor(typeof(RuleOverrideTile))]
|
|||
|
public class RuleOverrideTileEditor : Editor
|
|||
|
{
|
|||
|
private static class Styles
|
|||
|
{
|
|||
|
public static readonly GUIContent overrideTile = EditorGUIUtility.TrTextContent("Tile"
|
|||
|
, "The Rule Tile to override.");
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The RuleOverrideTile being edited
|
|||
|
/// </summary>
|
|||
|
public RuleOverrideTile overrideTile => target as RuleOverrideTile;
|
|||
|
/// <summary>
|
|||
|
/// The RuleTileEditor for the overridden instance of the RuleTile
|
|||
|
/// </summary>
|
|||
|
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;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// List of Sprites and overriding Sprites
|
|||
|
/// </summary>
|
|||
|
public List<KeyValuePair<Sprite, Sprite>> m_Sprites = new List<KeyValuePair<Sprite, Sprite>>();
|
|||
|
/// <summary>
|
|||
|
/// List of GameObjects and overriding GameObjects
|
|||
|
/// </summary>
|
|||
|
public List<KeyValuePair<GameObject, GameObject>> m_GameObjects = new List<KeyValuePair<GameObject, GameObject>>();
|
|||
|
private ReorderableList m_SpriteList;
|
|||
|
private ReorderableList m_GameObjectList;
|
|||
|
private int m_MissingOriginalSpriteIndex;
|
|||
|
private int m_MissingOriginalGameObjectIndex;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Height for a Sprite Element
|
|||
|
/// </summary>
|
|||
|
public static float k_SpriteElementHeight = 48;
|
|||
|
/// <summary>
|
|||
|
/// Height for a GameObject Element
|
|||
|
/// </summary>
|
|||
|
public static float k_GameObjectElementHeight = 16;
|
|||
|
/// <summary>
|
|||
|
/// Padding between Rule Elements
|
|||
|
/// </summary>
|
|||
|
public static float k_PaddingBetweenRules = 4;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// OnEnable for the RuleOverrideTileEditor
|
|||
|
/// </summary>
|
|||
|
public virtual void OnEnable()
|
|||
|
{
|
|||
|
if (m_SpriteList == null)
|
|||
|
{
|
|||
|
m_SpriteList = new ReorderableList(m_Sprites, typeof(KeyValuePair<Sprite, Sprite>), 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<Sprite, Sprite>), false, true, false, false);
|
|||
|
m_GameObjectList.drawHeaderCallback = DrawGameObjectListHeader;
|
|||
|
m_GameObjectList.drawElementCallback = DrawGameObjectElement;
|
|||
|
m_GameObjectList.elementHeightCallback = GetGameObjectElementHeight;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// OnDisable for the RuleOverrideTileEditor
|
|||
|
/// </summary>
|
|||
|
public virtual void OnDisable()
|
|||
|
{
|
|||
|
DestroyImmediate(ruleTileEditor);
|
|||
|
m_RuleTileEditorTarget = null;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draws the Inspector GUI for the RuleOverrideTileEditor
|
|||
|
/// </summary>
|
|||
|
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();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draws the header for the Sprite list
|
|||
|
/// </summary>
|
|||
|
/// <param name="rect">GUI Rect to draw the header at</param>
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draws the header for the GameObject list
|
|||
|
/// </summary>
|
|||
|
/// <param name="rect">GUI Rect to draw the header at</param>
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the GUI element height for a Sprite element with the given index
|
|||
|
/// </summary>
|
|||
|
/// <param name="index">Index of the Sprite element</param>
|
|||
|
/// <returns>GUI element height for the Sprite element</returns>
|
|||
|
public float GetSpriteElementHeight(int index)
|
|||
|
{
|
|||
|
float height = k_SpriteElementHeight + k_PaddingBetweenRules;
|
|||
|
|
|||
|
bool isMissing = index >= m_MissingOriginalSpriteIndex;
|
|||
|
if (isMissing)
|
|||
|
height += 16;
|
|||
|
|
|||
|
return height;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the GUI element height for a GameObject element with the given index
|
|||
|
/// </summary>
|
|||
|
/// <param name="index">Index of the GameObject element</param>
|
|||
|
/// <returns>GUI element height for the GameObject element</returns>
|
|||
|
public float GetGameObjectElementHeight(int index)
|
|||
|
{
|
|||
|
float height = k_GameObjectElementHeight + k_PaddingBetweenRules;
|
|||
|
|
|||
|
bool isMissing = index >= m_MissingOriginalGameObjectIndex;
|
|||
|
if (isMissing)
|
|||
|
height += 16;
|
|||
|
|
|||
|
return height;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draws the Sprite element for the RuleOverride list
|
|||
|
/// </summary>
|
|||
|
/// <param name="rect">Rect to draw the Sprite Element in</param>
|
|||
|
/// <param name="index">Index of the Sprite Element to draw</param>
|
|||
|
/// <param name="active">Whether the Sprite Element is active</param>
|
|||
|
/// <param name="focused">Whether the Sprite Element is focused</param>
|
|||
|
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<Sprite, Sprite>(originalSprite, overrideSprite);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draws the GameObject element for the RuleOverride list
|
|||
|
/// </summary>
|
|||
|
/// <param name="rect">Rect to draw the GameObject Element in</param>
|
|||
|
/// <param name="index">Index of the GameObject Element to draw</param>
|
|||
|
/// <param name="active">Whether the GameObject Element is active</param>
|
|||
|
/// <param name="focused">Whether the GameObject Element is focused</param>
|
|||
|
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<GameObject, GameObject>(originalGameObject, overrideGameObject);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draws a field for the RuleTile be overridden
|
|||
|
/// </summary>
|
|||
|
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<RuleTile> renferenceTils = new HashSet<RuleTile>();
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draw editor fields for custom properties for the RuleOverrideTile
|
|||
|
/// </summary>
|
|||
|
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
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Saves any changes to the RuleOverrideTile
|
|||
|
/// </summary>
|
|||
|
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();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Renders a static preview Texture2D for a RuleOverrideTile asset
|
|||
|
/// </summary>
|
|||
|
/// <param name="assetPath">Asset path of the RuleOverrideTile</param>
|
|||
|
/// <param name="subAssets">Arrays of assets from the given Asset path</param>
|
|||
|
/// <param name="width">Width of the static preview</param>
|
|||
|
/// <param name="height">Height of the static preview </param>
|
|||
|
/// <returns>Texture2D containing static preview for the RuleOverrideTile asset</returns>
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Whether the RuleOverrideTile has a preview GUI
|
|||
|
/// </summary>
|
|||
|
/// <returns>True if RuleOverrideTile has a preview GUI. False if not.</returns>
|
|||
|
public override bool HasPreviewGUI()
|
|||
|
{
|
|||
|
if (ruleTileEditor)
|
|||
|
return ruleTileEditor.HasPreviewGUI();
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Updates preview settings for the RuleOverrideTile.
|
|||
|
/// </summary>
|
|||
|
public override void OnPreviewSettings()
|
|||
|
{
|
|||
|
if (ruleTileEditor)
|
|||
|
ruleTileEditor.OnPreviewSettings();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Draws the preview GUI for the RuleTile
|
|||
|
/// </summary>
|
|||
|
/// <param name="rect">Rect to draw the preview GUI</param>
|
|||
|
/// <param name="background">The GUIStyle of the background for the preview</param>
|
|||
|
public override void OnPreviewGUI(Rect rect, GUIStyle background)
|
|||
|
{
|
|||
|
if (ruleTileEditor)
|
|||
|
ruleTileEditor.OnPreviewGUI(rect, background);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|