123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829 |
- using System;
- using System.Linq;
- using System.Reflection;
- using System.Collections.Generic;
- using UnityEngine.Tilemaps;
- using UnityEngine.Serialization;
-
- namespace UnityEngine
- {
- /// <summary>
- /// Generic visual tile for creating different tilesets like terrain, pipeline, random or animated tiles.
- /// This is templated to accept a Neighbor Rule Class for Custom Rules.
- /// </summary>
- /// <typeparam name="T">Neighbor Rule Class for Custom Rules</typeparam>
- public class RuleTile<T> : RuleTile
- {
- /// <summary>
- /// Returns the Neighbor Rule Class type for this Rule Tile.
- /// </summary>
- public sealed override Type m_NeighborType => typeof(T);
- }
-
- /// <summary>
- /// Generic visual tile for creating different tilesets like terrain, pipeline, random or animated tiles.
- /// </summary>
- [Serializable]
- [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/RuleTile.html")]
- public class RuleTile : TileBase
- {
- /// <summary>
- /// Returns the default Neighbor Rule Class type.
- /// </summary>
- public virtual Type m_NeighborType => typeof(TilingRuleOutput.Neighbor);
-
- /// <summary>
- /// The Default Sprite set when creating a new Rule.
- /// </summary>
- public Sprite m_DefaultSprite;
- /// <summary>
- /// The Default GameObject set when creating a new Rule.
- /// </summary>
- public GameObject m_DefaultGameObject;
- /// <summary>
- /// The Default Collider Type set when creating a new Rule.
- /// </summary>
- public Tile.ColliderType m_DefaultColliderType = Tile.ColliderType.Sprite;
-
- /// <summary>
- /// Angle in which the RuleTile is rotated by for matching in Degrees.
- /// </summary>
- public virtual int m_RotationAngle => 90;
-
- /// <summary>
- /// Number of rotations the RuleTile can be rotated by for matching.
- /// </summary>
- public int m_RotationCount => 360 / m_RotationAngle;
-
- /// <summary>
- /// The data structure holding the Rule information for matching Rule Tiles with
- /// its neighbors.
- /// </summary>
- [Serializable]
- public class TilingRuleOutput
- {
- /// <summary>
- /// Id for this Rule.
- /// </summary>
- public int m_Id;
- /// <summary>
- /// The output Sprites for this Rule.
- /// </summary>
- public Sprite[] m_Sprites = new Sprite[1];
- /// <summary>
- /// The output GameObject for this Rule.
- /// </summary>
- public GameObject m_GameObject;
- /// <summary>
- /// The output minimum Animation Speed for this Rule.
- /// </summary>
- [FormerlySerializedAs("m_AnimationSpeed")]
- public float m_MinAnimationSpeed = 1f;
- /// <summary>
- /// The output maximum Animation Speed for this Rule.
- /// </summary>
- [FormerlySerializedAs("m_AnimationSpeed")]
- public float m_MaxAnimationSpeed = 1f;
- /// <summary>
- /// The perlin scale factor for this Rule.
- /// </summary>
- public float m_PerlinScale = 0.5f;
- /// <summary>
- /// The output type for this Rule.
- /// </summary>
- public OutputSprite m_Output = OutputSprite.Single;
- /// <summary>
- /// The output Collider Type for this Rule.
- /// </summary>
- public Tile.ColliderType m_ColliderType = Tile.ColliderType.Sprite;
- /// <summary>
- /// The randomized transform output for this Rule.
- /// </summary>
- public Transform m_RandomTransform;
-
- /// <summary>
- /// The enumeration for matching Neighbors when matching Rule Tiles
- /// </summary>
- public class Neighbor
- {
- /// <summary>
- /// The Rule Tile will check if the contents of the cell in that direction is an instance of this Rule Tile.
- /// If not, the rule will fail.
- /// </summary>
- public const int This = 1;
- /// <summary>
- /// The Rule Tile will check if the contents of the cell in that direction is not an instance of this Rule Tile.
- /// If it is, the rule will fail.
- /// </summary>
- public const int NotThis = 2;
- }
-
- /// <summary>
- /// The enumeration for the transform rule used when matching Rule Tiles.
- /// </summary>
- public enum Transform
- {
- /// <summary>
- /// The Rule Tile will match Tiles exactly as laid out in its neighbors.
- /// </summary>
- Fixed,
- /// <summary>
- /// The Rule Tile will rotate and match its neighbors.
- /// </summary>
- Rotated,
- /// <summary>
- /// The Rule Tile will mirror in the X axis and match its neighbors.
- /// </summary>
- MirrorX,
- /// <summary>
- /// The Rule Tile will mirror in the Y axis and match its neighbors.
- /// </summary>
- MirrorY,
- /// <summary>
- /// The Rule Tile will mirror in the X or Y axis and match its neighbors.
- /// </summary>
- MirrorXY
- }
-
- /// <summary>
- /// The Output for the Tile which fits this Rule.
- /// </summary>
- public enum OutputSprite
- {
- /// <summary>
- /// A Single Sprite will be output.
- /// </summary>
- Single,
- /// <summary>
- /// A Random Sprite will be output.
- /// </summary>
- Random,
- /// <summary>
- /// A Sprite Animation will be output.
- /// </summary>
- Animation
- }
- }
-
- /// <summary>
- /// The data structure holding the Rule information for matching Rule Tiles with
- /// its neighbors.
- /// </summary>
- [Serializable]
- public class TilingRule : TilingRuleOutput
- {
- /// <summary>
- /// The matching Rule conditions for each of its neighboring Tiles.
- /// </summary>
- public List<int> m_Neighbors = new List<int>();
- /// <summary>
- /// * Preset this list to RuleTile backward compatible, but not support for HexagonalRuleTile backward compatible.
- /// </summary>
- public List<Vector3Int> m_NeighborPositions = new List<Vector3Int>()
- {
- new Vector3Int(-1, 1, 0),
- new Vector3Int(0, 1, 0),
- new Vector3Int(1, 1, 0),
- new Vector3Int(-1, 0, 0),
- new Vector3Int(1, 0, 0),
- new Vector3Int(-1, -1, 0),
- new Vector3Int(0, -1, 0),
- new Vector3Int(1, -1, 0),
- };
- /// <summary>
- /// The transform matching Rule for this Rule.
- /// </summary>
- public Transform m_RuleTransform;
-
- /// <summary>
- /// This clones a copy of the TilingRule.
- /// </summary>
- /// <returns>A copy of the TilingRule.</returns>
- public TilingRule Clone()
- {
- TilingRule rule = new TilingRule
- {
- m_Neighbors = new List<int>(m_Neighbors),
- m_NeighborPositions = new List<Vector3Int>(m_NeighborPositions),
- m_RuleTransform = m_RuleTransform,
- m_Sprites = new Sprite[m_Sprites.Length],
- m_GameObject = m_GameObject,
- m_MinAnimationSpeed = m_MinAnimationSpeed,
- m_MaxAnimationSpeed = m_MaxAnimationSpeed,
- m_PerlinScale = m_PerlinScale,
- m_Output = m_Output,
- m_ColliderType = m_ColliderType,
- m_RandomTransform = m_RandomTransform,
- };
- Array.Copy(m_Sprites, rule.m_Sprites, m_Sprites.Length);
- return rule;
- }
-
- /// <summary>
- /// Returns all neighbors of this Tile as a dictionary
- /// </summary>
- /// <returns>A dictionary of neighbors for this Tile</returns>
- public Dictionary<Vector3Int, int> GetNeighbors()
- {
- Dictionary<Vector3Int, int> dict = new Dictionary<Vector3Int, int>();
-
- for (int i = 0; i < m_Neighbors.Count && i < m_NeighborPositions.Count; i++)
- dict.Add(m_NeighborPositions[i], m_Neighbors[i]);
-
- return dict;
- }
-
- /// <summary>
- /// Applies the values from the given dictionary as this Tile's neighbors
- /// </summary>
- /// <param name="dict">Dictionary to apply values from</param>
- public void ApplyNeighbors(Dictionary<Vector3Int, int> dict)
- {
- m_NeighborPositions = dict.Keys.ToList();
- m_Neighbors = dict.Values.ToList();
- }
-
- /// <summary>
- /// Gets the cell bounds of the TilingRule.
- /// </summary>
- /// <returns>Returns the cell bounds of the TilingRule.</returns>
- public BoundsInt GetBounds()
- {
- BoundsInt bounds = new BoundsInt(Vector3Int.zero, Vector3Int.one);
- foreach (var neighbor in GetNeighbors())
- {
- bounds.xMin = Mathf.Min(bounds.xMin, neighbor.Key.x);
- bounds.yMin = Mathf.Min(bounds.yMin, neighbor.Key.y);
- bounds.xMax = Mathf.Max(bounds.xMax, neighbor.Key.x + 1);
- bounds.yMax = Mathf.Max(bounds.yMax, neighbor.Key.y + 1);
- }
- return bounds;
- }
- }
-
- /// <summary>
- /// Attribute which marks a property which cannot be overridden by a RuleOverrideTile
- /// </summary>
- public class DontOverride : Attribute { }
-
- /// <summary>
- /// A list of Tiling Rules for the Rule Tile.
- /// </summary>
- [HideInInspector] public List<TilingRule> m_TilingRules = new List<RuleTile.TilingRule>();
-
- /// <summary>
- /// Returns a set of neighboring positions for this RuleTile
- /// </summary>
- public HashSet<Vector3Int> neighborPositions
- {
- get
- {
- if (m_NeighborPositions.Count == 0)
- UpdateNeighborPositions();
-
- return m_NeighborPositions;
- }
- }
-
- private HashSet<Vector3Int> m_NeighborPositions = new HashSet<Vector3Int>();
-
- /// <summary>
- /// Updates the neighboring positions of this RuleTile
- /// </summary>
- public void UpdateNeighborPositions()
- {
- m_CacheTilemapsNeighborPositions.Clear();
-
- HashSet<Vector3Int> positions = m_NeighborPositions;
- positions.Clear();
-
- foreach (TilingRule rule in m_TilingRules)
- {
- foreach (var neighbor in rule.GetNeighbors())
- {
- Vector3Int position = neighbor.Key;
- positions.Add(position);
-
- // Check rule against rotations of 0, 90, 180, 270
- if (rule.m_RuleTransform == TilingRuleOutput.Transform.Rotated)
- {
- for (int angle = m_RotationAngle; angle < 360; angle += m_RotationAngle)
- {
- positions.Add(GetRotatedPosition(position, angle));
- }
- }
- // Check rule against x-axis, y-axis mirror
- else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorXY)
- {
- positions.Add(GetMirroredPosition(position, true, true));
- positions.Add(GetMirroredPosition(position, true, false));
- positions.Add(GetMirroredPosition(position, false, true));
- }
- // Check rule against x-axis mirror
- else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorX)
- {
- positions.Add(GetMirroredPosition(position, true, false));
- }
- // Check rule against y-axis mirror
- else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorY)
- {
- positions.Add(GetMirroredPosition(position, false, true));
- }
- }
- }
- }
-
- /// <summary>
- /// StartUp is called on the first frame of the running Scene.
- /// </summary>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <param name="tilemap">The Tilemap the tile is present on.</param>
- /// <param name="instantiatedGameObject">The GameObject instantiated for the Tile.</param>
- /// <returns>Whether StartUp was successful</returns>
- public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject instantiatedGameObject)
- {
- if (instantiatedGameObject != null)
- {
- Tilemap tmpMap = tilemap.GetComponent<Tilemap>();
- Matrix4x4 orientMatrix = tmpMap.orientationMatrix;
-
- var iden = Matrix4x4.identity;
- Vector3 gameObjectTranslation = new Vector3();
- Quaternion gameObjectRotation = new Quaternion();
- Vector3 gameObjectScale = new Vector3();
-
- bool ruleMatched = false;
- Matrix4x4 transform = iden;
- foreach (TilingRule rule in m_TilingRules)
- {
- if (RuleMatches(rule, position, tilemap, ref transform))
- {
- transform = orientMatrix * transform;
-
- // Converts the tile's translation, rotation, & scale matrix to values to be used by the instantiated GameObject
- gameObjectTranslation = new Vector3(transform.m03, transform.m13, transform.m23);
- gameObjectRotation = Quaternion.LookRotation(new Vector3(transform.m02, transform.m12, transform.m22), new Vector3(transform.m01, transform.m11, transform.m21));
- gameObjectScale = transform.lossyScale;
-
- ruleMatched = true;
- break;
- }
- }
- if (!ruleMatched)
- {
- // Fallback to just using the orientMatrix for the translation, rotation, & scale values.
- gameObjectTranslation = new Vector3(orientMatrix.m03, orientMatrix.m13, orientMatrix.m23);
- gameObjectRotation = Quaternion.LookRotation(new Vector3(orientMatrix.m02, orientMatrix.m12, orientMatrix.m22), new Vector3(orientMatrix.m01, orientMatrix.m11, orientMatrix.m21));
- gameObjectScale = orientMatrix.lossyScale;
- }
-
- instantiatedGameObject.transform.localPosition = gameObjectTranslation + tmpMap.CellToLocalInterpolated(position + tmpMap.tileAnchor);
- instantiatedGameObject.transform.localRotation = gameObjectRotation;
- instantiatedGameObject.transform.localScale = gameObjectScale;
- }
-
- return true;
- }
-
- /// <summary>
- /// Retrieves any tile rendering data from the scripted tile.
- /// </summary>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <param name="tilemap">The Tilemap the tile is present on.</param>
- /// <param name="tileData">Data to render the tile.</param>
- public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
- {
- var iden = Matrix4x4.identity;
-
- tileData.sprite = m_DefaultSprite;
- tileData.gameObject = m_DefaultGameObject;
- tileData.colliderType = m_DefaultColliderType;
- tileData.flags = TileFlags.LockTransform;
- tileData.transform = iden;
-
- Matrix4x4 transform = iden;
- foreach (TilingRule rule in m_TilingRules)
- {
- if (RuleMatches(rule, position, tilemap, ref transform))
- {
- switch (rule.m_Output)
- {
- case TilingRuleOutput.OutputSprite.Single:
- case TilingRuleOutput.OutputSprite.Animation:
- tileData.sprite = rule.m_Sprites[0];
- break;
- case TilingRuleOutput.OutputSprite.Random:
- int index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, rule.m_PerlinScale, 100000f) * rule.m_Sprites.Length), 0, rule.m_Sprites.Length - 1);
- tileData.sprite = rule.m_Sprites[index];
- if (rule.m_RandomTransform != TilingRuleOutput.Transform.Fixed)
- transform = ApplyRandomTransform(rule.m_RandomTransform, transform, rule.m_PerlinScale, position);
- break;
- }
- tileData.transform = transform;
- tileData.gameObject = rule.m_GameObject;
- tileData.colliderType = rule.m_ColliderType;
- break;
- }
- }
- }
-
- /// <summary>
- /// Returns a Perlin Noise value based on the given inputs.
- /// </summary>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <param name="scale">The Perlin Scale factor of the Tile.</param>
- /// <param name="offset">Offset of the Tile on the Tilemap.</param>
- /// <returns>A Perlin Noise value based on the given inputs.</returns>
- public static float GetPerlinValue(Vector3Int position, float scale, float offset)
- {
- return Mathf.PerlinNoise((position.x + offset) * scale, (position.y + offset) * scale);
- }
-
- static Dictionary<Tilemap, KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>>> m_CacheTilemapsNeighborPositions = new Dictionary<Tilemap, KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>>>();
- static TileBase[] m_AllocatedUsedTileArr = Array.Empty<TileBase>();
-
- static bool IsTilemapUsedTilesChange(Tilemap tilemap, out KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>> hashSet)
- {
- if (!m_CacheTilemapsNeighborPositions.TryGetValue(tilemap, out hashSet))
- return true;
-
- var oldUsedTiles = hashSet.Key;
- int newUsedTilesCount = tilemap.GetUsedTilesCount();
- if (newUsedTilesCount != oldUsedTiles.Count)
- return true;
-
- if (m_AllocatedUsedTileArr.Length < newUsedTilesCount)
- Array.Resize(ref m_AllocatedUsedTileArr, newUsedTilesCount);
-
- tilemap.GetUsedTilesNonAlloc(m_AllocatedUsedTileArr);
- for (int i = 0; i < newUsedTilesCount; i++)
- {
- TileBase newUsedTile = m_AllocatedUsedTileArr[i];
- if (!oldUsedTiles.Contains(newUsedTile))
- return true;
- }
-
- return false;
- }
-
- static KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>> CachingTilemapNeighborPositions(Tilemap tilemap)
- {
- int usedTileCount = tilemap.GetUsedTilesCount();
- HashSet<TileBase> usedTiles = new HashSet<TileBase>();
- HashSet<Vector3Int> neighborPositions = new HashSet<Vector3Int>();
-
- if (m_AllocatedUsedTileArr.Length < usedTileCount)
- Array.Resize(ref m_AllocatedUsedTileArr, usedTileCount);
-
- tilemap.GetUsedTilesNonAlloc(m_AllocatedUsedTileArr);
-
- for (int i = 0; i < usedTileCount; i++)
- {
- TileBase tile = m_AllocatedUsedTileArr[i];
- usedTiles.Add(tile);
- RuleTile ruleTile = null;
-
- if (tile is RuleTile rt)
- ruleTile = rt;
- else if (tile is RuleOverrideTile ot)
- ruleTile = ot.m_Tile;
-
- if (ruleTile)
- foreach (Vector3Int neighborPosition in ruleTile.neighborPositions)
- neighborPositions.Add(neighborPosition);
- }
-
- var value = new KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>>(usedTiles, neighborPositions);
- m_CacheTilemapsNeighborPositions[tilemap] = value;
- return value;
- }
-
- static bool NeedRelease()
- {
- foreach (var keypair in m_CacheTilemapsNeighborPositions)
- {
- if (keypair.Key == null)
- {
- return true;
- }
- }
- return false;
- }
-
- static void ReleaseDestroyedTilemapCacheData()
- {
- if (!NeedRelease())
- return;
-
- var hasCleared = false;
- var keys = m_CacheTilemapsNeighborPositions.Keys.ToArray();
- foreach (var key in keys)
- {
- if (key == null && m_CacheTilemapsNeighborPositions.Remove(key))
- hasCleared = true;
- }
- if (hasCleared)
- {
- // TrimExcess
- m_CacheTilemapsNeighborPositions = new Dictionary<Tilemap, KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>>>(m_CacheTilemapsNeighborPositions);
- }
- }
-
- /// <summary>
- /// Retrieves any tile animation data from the scripted tile.
- /// </summary>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <param name="tilemap">The Tilemap the tile is present on.</param>
- /// <param name="tileAnimationData">Data to run an animation on the tile.</param>
- /// <returns>Whether the call was successful.</returns>
- public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, ref TileAnimationData tileAnimationData)
- {
- Matrix4x4 transform = Matrix4x4.identity;
- foreach (TilingRule rule in m_TilingRules)
- {
- if (rule.m_Output == TilingRuleOutput.OutputSprite.Animation)
- {
- if (RuleMatches(rule, position, tilemap, ref transform))
- {
- tileAnimationData.animatedSprites = rule.m_Sprites;
- tileAnimationData.animationSpeed = Random.Range( rule.m_MinAnimationSpeed, rule.m_MaxAnimationSpeed);
- return true;
- }
- }
- }
- return false;
- }
-
- /// <summary>
- /// This method is called when the tile is refreshed.
- /// </summary>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <param name="tilemap">The Tilemap the tile is present on.</param>
- public override void RefreshTile(Vector3Int position, ITilemap tilemap)
- {
- base.RefreshTile(position, tilemap);
-
- Tilemap baseTilemap = tilemap.GetComponent<Tilemap>();
-
- ReleaseDestroyedTilemapCacheData(); // Prevent memory leak
-
- if (IsTilemapUsedTilesChange(baseTilemap, out var neighborPositionsSet))
- neighborPositionsSet = CachingTilemapNeighborPositions(baseTilemap);
-
- var neighborPositionsRuleTile = neighborPositionsSet.Value;
- foreach (Vector3Int offset in neighborPositionsRuleTile)
- {
- Vector3Int offsetPosition = GetOffsetPositionReverse(position, offset);
- TileBase tile = tilemap.GetTile(offsetPosition);
- RuleTile ruleTile = null;
-
- if (tile is RuleTile rt)
- ruleTile = rt;
- else if (tile is RuleOverrideTile ot)
- ruleTile = ot.m_Tile;
-
- if (ruleTile != null)
- if (ruleTile == this || ruleTile.neighborPositions.Contains(offset))
- base.RefreshTile(offsetPosition, tilemap);
- }
- }
-
- /// <summary>
- /// Does a Rule Match given a Tiling Rule and neighboring Tiles.
- /// </summary>
- /// <param name="rule">The Tiling Rule to match with.</param>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <param name="tilemap">The tilemap to match with.</param>
- /// <param name="transform">A transform matrix which will match the Rule.</param>
- /// <returns>True if there is a match, False if not.</returns>
- public virtual bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, ref Matrix4x4 transform)
- {
- if (RuleMatches(rule, position, tilemap, 0))
- {
- transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, 0f), Vector3.one);
- return true;
- }
-
- // Check rule against rotations of 0, 90, 180, 270
- if (rule.m_RuleTransform == TilingRuleOutput.Transform.Rotated)
- {
- for (int angle = m_RotationAngle; angle < 360; angle += m_RotationAngle)
- {
- if (RuleMatches(rule, position, tilemap, angle))
- {
- transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
- return true;
- }
- }
- }
- // Check rule against x-axis, y-axis mirror
- else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorXY)
- {
- if (RuleMatches(rule, position, tilemap, true, true))
- {
- transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, -1f, 1f));
- return true;
- }
- if (RuleMatches(rule, position, tilemap, true, false))
- {
- transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
- return true;
- }
- if (RuleMatches(rule, position, tilemap, false, true))
- {
- transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
- return true;
- }
- }
- // Check rule against x-axis mirror
- else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorX)
- {
- if (RuleMatches(rule, position, tilemap, true, false))
- {
- transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
- return true;
- }
- }
- // Check rule against y-axis mirror
- else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorY)
- {
- if (RuleMatches(rule, position, tilemap, false, true))
- {
- transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
- return true;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Returns a random transform matrix given the random transform rule.
- /// </summary>
- /// <param name="type">Random transform rule.</param>
- /// <param name="original">The original transform matrix.</param>
- /// <param name="perlinScale">The Perlin Scale factor of the Tile.</param>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <returns>A random transform matrix.</returns>
- public virtual Matrix4x4 ApplyRandomTransform(TilingRuleOutput.Transform type, Matrix4x4 original, float perlinScale, Vector3Int position)
- {
- float perlin = GetPerlinValue(position, perlinScale, 200000f);
- switch (type)
- {
- case TilingRuleOutput.Transform.MirrorXY:
- return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(Math.Abs(perlin - 0.5) > 0.25 ? 1f : -1f, perlin < 0.5 ? 1f : -1f, 1f));
- case TilingRuleOutput.Transform.MirrorX:
- return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(perlin < 0.5 ? 1f : -1f, 1f, 1f));
- case TilingRuleOutput.Transform.MirrorY:
- return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, perlin < 0.5 ? 1f : -1f, 1f));
- case TilingRuleOutput.Transform.Rotated:
- int angle = Mathf.Clamp(Mathf.FloorToInt(perlin * m_RotationCount), 0, m_RotationCount - 1) * m_RotationAngle;
- return Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
- }
- return original;
- }
-
- /// <summary>
- /// Returns custom fields for this RuleTile
- /// </summary>
- /// <param name="isOverrideInstance">Whether override fields are returned</param>
- /// <returns>Custom fields for this RuleTile</returns>
- public FieldInfo[] GetCustomFields(bool isOverrideInstance)
- {
- return this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
- .Where(field => typeof(RuleTile).GetField(field.Name) == null)
- .Where(field => field.IsPublic || field.IsDefined(typeof(SerializeField)))
- .Where(field => !field.IsDefined(typeof(HideInInspector)))
- .Where(field => !isOverrideInstance || !field.IsDefined(typeof(DontOverride)))
- .ToArray();
- }
-
- /// <summary>
- /// Checks if there is a match given the neighbor matching rule and a Tile.
- /// </summary>
- /// <param name="neighbor">Neighbor matching rule.</param>
- /// <param name="other">Tile to match.</param>
- /// <returns>True if there is a match, False if not.</returns>
- public virtual bool RuleMatch(int neighbor, TileBase other)
- {
- if (other is RuleOverrideTile ot)
- other = ot.m_InstanceTile;
-
- switch (neighbor)
- {
- case TilingRuleOutput.Neighbor.This: return other == this;
- case TilingRuleOutput.Neighbor.NotThis: return other != this;
- }
- return true;
- }
-
- /// <summary>
- /// Checks if there is a match given the neighbor matching rule and a Tile with a rotation angle.
- /// </summary>
- /// <param name="rule">Neighbor matching rule.</param>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <param name="tilemap">Tilemap to match.</param>
- /// <param name="angle">Rotation angle for matching.</param>
- /// <returns>True if there is a match, False if not.</returns>
- public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, int angle)
- {
- var minCount = Math.Min(rule.m_Neighbors.Count, rule.m_NeighborPositions.Count);
- for (int i = 0; i < minCount ; i++)
- {
- int neighbor = rule.m_Neighbors[i];
- Vector3Int positionOffset = GetRotatedPosition(rule.m_NeighborPositions[i], angle);
- TileBase other = tilemap.GetTile(GetOffsetPosition(position, positionOffset));
- if (!RuleMatch(neighbor, other))
- {
- return false;
- }
- }
- return true;
- }
-
- /// <summary>
- /// Checks if there is a match given the neighbor matching rule and a Tile with mirrored axii.
- /// </summary>
- /// <param name="rule">Neighbor matching rule.</param>
- /// <param name="position">Position of the Tile on the Tilemap.</param>
- /// <param name="tilemap">Tilemap to match.</param>
- /// <param name="mirrorX">Mirror X Axis for matching.</param>
- /// <param name="mirrorY">Mirror Y Axis for matching.</param>
- /// <returns>True if there is a match, False if not.</returns>
- public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, bool mirrorX, bool mirrorY)
- {
- var minCount = Math.Min(rule.m_Neighbors.Count, rule.m_NeighborPositions.Count);
- for (int i = 0; i < minCount; i++)
- {
- int neighbor = rule.m_Neighbors[i];
- Vector3Int positionOffset = GetMirroredPosition(rule.m_NeighborPositions[i], mirrorX, mirrorY);
- TileBase other = tilemap.GetTile(GetOffsetPosition(position, positionOffset));
- if (!RuleMatch(neighbor, other))
- {
- return false;
- }
- }
- return true;
- }
-
- /// <summary>
- /// Gets a rotated position given its original position and the rotation in degrees.
- /// </summary>
- /// <param name="position">Original position of Tile.</param>
- /// <param name="rotation">Rotation in degrees.</param>
- /// <returns>Rotated position of Tile.</returns>
- public virtual Vector3Int GetRotatedPosition(Vector3Int position, int rotation)
- {
- switch (rotation)
- {
- case 0:
- return position;
- case 90:
- return new Vector3Int(position.y, -position.x, 0);
- case 180:
- return new Vector3Int(-position.x, -position.y, 0);
- case 270:
- return new Vector3Int(-position.y, position.x, 0);
- }
- return position;
- }
-
- /// <summary>
- /// Gets a mirrored position given its original position and the mirroring axii.
- /// </summary>
- /// <param name="position">Original position of Tile.</param>
- /// <param name="mirrorX">Mirror in the X Axis.</param>
- /// <param name="mirrorY">Mirror in the Y Axis.</param>
- /// <returns>Mirrored position of Tile.</returns>
- public virtual Vector3Int GetMirroredPosition(Vector3Int position, bool mirrorX, bool mirrorY)
- {
- if (mirrorX)
- position.x *= -1;
- if (mirrorY)
- position.y *= -1;
- return position;
- }
-
- /// <summary>
- /// Get the offset for the given position with the given offset.
- /// </summary>
- /// <param name="position">Position to offset.</param>
- /// <param name="offset">Offset for the position.</param>
- /// <returns>The offset position.</returns>
- public virtual Vector3Int GetOffsetPosition(Vector3Int position, Vector3Int offset)
- {
- return position + offset;
- }
-
- /// <summary>
- /// Get the reversed offset for the given position with the given offset.
- /// </summary>
- /// <param name="position">Position to offset.</param>
- /// <param name="offset">Offset for the position.</param>
- /// <returns>The reversed offset position.</returns>
- public virtual Vector3Int GetOffsetPositionReverse(Vector3Int position, Vector3Int offset)
- {
- return position - offset;
- }
- }
- }
|