diff --git a/Editor/Tiles/HexagonalRuleTile/HexagonalRuleTileEditor.cs b/Editor/Tiles/HexagonalRuleTile/HexagonalRuleTileEditor.cs index b49628d..662c76b 100644 --- a/Editor/Tiles/HexagonalRuleTile/HexagonalRuleTileEditor.cs +++ b/Editor/Tiles/HexagonalRuleTile/HexagonalRuleTileEditor.cs @@ -56,34 +56,14 @@ public override BoundsInt GetRuleGUIBounds(BoundsInt bounds, RuleTile.TilingRule break; } } - if (extendNeighbor) - { - bounds.xMin--; - bounds.yMin--; - bounds.xMax++; - bounds.yMax++; - } - bounds.xMin = Mathf.Min(bounds.xMin, -1); - bounds.yMin = Mathf.Min(bounds.yMin, -1); - bounds.xMax = Mathf.Max(bounds.xMax, 2); - bounds.yMax = Mathf.Max(bounds.yMax, 2); - return bounds; + return base.GetRuleGUIBounds(bounds, rule); } public override Vector2 GetMatrixSize(BoundsInt bounds) { var hexTile = tile as HexagonalRuleTile; Vector2 size = base.GetMatrixSize(bounds); - - if (hexTile.m_FlatTop) - { - float x = size.x; - float y = size.y; - size.x = y; - size.y = x; - } - - return size; + return hexTile.m_FlatTop ? new Vector2(size.y, size.x) : size; } public override void RuleMatrixOnGUI(RuleTile tile, Rect rect, BoundsInt bounds, RuleTile.TilingRule tilingRule) @@ -158,12 +138,8 @@ public override void RuleMatrixOnGUI(RuleTile tile, Rect rect, BoundsInt bounds, { Vector3Int pos = new Vector3Int(x, y, 0); Vector2 offset = new Vector2(x - bounds.xMin, -y + bounds.yMax - 1); - Rect r; - - if (flatTop) - r = new Rect(rect.xMin + offset.y * w, rect.yMax - offset.x * h - h, w - 1, h - 1); - else - r = new Rect(rect.xMin + offset.x * w, rect.yMin + offset.y * h, w - 1, h - 1); + Rect r = flatTop ? new Rect(rect.xMin + offset.y * w, rect.yMax - offset.x * h - h, w - 1, h - 1) + : new Rect(rect.xMin + offset.x * w, rect.yMin + offset.y * h, w - 1, h - 1); if (y % 2 != 0) { @@ -182,16 +158,15 @@ public override void RuleMatrixOnGUI(RuleTile tile, Rect rect, BoundsInt bounds, } if (RuleNeighborUpdate(r, tilingRule, neighbors, pos)) { - tile.UpdateRemoteRulePositions(); + tile.UpdateNeighborPositions(); } } else { - // Center RuleTransformOnGUI(r, tilingRule.m_RuleTransform); if (RuleTransformUpdate(r, tilingRule)) { - tile.UpdateRemoteRulePositions(); + tile.UpdateNeighborPositions(); } } } diff --git a/Editor/Tiles/IsometricRuleTile/IsometricRuleTileEditor.cs b/Editor/Tiles/IsometricRuleTile/IsometricRuleTileEditor.cs index 58241f2..1982fb3 100644 --- a/Editor/Tiles/IsometricRuleTile/IsometricRuleTileEditor.cs +++ b/Editor/Tiles/IsometricRuleTile/IsometricRuleTileEditor.cs @@ -32,16 +32,16 @@ public override void RuleMatrixOnGUI(RuleTile ruleTile, Rect rect, BoundsInt bou for (int y = 0; y <= bounds.size.y; y++) { float left = rect.xMin + d * y; - float right = rect.xMax - d * (bounds.size.y - y); float top = rect.yMin + d * y; + float right = rect.xMax - d * (bounds.size.y - y); float bottom = rect.yMax - d * (bounds.size.y - y); Handles.DrawLine(new Vector3(left, bottom), new Vector3(right, top)); } for (int x = 0; x <= bounds.size.x; x++) { float left = rect.xMin + d * x; - float right = rect.xMax - d * (bounds.size.x - x); float top = rect.yMax - d * x; + float right = rect.xMax - d * (bounds.size.x - x); float bottom = rect.yMin + d * (bounds.size.x - x); Handles.DrawLine(new Vector3(left, bottom), new Vector3(right, top)); } @@ -53,8 +53,8 @@ public override void RuleMatrixOnGUI(RuleTile ruleTile, Rect rect, BoundsInt bou float iconSize = rect.width / (bounds.size.x + bounds.size.y); var rect2 = new Rect(rect); rect2.xMin += iconSize * 0.5f; - rect2.xMax -= iconSize * 0.5f; rect2.yMin += iconSize * 0.5f; + rect2.xMax -= iconSize * 0.5f; rect2.yMax -= iconSize * 0.5f; iconSize = rect2.width / (bounds.size.x + bounds.size.y - 1); float p = Mathf.Pow(2, 0.5f); @@ -82,7 +82,7 @@ public override void RuleMatrixOnGUI(RuleTile ruleTile, Rect rect, BoundsInt bou } if (RuleNeighborUpdate(r, tilingRule, neighbors, pos)) { - tile.UpdateRemoteRulePositions(); + tile.UpdateNeighborPositions(); } } else @@ -90,7 +90,7 @@ public override void RuleMatrixOnGUI(RuleTile ruleTile, Rect rect, BoundsInt bou RuleTransformOnGUI(r, tilingRule.m_RuleTransform); if (RuleTransformUpdate(r, tilingRule)) { - tile.UpdateRemoteRulePositions(); + tile.UpdateNeighborPositions(); } } } diff --git a/Editor/Tiles/RuleTile/RuleTileEditor.cs b/Editor/Tiles/RuleTile/RuleTileEditor.cs index c7b3d5b..e02e5e8 100644 --- a/Editor/Tiles/RuleTile/RuleTileEditor.cs +++ b/Editor/Tiles/RuleTile/RuleTileEditor.cs @@ -83,7 +83,7 @@ public void OnEnable() m_ReorderableList.drawHeaderCallback = OnDrawHeader; m_ReorderableList.drawElementCallback = OnDrawElement; m_ReorderableList.elementHeightCallback = GetElementHeight; - m_ReorderableList.onReorderCallback = ListUpdated; + m_ReorderableList.onChangedCallback = ListUpdated; m_ReorderableList.onAddCallback = OnAddElement; } @@ -424,7 +424,7 @@ public virtual void RuleMatrixOnGUI(RuleTile tile, Rect rect, BoundsInt bounds, } if (RuleNeighborUpdate(r, tilingRule, neighbors, pos)) { - tile.UpdateRemoteRulePositions(); + tile.UpdateNeighborPositions(); } } else @@ -432,7 +432,7 @@ public virtual void RuleMatrixOnGUI(RuleTile tile, Rect rect, BoundsInt bounds, RuleTransformOnGUI(r, tilingRule.m_RuleTransform); if (RuleTransformUpdate(r, tilingRule)) { - tile.UpdateRemoteRulePositions(); + tile.UpdateNeighborPositions(); } } } diff --git a/Runtime/Tiles/HexagonalRuleTile/HexagonalRuleTile.cs b/Runtime/Tiles/HexagonalRuleTile/HexagonalRuleTile.cs index 640fc1b..4d6b0ec 100644 --- a/Runtime/Tiles/HexagonalRuleTile/HexagonalRuleTile.cs +++ b/Runtime/Tiles/HexagonalRuleTile/HexagonalRuleTile.cs @@ -25,24 +25,7 @@ public class HexagonalRuleTile : HexagonalRuleTile public class HexagonalRuleTile : RuleTile { - /// - /// Returns the number of neighbors a Rule Tile can have. - /// - public int neighborCount => 6; - public override int m_RotationAngle => 60; - public override Vector3Int[] m_NearbyNeighborPositions => new Vector3Int[] { - new Vector3Int(-1, 1, 0), - new Vector3Int(0, 1, 0), - new Vector3Int(-1, 0, 0), - new Vector3Int(1, 0, 0), - new Vector3Int(-1, -1, 0), - new Vector3Int(0, -1, 0), - }; - public override bool IsNearbyNeighborPosition(Vector3Int position) - { - return (position.x >= -1 && position.x <= 0 && position.y >= -1 && position.y <= 1) || position == Vector3Int.right; - } private static float[] m_CosAngleArr1 = { Mathf.Cos(0 * Mathf.Deg2Rad), @@ -125,30 +108,6 @@ protected override Vector3Int GetOffsetPositionReverse(Vector3Int position, Vect return location; } - /// - /// Returns a random transform matrix given the random transform rule. - /// - /// Random transform rule. - /// The original transform matrix. - /// The Perlin Scale factor of the Tile. - /// Position of the Tile on the Tilemap. - /// A random transform matrix. - protected override Matrix4x4 ApplyRandomTransform(TilingRule.Transform type, Matrix4x4 original, float perlinScale, Vector3Int position) - { - float perlin = GetPerlinValue(position, perlinScale, 200000f); - switch (type) - { - case TilingRule.Transform.MirrorX: - return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(perlin < 0.5 ? 1f : -1f, 1f, 1f)); - case TilingRule.Transform.MirrorY: - return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, perlin < 0.5 ? 1f : -1f, 1f)); - case TilingRule.Transform.Rotated: - int angle = Mathf.Clamp(Mathf.FloorToInt(perlin * neighborCount), 0, neighborCount - 1) * (360 / neighborCount); - return Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one); - } - return original; - } - /// /// Gets a rotated position given its original position and the rotation in degrees. /// diff --git a/Runtime/Tiles/RuleTile/RuleTile.cs b/Runtime/Tiles/RuleTile/RuleTile.cs index 942136b..be134a5 100644 --- a/Runtime/Tiles/RuleTile/RuleTile.cs +++ b/Runtime/Tiles/RuleTile/RuleTile.cs @@ -44,20 +44,7 @@ public class RuleTile : TileBase public Tile.ColliderType m_DefaultColliderType = Tile.ColliderType.Sprite; public virtual int m_RotationAngle => 90; - public virtual Vector3Int[] m_NearbyNeighborPositions => new 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), - }; - public virtual bool IsNearbyNeighborPosition(Vector3Int position) - { - return position.x >= -1 && position.x <= 1 && position.y >= -1 && position.y <= 1; - } + public int m_RotationCount => 360 / m_RotationAngle; /// /// The data structure holding the Rule information for matching Rule Tiles with @@ -227,54 +214,60 @@ public enum OutputSprite /// [HideInInspector] public List m_TilingRules = new List(); - public List remoteNeighborPositions + public HashSet neighborPositions { get { - if (!m_RemoteNeighborPositionsInit) - UpdateRemoteRulePositions(); + if (m_NeighborPositions.Count == 0) + UpdateNeighborPositions(); - return m_RemoteNeighborPositions; + return m_NeighborPositions; } } - private List m_RemoteNeighborPositions; - private bool m_RemoteNeighborPositionsInit; + private HashSet m_NeighborPositions = new HashSet(); - public void UpdateRemoteRulePositions() + public void UpdateNeighborPositions() { - Dictionary positions = new Dictionary(); + m_CacheTilemapsNeighborPositions.Clear(); + + HashSet positions = m_NeighborPositions; + positions.Clear(); foreach (TilingRule rule in m_TilingRules) { foreach (var neighbor in rule.GetNeighbors()) { Vector3Int position = neighbor.Key; - if (!IsNearbyNeighborPosition(position)) - { - positions[position] = true; + positions.Add(position); - // Check rule against rotations of 0, 90, 180, 270 - if (rule.m_RuleTransform == TilingRule.Transform.Rotated) + // Check rule against rotations of 0, 90, 180, 270 + if (rule.m_RuleTransform == TilingRule.Transform.Rotated) + { + for (int angle = m_RotationAngle; angle < 360; angle += m_RotationAngle) { - for (int angle = 0; angle < 360; angle += m_RotationAngle) - { - positions[GetRotatedPosition(position, angle)] = true; - } + positions.Add(GetRotatedPosition(position, angle)); } - - // Check rule against x-axis, y-axis mirror - positions[GetMirroredPosition( - position, - rule.m_RuleTransform == TilingRule.Transform.MirrorX || rule.m_RuleTransform == TilingRule.Transform.MirrorXY, - rule.m_RuleTransform == TilingRule.Transform.MirrorY || rule.m_RuleTransform == TilingRule.Transform.MirrorXY) - ] = true; + } + // Check rule against x-axis, y-axis mirror + else if (rule.m_RuleTransform == TilingRule.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 == TilingRule.Transform.MirrorX) + { + positions.Add(GetMirroredPosition(position, true, false)); + } + // Check rule against y-axis mirror + else if (rule.m_RuleTransform == TilingRule.Transform.MirrorY) + { + positions.Add(GetMirroredPosition(position, false, true)); } } } - - m_RemoteNeighborPositions = positions.Keys.ToList(); - m_RemoteNeighborPositionsInit = true; } /// @@ -364,6 +357,70 @@ protected static float GetPerlinValue(Vector3Int position, float scale, float of return Mathf.PerlinNoise((position.x + offset) * scale, (position.y + offset) * scale); } + static Dictionary, HashSet>> m_CacheTilemapsNeighborPositions = new Dictionary, HashSet>>(); + static TileBase[] m_AllocatedUsedTileArr = new TileBase[0]; + + static bool IsTilemapUsedTilesChange(Tilemap tilemap) + { + if (!m_CacheTilemapsNeighborPositions.ContainsKey(tilemap)) + return true; + + var oldUsedTiles = m_CacheTilemapsNeighborPositions[tilemap].Key; + int newUsedTilesCount = tilemap.GetUsedTilesCount(); + + if (newUsedTilesCount != oldUsedTiles.Count) + return true; + + if (m_AllocatedUsedTileArr.Length < newUsedTilesCount) + m_AllocatedUsedTileArr = new TileBase[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 void CachingTilemapNeighborPositions(Tilemap tilemap) + { + int usedTileCount = tilemap.GetUsedTilesCount(); + HashSet usedTiles = new HashSet(); + HashSet neighborPositions = new HashSet(); + + if (m_AllocatedUsedTileArr.Length < usedTileCount) + m_AllocatedUsedTileArr = new TileBase[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) + ruleTile = tile as RuleTile; + else if (tile is RuleOverrideTile) + ruleTile = (tile as RuleOverrideTile).m_Tile; + + if (ruleTile) + foreach (Vector3Int neighborPosition in ruleTile.neighborPositions) + neighborPositions.Add(neighborPosition); + } + + m_CacheTilemapsNeighborPositions[tilemap] = new KeyValuePair, HashSet>(usedTiles, neighborPositions); + } + static void ReleaseDestroyedTilemapCacheData() + { + m_CacheTilemapsNeighborPositions = m_CacheTilemapsNeighborPositions + .Where(data => data.Key != null) + .ToDictionary(data => data.Key, data => data.Value); + } + /// /// Retrieves any tile animation data from the scripted tile. /// @@ -398,49 +455,29 @@ public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, public override void RefreshTile(Vector3Int location, ITilemap tilemap) { base.RefreshTile(location, tilemap); - RefreshNearTiles(location, tilemap); - RefreshRemoteTiles(location, tilemap); - } - public void RefreshNearTiles(Vector3Int location, ITilemap tilemap) - { - foreach (Vector3Int offset in m_NearbyNeighborPositions) - { - base.RefreshTile(location + offset, tilemap); - } - } - - private TileBase[] m_CacheUsedTiles = new TileBase[0]; - public void RefreshRemoteTiles(Vector3Int location, ITilemap tilemap) - { Tilemap tilemap_2 = tilemap.GetComponent(); - if (!tilemap_2) - return; - - int usedTilesCount = tilemap_2.GetUsedTilesCount(); - if (m_CacheUsedTiles.Length < usedTilesCount) - m_CacheUsedTiles = new TileBase[usedTilesCount]; + ReleaseDestroyedTilemapCacheData(); // Prevent memory leak - tilemap_2.GetUsedTilesNonAlloc(m_CacheUsedTiles); + if (IsTilemapUsedTilesChange(tilemap_2)) + CachingTilemapNeighborPositions(tilemap_2); - foreach (TileBase usedTile in m_CacheUsedTiles) + HashSet neighborPositions = m_CacheTilemapsNeighborPositions[tilemap_2].Value; + foreach (Vector3Int offset in neighborPositions) { - if (!usedTile) - break; - - if (!(usedTile is RuleTile)) - continue; - - RuleTile tile = usedTile as RuleTile; - foreach (Vector3Int offset in tile.remoteNeighborPositions) - { - Vector3Int remotePosition = GetOffsetPositionReverse(location, offset); - if (tilemap.GetTile(remotePosition) == tile) - { - base.RefreshTile(remotePosition, tilemap); - } - } + Vector3Int position = GetOffsetPositionReverse(location, offset); + TileBase tile = tilemap_2.GetTile(position); + RuleTile ruleTile = null; + + if (tile is RuleTile) + ruleTile = tile as RuleTile; + else if (tile is RuleOverrideTile) + ruleTile = (tile as RuleOverrideTile).m_Tile; + + if (ruleTile) + if (ruleTile.neighborPositions.Contains(offset)) + base.RefreshTile(position, tilemap); } } @@ -471,6 +508,7 @@ protected virtual bool RuleMatches(TilingRule rule, Vector3Int position, ITilema } } } + // Check rule against x-axis, y-axis mirror else if (rule.m_RuleTransform == TilingRule.Transform.MirrorXY) { if (RuleMatches(rule, position, tilemap, true, true)) @@ -531,7 +569,7 @@ protected virtual Matrix4x4 ApplyRandomTransform(TilingRule.Transform type, Matr case TilingRule.Transform.MirrorY: return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, perlin < 0.5 ? 1f : -1f, 1f)); case TilingRule.Transform.Rotated: - int angle = Mathf.Clamp(Mathf.FloorToInt(perlin * 4), 0, 3) * 90; + 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;