using System; using System.Collections.Generic; using System.Linq; using UnityEngine.Scripting.APIUpdating; #if UNITY_EDITOR using UnityEditor; #endif namespace UnityEngine.Tilemaps { /// /// Rule Override Tiles are Tiles which can override a subset of Rules for a given Rule Tile to provide specialised behaviour while keeping most of the Rules originally set in the Rule Tile. /// [MovedFrom(true, "UnityEngine")] [Serializable] [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/RuleOverrideTile.html")] public class RuleOverrideTile : TileBase { /// /// A data structure storing the Sprite overriding the original RuleTile Sprite /// [Serializable] public class TileSpritePair { /// /// Original Sprite from the original RuleTile. /// public Sprite m_OriginalSprite; /// /// Overriding Sprite for the Original Sprite. /// public Sprite m_OverrideSprite; } /// /// A data structure storing the GameObject overriding the original RuleTile GameObject /// [Serializable] public class TileGameObjectPair { /// /// Original GameObject from the original RuleTile. /// public GameObject m_OriginalGameObject; /// /// Overriding GameObject for the Original Sprite. /// public GameObject m_OverrideGameObject; } /// /// Gets the overriding Sprite of a given Sprite. /// /// The original Sprite that is overridden public Sprite this[Sprite originalSprite] { get { foreach (TileSpritePair spritePair in m_Sprites) { if (spritePair.m_OriginalSprite == originalSprite) { return spritePair.m_OverrideSprite; } } return null; } set { if (value == null) { m_Sprites = m_Sprites.Where(spritePair => spritePair.m_OriginalSprite != originalSprite).ToList(); } else { foreach (TileSpritePair spritePair in m_Sprites) { if (spritePair.m_OriginalSprite == originalSprite) { spritePair.m_OverrideSprite = value; return; } } m_Sprites.Add(new TileSpritePair() { m_OriginalSprite = originalSprite, m_OverrideSprite = value, }); } } } /// /// Gets the overriding GameObject of a given GameObject. /// /// The original GameObject that is overridden public GameObject this[GameObject originalGameObject] { get { foreach (TileGameObjectPair gameObjectPair in m_GameObjects) { if (gameObjectPair.m_OriginalGameObject == originalGameObject) { return gameObjectPair.m_OverrideGameObject; } } return null; } set { if (value == null) { m_GameObjects = m_GameObjects.Where(gameObjectPair => gameObjectPair.m_OriginalGameObject != originalGameObject).ToList(); } else { foreach (TileGameObjectPair gameObjectPair in m_GameObjects) { if (gameObjectPair.m_OriginalGameObject == originalGameObject) { gameObjectPair.m_OverrideGameObject = value; return; } } m_GameObjects.Add(new TileGameObjectPair() { m_OriginalGameObject = originalGameObject, m_OverrideGameObject = value, }); } } } /// /// The RuleTile to override /// public RuleTile m_Tile; /// /// A list of Sprite Overrides /// public List m_Sprites = new List(); /// /// A list of GameObject Overrides /// public List m_GameObjects = new List(); /// /// Returns the Rule Tile for retrieving TileData /// [HideInInspector] public RuleTile m_InstanceTile; private void CreateInstanceTile() { var t = m_Tile.GetType(); RuleTile instanceTile = CreateInstance(t) as RuleTile; instanceTile.hideFlags = HideFlags.NotEditable; instanceTile.name = m_Tile.name + " (Override)"; m_InstanceTile = instanceTile; #if UNITY_EDITOR if(AssetDatabase.Contains(this)) AssetDatabase.AddObjectToAsset(instanceTile, this); EditorUtility.SetDirty(this); #endif } /// /// Applies overrides to this /// /// A list of overrides to apply /// The input overrides list is not valid public void ApplyOverrides(IList> overrides) { if (overrides == null) throw new ArgumentNullException("overrides"); for (int i = 0; i < overrides.Count; i++) this[overrides[i].Key] = overrides[i].Value; } /// /// Applies overrides to this /// /// A list of overrides to apply /// The input overrides list is not valid public void ApplyOverrides(IList> overrides) { if (overrides == null) throw new ArgumentNullException("overrides"); for (int i = 0; i < overrides.Count; i++) this[overrides[i].Key] = overrides[i].Value; } /// /// Gets overrides for this /// /// A list of overrides to fill /// Returns the number of valid overrides for Sprites /// The input overrides list is not valid public void GetOverrides(List> overrides, ref int validCount) { if (overrides == null) throw new ArgumentNullException("overrides"); overrides.Clear(); List originalSprites = new List(); if (m_Tile) { if (m_Tile.m_DefaultSprite) originalSprites.Add(m_Tile.m_DefaultSprite); foreach (RuleTile.TilingRule rule in m_Tile.m_TilingRules) foreach (Sprite sprite in rule.m_Sprites) if (sprite && !originalSprites.Contains(sprite)) originalSprites.Add(sprite); } validCount = originalSprites.Count; foreach (var pair in m_Sprites) if (!originalSprites.Contains(pair.m_OriginalSprite)) originalSprites.Add(pair.m_OriginalSprite); foreach (Sprite sprite in originalSprites) overrides.Add(new KeyValuePair(sprite, this[sprite])); } /// /// Gets overrides for this /// /// A list of overrides to fill /// Returns the number of valid overrides for GameObjects /// The input overrides list is not valid public void GetOverrides(List> overrides, ref int validCount) { if (overrides == null) throw new ArgumentNullException("overrides"); overrides.Clear(); List originalGameObjects = new List(); if (m_Tile) { if (m_Tile.m_DefaultGameObject) originalGameObjects.Add(m_Tile.m_DefaultGameObject); foreach (RuleTile.TilingRule rule in m_Tile.m_TilingRules) if (rule.m_GameObject && !originalGameObjects.Contains(rule.m_GameObject)) originalGameObjects.Add(rule.m_GameObject); } validCount = originalGameObjects.Count; foreach (var pair in m_GameObjects) if (!originalGameObjects.Contains(pair.m_OriginalGameObject)) originalGameObjects.Add(pair.m_OriginalGameObject); foreach (GameObject gameObject in originalGameObjects) overrides.Add(new KeyValuePair(gameObject, this[gameObject])); } /// /// Updates the Rules with the Overrides set for this RuleOverrideTile /// public virtual void Override() { if (!m_Tile) return; if (!m_InstanceTile) CreateInstanceTile(); PrepareOverride(); var tile = m_InstanceTile; tile.m_DefaultSprite = this[tile.m_DefaultSprite] ?? tile.m_DefaultSprite; tile.m_DefaultGameObject = this[tile.m_DefaultGameObject] ?? tile.m_DefaultGameObject; foreach (var rule in tile.m_TilingRules) { for (int i = 0; i < rule.m_Sprites.Length; i++) { Sprite sprite = rule.m_Sprites[i]; rule.m_Sprites[i] = this[sprite] ?? sprite; } rule.m_GameObject = this[rule.m_GameObject] ?? rule.m_GameObject; } } /// /// Prepares the Overrides set for this RuleOverrideTile /// public void PrepareOverride() { // Create clone of instanceTile to keep data from collections being overridden by JsonUtility var tempTile = Instantiate(m_InstanceTile); var customData = m_InstanceTile.GetCustomFields(true) .ToDictionary(field => field, field => field.GetValue(tempTile)); JsonUtility.FromJsonOverwrite(JsonUtility.ToJson(m_Tile), m_InstanceTile); foreach (var kvp in customData) kvp.Key.SetValue(m_InstanceTile, kvp.Value); } /// /// Retrieves any tile animation data from the scripted tile. /// /// Position of the Tile on the Tilemap. /// The Tilemap the tile is present on. /// Data to run an animation on the tile. /// Whether the call was successful. public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, ref TileAnimationData tileAnimationData) { if (!m_InstanceTile) return false; return m_InstanceTile.GetTileAnimationData(position, tilemap, ref tileAnimationData); } /// /// Retrieves any tile rendering data from the scripted tile. /// /// Position of the Tile on the Tilemap. /// The Tilemap the tile is present on. /// Data to render the tile. public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData) { if (!m_InstanceTile) return; m_InstanceTile.GetTileData(position, tilemap, ref tileData); } /// /// This method is called when the tile is refreshed. /// /// Position of the Tile on the Tilemap. /// The Tilemap the tile is present on. public override void RefreshTile(Vector3Int position, ITilemap tilemap) { if (!m_InstanceTile) return; m_InstanceTile.RefreshTile(position, tilemap); } /// /// StartUp is called on the first frame of the running Scene. /// /// Position of the Tile on the Tilemap. /// The Tilemap the tile is present on. /// The GameObject instantiated for the Tile. /// Whether StartUp was successful public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go) { if (!m_InstanceTile) return true; return m_InstanceTile.StartUp(position, tilemap, go); } /// /// Callback when the tile is enabled /// public void OnEnable() { if (m_Tile == null) return; if (m_InstanceTile == null) Override(); } } }