Search Unity

Tileset rules with multiple tilesets

Discussion in '2D' started by xandermarritt, Jan 5, 2018.

  1. xandermarritt

    xandermarritt

    Joined:
    Nov 16, 2013
    Posts:
    4
    Hey All,

    Just getting started with unity and im building a 2d platformer utilizing the tileset rules system, which is pretty great. However im wondering if there is a way to apply rules for if a tile is next to another tile of a different type? for example if a white tile is next to a black tile instead of having what is suposedly the 'edge' of each type butting up against eachother, they would connect as if it were one of their own?

    Even better than that could it be possible to have a transition tile when its next to a different type. for example an ice tile next to a ground tile. when the ice tile detects its next to the ground it uses an icy ground transition?

    This would also be extreamly usfull for adding slopes mixed with non slopes... plus it seems almost essential to top down games with a variety of ground covers, am i missing something?

    Is there a way to do this, can i use some kind of work around, or do i have to revert to the age old method of placing every tile type by hand?
     
    guiffen and totoledao like this.
  2. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
  3. xandermarritt

    xandermarritt

    Joined:
    Nov 16, 2013
    Posts:
    4
    Hi Jeffrey, thanks for the reply, yeah i did have a little poke arround in the script and see that, thought about adding a 'that' option but beyond those simple terms not much was understood. I think i have much more to learn before i can dive into changing something like this unfortunatley. this is my first project in unity and im coming from having a little knowledge in a few languages but no C# or understanding of how unity works.

    I was hoping there might be something i overlooked or a resource where someone had acheived this before. Thanks again, apreciated.
     
  4. BeanBugDev

    BeanBugDev

    Joined:
    May 11, 2018
    Posts:
    2
    @xandermarritt @jeffreyschoch
    I just finished making exactly what you're looking for: an upgrade to rule tiles that lets you add more options to each rule. You can create a new inclusive or exclusive group and use it's icon in place of the red x and green arrow in the little grids. Inclusive groups are a lists of tiles that work a lot like the green arrow: if any one of those tiles are in that direction of the rule tile, it will set to true. Exclusive groups work the opposite way. If any of the tiles in the list are there, it's false.

    Link here: https://assetstore.unity.com/packages/tools/sprite-management/advanced-rule-tiles-118786
     
    Last edited: Jun 11, 2018
    mccann likes this.
  5. honzapat

    honzapat

    Joined:
    Oct 3, 2015
    Posts:
    18
    Iam not sure that you are legally allowed to have price on it if its fork of original rule tile. If Iam not mistaken you shouldnt even publish it if it uses even the smallest part of their code.
     
  6. BeanBugDev

    BeanBugDev

    Joined:
    May 11, 2018
    Posts:
    2
    Their code is licensed as free to modify and sell. Please look into things before accusing people
     
    Last edited: Jun 20, 2019
    emalmud likes this.
  7. johnvasgird

    johnvasgird

    Joined:
    May 30, 2017
    Posts:
    1
    I updated the RuleTile.cs and RuleTileEditor.cs if you want to copy and paste the code into the scripts. Not pretty but it works (also no textures for extra rules).

    A rule block with Rule 3 means that block cannot be null.
    A rule block with Rule 4 means that block must be null.

    Also added a toggle if you want it to check across all tile maps.

    Sorry if it doesn't work, but it was what I made for my own project.

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine.Tilemaps;
    4.  
    5. namespace UnityEngine
    6. {
    7.  
    8.     public class RuleTile<T> : RuleTile
    9.     {
    10.         public sealed override Type m_NeighborType { get { return typeof(T); } }
    11.     }
    12.  
    13.     [Serializable]
    14.     [CreateAssetMenu(fileName = "New Rule Tile", menuName = "Tiles/Rule Tile")]
    15.     public class RuleTile : TileBase
    16.     {
    17.         public static Tilemap[] tileMaps;
    18.  
    19.         public virtual Type m_NeighborType { get { return typeof(TilingRule.Neighbor); } }
    20.  
    21.         private static readonly int[,] RotatedOrMirroredIndexes =
    22.         {
    23.             {2, 4, 7, 1, 6, 0, 3, 5}, // 90
    24.             {7, 6, 5, 4, 3, 2, 1, 0}, // 180, XY
    25.             {5, 3, 0, 6, 1, 7, 4, 2}, // 270
    26.             {2, 1, 0, 4, 3, 7, 6, 5}, // X
    27.             {5, 6, 7, 3, 4, 0, 1, 2}, // Y
    28.         };
    29.         private static readonly int NeighborCount = 8;
    30.         public virtual int neighborCount
    31.         {
    32.             get { return NeighborCount; }
    33.         }
    34.  
    35.         public Sprite m_DefaultSprite;
    36.         public bool m_CheckAllTilemaps = false;
    37.         public GameObject m_DefaultGameObject;
    38.         public Tile.ColliderType m_DefaultColliderType = Tile.ColliderType.Sprite;
    39.         public TileBase m_Self
    40.         {
    41.             get { return m_OverrideSelf ? m_OverrideSelf : this; }
    42.             set { m_OverrideSelf = value; }
    43.         }
    44.  
    45.         protected TileBase[] m_CachedNeighboringTiles = new TileBase[NeighborCount];
    46.         private TileBase m_OverrideSelf;
    47.         private Quaternion m_GameObjectQuaternion;
    48.  
    49.         [Serializable]
    50.         public class TilingRule
    51.         {
    52.             public int[] m_Neighbors;
    53.             public Sprite[] m_Sprites;
    54.             public GameObject m_GameObject;
    55.             public float m_AnimationSpeed;
    56.             public float m_PerlinScale;
    57.             public Transform m_RuleTransform;
    58.             public OutputSprite m_Output;
    59.             public Tile.ColliderType m_ColliderType;
    60.             public Transform m_RandomTransform;
    61.  
    62.             public TilingRule()
    63.             {
    64.                 m_Output = OutputSprite.Single;
    65.                 m_Neighbors = new int[NeighborCount];
    66.                 m_Sprites = new Sprite[1];
    67.                 m_GameObject = null;
    68.                 m_AnimationSpeed = 1f;
    69.                 m_PerlinScale = 0.5f;
    70.                 m_ColliderType = Tile.ColliderType.Sprite;
    71.  
    72.                 for (int i = 0; i < m_Neighbors.Length; i++)
    73.                     m_Neighbors[i] = Neighbor.DontCare;
    74.             }
    75.  
    76.             public class Neighbor
    77.             {
    78.                 public const int DontCare = 0;
    79.                 public const int This = 1;
    80.                 public const int NotThis = 2;
    81.                 public const int AnyButNull = 3;
    82.                 public const int MustBeNull = 4;
    83.             }
    84.             public enum Transform { Fixed, Rotated, MirrorX, MirrorY }
    85.             public enum OutputSprite { Single, Random, Animation }
    86.         }
    87.  
    88.         [HideInInspector] public List<TilingRule> m_TilingRules;
    89.  
    90.         public override bool StartUp(Vector3Int location, ITilemap tilemap, GameObject instantiateedGameObject)
    91.         {
    92.             if (m_CheckAllTilemaps)
    93.             tileMaps = GameObject.FindObjectsOfType<Tilemap>();
    94.             else
    95.             {
    96.                 tileMaps = new Tilemap[0];
    97.             }
    98.             if (instantiateedGameObject != null)
    99.             {
    100.                 Tilemap tmpMap = tilemap.GetComponent<Tilemap>();
    101.                 instantiateedGameObject.transform.position = tmpMap.LocalToWorld(tmpMap.CellToLocalInterpolated(location + tmpMap.tileAnchor));
    102.                 instantiateedGameObject.transform.rotation = m_GameObjectQuaternion;
    103.             }
    104.  
    105.             return true;
    106.         }
    107.  
    108.         public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    109.         {
    110.             TileBase[] neighboringTiles = null;
    111.             GetMatchingNeighboringTiles(tilemap, position, ref neighboringTiles);
    112.             var iden = Matrix4x4.identity;
    113.  
    114.             tileData.sprite = m_DefaultSprite;
    115.             tileData.gameObject = m_DefaultGameObject;
    116.             tileData.colliderType = m_DefaultColliderType;
    117.             tileData.flags = TileFlags.LockTransform;
    118.             tileData.transform = iden;
    119.  
    120.             foreach (TilingRule rule in m_TilingRules)
    121.             {
    122.                 Matrix4x4 transform = iden;
    123.                 if (RuleMatches(rule, ref neighboringTiles, ref transform))
    124.                 {
    125.                     switch (rule.m_Output)
    126.                     {
    127.                         case TilingRule.OutputSprite.Single:
    128.                         case TilingRule.OutputSprite.Animation:
    129.                             tileData.sprite = rule.m_Sprites[0];
    130.                             break;
    131.                         case TilingRule.OutputSprite.Random:
    132.                             int index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, rule.m_PerlinScale, 100000f) * rule.m_Sprites.Length), 0, rule.m_Sprites.Length - 1);
    133.                             tileData.sprite = rule.m_Sprites[index];
    134.                             if (rule.m_RandomTransform != TilingRule.Transform.Fixed)
    135.                                 transform = ApplyRandomTransform(rule.m_RandomTransform, transform, rule.m_PerlinScale, position);
    136.                             break;
    137.                     }
    138.                     tileData.transform = transform;
    139.                     tileData.gameObject = rule.m_GameObject;
    140.                     tileData.colliderType = rule.m_ColliderType;
    141.  
    142.                     // Converts the tile's rotation matrix to a quaternion to be used by the instantiated Game Object
    143.                     m_GameObjectQuaternion = Quaternion.LookRotation(new Vector3(transform.m02, transform.m12, transform.m22), new Vector3(transform.m01, transform.m11, transform.m21));
    144.                     break;
    145.                 }
    146.             }
    147.         }
    148.  
    149.         protected static float GetPerlinValue(Vector3Int position, float scale, float offset)
    150.         {
    151.             return Mathf.PerlinNoise((position.x + offset) * scale, (position.y + offset) * scale);
    152.         }
    153.  
    154.         public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, ref TileAnimationData tileAnimationData)
    155.         {
    156.             TileBase[] neighboringTiles = null;
    157.             var iden = Matrix4x4.identity;
    158.             foreach (TilingRule rule in m_TilingRules)
    159.             {
    160.                 if (rule.m_Output == TilingRule.OutputSprite.Animation)
    161.                 {
    162.                     Matrix4x4 transform = iden;
    163.                     GetMatchingNeighboringTiles(tilemap, position, ref neighboringTiles);
    164.                     if (RuleMatches(rule, ref neighboringTiles, ref transform))
    165.                     {
    166.                         tileAnimationData.animatedSprites = rule.m_Sprites;
    167.                         tileAnimationData.animationSpeed = rule.m_AnimationSpeed;
    168.                         return true;
    169.                     }
    170.                 }
    171.             }
    172.             return false;
    173.         }
    174.  
    175.         public override void RefreshTile(Vector3Int location, ITilemap tileMap)
    176.         {
    177.             if (m_TilingRules != null && m_TilingRules.Count > 0)
    178.             {
    179.                 for (int y = -1; y <= 1; y++)
    180.                 {
    181.                     for (int x = -1; x <= 1; x++)
    182.                     {
    183.                         base.RefreshTile(location + new Vector3Int(x, y, 0), tileMap);
    184.                     }
    185.                 }
    186.             }
    187.             else
    188.             {
    189.                 base.RefreshTile(location, tileMap);
    190.             }
    191.         }
    192.  
    193.         protected virtual bool RuleMatches(TilingRule rule, ref TileBase[] neighboringTiles, ref Matrix4x4 transform)
    194.         {
    195.             // Check rule against rotations of 0, 90, 180, 270
    196.             //if (m_TilingRules.Count == neighboringTiles.Length)
    197.             //    Debug.Log(m_TilingRules.Count);
    198.             for (int angle = 0; angle <= (rule.m_RuleTransform == TilingRule.Transform.Rotated ? 270 : 0); angle += 90)
    199.             {
    200.                 if (RuleMatches(rule, ref neighboringTiles, angle))
    201.                 {
    202.                     transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
    203.                     return true;
    204.                 }
    205.             }
    206.  
    207.             // Check rule against x-axis mirror
    208.             if ((rule.m_RuleTransform == TilingRule.Transform.MirrorX) && RuleMatches(rule, ref neighboringTiles, true, false))
    209.             {
    210.                 transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
    211.                 return true;
    212.             }
    213.  
    214.             // Check rule against y-axis mirror
    215.             if ((rule.m_RuleTransform == TilingRule.Transform.MirrorY) && RuleMatches(rule, ref neighboringTiles, false, true))
    216.             {
    217.                 transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
    218.                 return true;
    219.             }
    220.  
    221.             return false;
    222.         }
    223.  
    224.         protected virtual Matrix4x4 ApplyRandomTransform(TilingRule.Transform type, Matrix4x4 original, float perlinScale, Vector3Int position)
    225.         {
    226.             float perlin = GetPerlinValue(position, perlinScale, 200000f);
    227.             switch (type)
    228.             {
    229.                 case TilingRule.Transform.MirrorX:
    230.                     return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(perlin < 0.5 ? 1f : -1f, 1f, 1f));
    231.                 case TilingRule.Transform.MirrorY:
    232.                     return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, perlin < 0.5 ? 1f : -1f, 1f));
    233.                 case TilingRule.Transform.Rotated:
    234.                     int angle = Mathf.Clamp(Mathf.FloorToInt(perlin * 4), 0, 3) * 90;
    235.                     return Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
    236.             }
    237.             return original;
    238.         }
    239.  
    240.         public virtual bool RuleMatch(int neighbor, TileBase tile)
    241.         {
    242.             if (TilingRule.Neighbor.MustBeNull == neighbor)
    243.             {
    244.                 Debug.Log(tile);
    245.                 return tile == null;
    246.             }
    247.             if (TilingRule.Neighbor.AnyButNull == neighbor)
    248.             {
    249.                 return tile != null;
    250.             }
    251.             if (tile is RuleOverrideTile)
    252.                 tile = (tile as RuleOverrideTile).runtimeTile.m_Self;
    253.             else if (tile is RuleTile)
    254.                 tile = (tile as RuleTile).m_Self;
    255.  
    256.             switch (neighbor)
    257.             {
    258.                
    259.                 case TilingRule.Neighbor.This: return tile == m_Self;
    260.                 case TilingRule.Neighbor.NotThis: return tile != m_Self;
    261.             }
    262.             return true;
    263.         }
    264.  
    265.         protected bool RuleMatches(TilingRule rule, ref TileBase[] neighboringTiles, int angle)
    266.         {
    267.            
    268.             for (int i = 0; i < neighborCount; ++i)
    269.             {
    270.                
    271.                 int index = GetRotatedIndex(i, angle);
    272.                 TileBase tile = neighboringTiles[index];
    273.                 if (!RuleMatch(rule.m_Neighbors[i], tile))
    274.                 {
    275.                     return false;
    276.                 }
    277.             }
    278.            
    279.             return true;
    280.         }
    281.  
    282.         protected bool RuleMatches(TilingRule rule, ref TileBase[] neighboringTiles, bool mirrorX, bool mirrorY)
    283.         {
    284.             for (int i = 0; i < neighborCount; ++i)
    285.             {
    286.                 int index = GetMirroredIndex(i, mirrorX, mirrorY);
    287.                 TileBase tile = neighboringTiles[index];
    288.                 if (!RuleMatch(rule.m_Neighbors[i], tile))
    289.                 {
    290.                     return false;
    291.                 }
    292.             }
    293.             return true;
    294.         }
    295.  
    296.         protected virtual void GetMatchingNeighboringTiles(ITilemap tilemap, Vector3Int position, ref TileBase[] neighboringTiles)
    297.         {
    298.             if (neighboringTiles != null)
    299.                 return;
    300.  
    301.             if (m_CachedNeighboringTiles == null || m_CachedNeighboringTiles.Length < neighborCount)
    302.                 m_CachedNeighboringTiles = new TileBase[neighborCount];
    303.  
    304.             int index = 0;
    305.             for (int y = 1; y >= -1; y--)
    306.             {
    307.                 for (int x = -1; x <= 1; x++)
    308.                 {
    309.                     if (x != 0 || y != 0)
    310.                     {
    311.                         Vector3Int tilePosition = new Vector3Int(position.x + x, position.y + y, position.z);
    312.                         TileBase tile = tilemap.GetTile(tilePosition);
    313.                         m_CachedNeighboringTiles[index++] = tile;
    314.                         if (tile == null)
    315.                         {
    316.                             foreach (Tilemap map in tileMaps)
    317.                             {
    318.                                 tile = map.GetTile(tilePosition);
    319.                                 if (tile != null)
    320.                                 {
    321.                                     m_CachedNeighboringTiles[index-1] = tile;
    322.                                     break;
    323.                                 }
    324.                             }
    325.  
    326.                         }
    327.                        
    328.                     }
    329.                 }
    330.             }
    331.             neighboringTiles = m_CachedNeighboringTiles;
    332.         }
    333.  
    334.         protected virtual int GetRotatedIndex(int original, int rotation)
    335.         {
    336.             switch (rotation)
    337.             {
    338.                 case 0:
    339.                     return original;
    340.                 case 90:
    341.                     return RotatedOrMirroredIndexes[0, original];
    342.                 case 180:
    343.                     return RotatedOrMirroredIndexes[1, original];
    344.                 case 270:
    345.                     return RotatedOrMirroredIndexes[2, original];
    346.             }
    347.             return original;
    348.         }
    349.  
    350.         protected virtual int GetMirroredIndex(int original, bool mirrorX, bool mirrorY)
    351.         {
    352.             if (mirrorX && mirrorY)
    353.             {
    354.                 return RotatedOrMirroredIndexes[1, original];
    355.             }
    356.             if (mirrorX)
    357.             {
    358.                 return RotatedOrMirroredIndexes[3, original];
    359.             }
    360.             if (mirrorY)
    361.             {
    362.                 return RotatedOrMirroredIndexes[4, original];
    363.             }
    364.             return original;
    365.         }
    366.     }
    367. }
    368.  
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Reflection;
    4. using System.Linq;
    5. using UnityEditorInternal;
    6. using UnityEngine;
    7. using UnityEngine.Tilemaps;
    8. using Object = UnityEngine.Object;
    9.  
    10. namespace UnityEditor
    11. {
    12.     [CustomEditor(typeof(RuleTile), true)]
    13.     [CanEditMultipleObjects]
    14.     internal class RuleTileEditor : Editor
    15.     {
    16.         private const string s_XIconString = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAABoSURBVDhPnY3BDcAgDAOZhS14dP1O0x2C/LBEgiNSHvfwyZabmV0jZRUpq2zi6f0DJwdcQOEdwwDLypF0zHLMa9+NQRxkQ+ACOT2STVw/q8eY1346ZlE54sYAhVhSDrjwFymrSFnD2gTZpls2OvFUHAAAAABJRU5ErkJggg==";
    17.         private const string s_Arrow0 = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAACYSURBVDhPzZExDoQwDATzE4oU4QXXcgUFj+YxtETwgpMwXuFcwMFSRMVKKwzZcWzhiMg91jtg34XIntkre5EaT7yjjhI9pOD5Mw5k2X/DdUwFr3cQ7Pu23E/BiwXyWSOxrNqx+ewnsayam5OLBtbOGPUM/r93YZL4/dhpR/amwByGFBz170gNChA6w5bQQMqramBTgJ+Z3A58WuWejPCaHQAAAABJRU5ErkJggg==";
    18.         private const string s_Arrow1 = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAABqSURBVDhPxYzBDYAgEATpxYcd+PVr0fZ2siZrjmMhFz6STIiDs8XMlpEyi5RkO/d66TcgJUB43JfNBqRkSEYDnYjhbKD5GIUkDqRDwoH3+NgTAw+bL/aoOP4DOgH+iwECEt+IlFmkzGHlAYKAWF9R8zUnAAAAAElFTkSuQmCC";
    19.         private const string s_Arrow2 = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAC0SURBVDhPjVE5EsIwDMxPKFKYF9CagoJH8xhaMskLmEGsjOSRkBzYmU2s9a58TUQUmCH1BWEHweuKP+D8tphrWcAHuIGrjPnPNY8X2+DzEWE+FzrdrkNyg2YGNNfRGlyOaZDJOxBrDhgOowaYW8UW0Vau5ZkFmXbbDr+CzOHKmLinAXMEePyZ9dZkZR+s5QX2O8DY3zZ/sgYcdDqeEVp8516o0QQV1qeMwg6C91toYoLoo+kNt/tpKQEVvFQAAAAASUVORK5CYII=";
    20.         private const string s_Arrow3 = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAB2SURBVDhPzY1LCoAwEEPnLi48gW5d6p31bH5SMhp0Cq0g+CCLxrzRPqMZ2pRqKG4IqzJc7JepTlbRZXYpWTg4RZE1XAso8VHFKNhQuTjKtZvHUNCEMogO4K3BhvMn9wP4EzoPZ3n0AGTW5fiBVzLAAYTP32C2Ay3agtu9V/9PAAAAAElFTkSuQmCC";
    21.         private const string s_Arrow5 = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAABqSURBVDhPnY3BCYBADASvFx924NevRdvbyoLBmNuDJQMDGjNxAFhK1DyUQ9fvobCdO+j7+sOKj/uSB+xYHZAxl7IR1wNTXJeVcaAVU+614uWfCT9mVUhknMlxDokd15BYsQrJFHeUQ0+MB5ErsPi/6hO1AAAAAElFTkSuQmCC";
    22.         private const string s_Arrow6 = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAACaSURBVDhPxZExEkAwEEVzE4UiTqClUDi0w2hlOIEZsV82xCZmQuPPfFn8t1mirLWf7S5flQOXjd64vCuEKWTKVt+6AayH3tIa7yLg6Qh2FcKFB72jBgJeziA1CMHzeaNHjkfwnAK86f3KUafU2ClHIJSzs/8HHLv09M3SaMCxS7ljw/IYJWzQABOQZ66x4h614ahTCL/WT7BSO51b5Z5hSx88AAAAAElFTkSuQmCC";
    23.         private const string s_Arrow7 = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAABQSURBVDhPYxh8QNle/T8U/4MKEQdAmsz2eICx6W530gygr2aQBmSMphkZYxqErAEXxusKfAYQ7XyyNMIAsgEkaYQBkAFkaYQBsjXSGDAwAAD193z4luKPrAAAAABJRU5ErkJggg==";
    24.         private const string s_Arrow8 = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAACYSURBVDhPxZE9DoAwCIW9iUOHegJXHRw8tIdx1egJTMSHAeMPaHSR5KVQ+KCkCRF91mdz4VDEWVzXTBgg5U1N5wahjHzXS3iFFVRxAygNVaZxJ6VHGIl2D6oUXP0ijlJuTp724FnID1Lq7uw2QM5+thoKth0N+GGyA7IA3+yM77Ag1e2zkey5gCdAg/h8csy+/89v7E+YkgUntOWeVt2SfAAAAABJRU5ErkJggg==";
    25.         private const string s_MirrorX = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwQAADsEBuJFr7QAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC41ZYUyZQAAAG1JREFUOE+lj9ENwCAIRB2IFdyRfRiuDSaXAF4MrR9P5eRhHGb2Gxp2oaEjIovTXSrAnPNx6hlgyCZ7o6omOdYOldGIZhAziEmOTSfigLV0RYAB9y9f/7kO8L3WUaQyhCgz0dmCL9CwCw172HgBeyG6oloC8fAAAAAASUVORK5CYII=";
    26.         private const string s_MirrorY = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC41ZYUyZQAAAG9JREFUOE+djckNACEMAykoLdAjHbPyw1IOJ0L7mAejjFlm9hspyd77Kk+kBAjPOXcakJIh6QaKyOE0EB5dSPJAiUmOiL8PMVGxugsP/0OOib8vsY8yYwy6gRyC8CB5QIWgCMKBLgRSkikEUr5h6wOPWfMoCYILdgAAAABJRU5ErkJggg==";
    27.         private const string s_Rotated = "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwQAADsEBuJFr7QAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC41ZYUyZQAAAHdJREFUOE+djssNwCAMQxmIFdgx+2S4Vj4YxWlQgcOT8nuG5u5C732Sd3lfLlmPMR4QhXgrTQaimUlA3EtD+CJlBuQ7aUAUMjEAv9gWCQNEPhHJUkYfZ1kEpcxDzioRzGIlr0Qwi0r+Q5rTgM+AAVcygHgt7+HtBZs/2QVWP8ahAAAAAElFTkSuQmCC";
    28.  
    29.         private static Texture2D[] s_Arrows;
    30.         public static Texture2D[] arrows
    31.         {
    32.             get
    33.             {
    34.                 if (s_Arrows == null)
    35.                 {
    36.                     s_Arrows = new Texture2D[10];
    37.                     s_Arrows[0] = Base64ToTexture(s_Arrow0);
    38.                     s_Arrows[1] = Base64ToTexture(s_Arrow1);
    39.                     s_Arrows[2] = Base64ToTexture(s_Arrow2);
    40.                     s_Arrows[3] = Base64ToTexture(s_Arrow3);
    41.                     s_Arrows[5] = Base64ToTexture(s_Arrow5);
    42.                     s_Arrows[6] = Base64ToTexture(s_Arrow6);
    43.                     s_Arrows[7] = Base64ToTexture(s_Arrow7);
    44.                     s_Arrows[8] = Base64ToTexture(s_Arrow8);
    45.                     s_Arrows[9] = Base64ToTexture(s_XIconString);
    46.                 }
    47.                 return s_Arrows;
    48.             }
    49.         }
    50.  
    51.         private static Texture2D[] s_AutoTransforms;
    52.         public static Texture2D[] autoTransforms
    53.         {
    54.             get
    55.             {
    56.                 if (s_AutoTransforms == null)
    57.                 {
    58.                     s_AutoTransforms = new Texture2D[3];
    59.                     s_AutoTransforms[0] = Base64ToTexture(s_Rotated);
    60.                     s_AutoTransforms[1] = Base64ToTexture(s_MirrorX);
    61.                     s_AutoTransforms[2] = Base64ToTexture(s_MirrorY);
    62.                 }
    63.                 return s_AutoTransforms;
    64.             }
    65.         }
    66.  
    67.         private RuleTile tile { get { return (target as RuleTile); } }
    68.         private ReorderableList m_ReorderableList;
    69.  
    70.         internal const float k_DefaultElementHeight = 48f;
    71.         internal const float k_PaddingBetweenRules = 26f;
    72.         internal const float k_SingleLineHeight = 16f;
    73.         internal const float k_LabelWidth = 80f;
    74.  
    75.         public void OnEnable()
    76.         {
    77.             if (tile.m_TilingRules == null)
    78.                 tile.m_TilingRules = new List<RuleTile.TilingRule>();
    79.  
    80.             m_ReorderableList = new ReorderableList(tile.m_TilingRules, typeof(RuleTile.TilingRule), true, true, true, true);
    81.             m_ReorderableList.drawHeaderCallback = OnDrawHeader;
    82.             m_ReorderableList.drawElementCallback = OnDrawElement;
    83.             m_ReorderableList.elementHeightCallback = GetElementHeight;
    84.             m_ReorderableList.onReorderCallback = ListUpdated;
    85.             m_ReorderableList.onAddCallback = OnAddElement;
    86.         }
    87.  
    88.         private void ListUpdated(ReorderableList list)
    89.         {
    90.             SaveTile();
    91.         }
    92.  
    93.         private float GetElementHeight(int index)
    94.         {
    95.             if (tile.m_TilingRules != null && tile.m_TilingRules.Count > 0)
    96.             {
    97.                 switch (tile.m_TilingRules[index].m_Output)
    98.                 {
    99.                     case RuleTile.TilingRule.OutputSprite.Random:
    100.                         return k_DefaultElementHeight + k_SingleLineHeight * (tile.m_TilingRules[index].m_Sprites.Length + 3) + k_PaddingBetweenRules;
    101.                     case RuleTile.TilingRule.OutputSprite.Animation:
    102.                         return k_DefaultElementHeight + k_SingleLineHeight * (tile.m_TilingRules[index].m_Sprites.Length + 2) + k_PaddingBetweenRules;
    103.                 }
    104.             }
    105.             return k_DefaultElementHeight + k_PaddingBetweenRules;
    106.         }
    107.  
    108.         private void OnDrawElement(Rect rect, int index, bool isactive, bool isfocused)
    109.         {
    110.             RuleTile.TilingRule rule = tile.m_TilingRules[index];
    111.  
    112.             float yPos = rect.yMin + 2f;
    113.             float height = rect.height - k_PaddingBetweenRules;
    114.             float matrixWidth = k_DefaultElementHeight;
    115.  
    116.             Rect inspectorRect = new Rect(rect.xMin, yPos, rect.width - matrixWidth * 2f - 20f, height);
    117.             Rect matrixRect = new Rect(rect.xMax - matrixWidth * 2f - 10f, yPos, matrixWidth, k_DefaultElementHeight);
    118.             Rect spriteRect = new Rect(rect.xMax - matrixWidth - 5f, yPos, matrixWidth, k_DefaultElementHeight);
    119.  
    120.             EditorGUI.BeginChangeCheck();
    121.             RuleInspectorOnGUI(inspectorRect, rule);
    122.             RuleMatrixOnGUI(tile, matrixRect, rule);
    123.             SpriteOnGUI(spriteRect, rule);
    124.             if (EditorGUI.EndChangeCheck())
    125.                 SaveTile();
    126.         }
    127.  
    128.         private void OnAddElement(ReorderableList list)
    129.         {
    130.             RuleTile.TilingRule rule = new RuleTile.TilingRule();
    131.             rule.m_Output = RuleTile.TilingRule.OutputSprite.Single;
    132.             rule.m_Sprites[0] = tile.m_DefaultSprite;
    133.             rule.m_GameObject = tile.m_DefaultGameObject;
    134.             rule.m_ColliderType = tile.m_DefaultColliderType;
    135.             tile.m_TilingRules.Add(rule);
    136.         }
    137.  
    138.         private void SaveTile()
    139.         {
    140.             EditorUtility.SetDirty(target);
    141.             SceneView.RepaintAll();
    142.  
    143.             UpdateOverrideTiles();
    144.         }
    145.  
    146.         private void UpdateOverrideTiles()
    147.         {
    148.             string[] overrideTileGuids = AssetDatabase.FindAssets("t:RuleOverrideTile");
    149.             foreach (string overrideTileGuid in overrideTileGuids)
    150.             {
    151.                 string overrideTilePath = AssetDatabase.GUIDToAssetPath(overrideTileGuid);
    152.                 RuleOverrideTile overrideTile = AssetDatabase.LoadAssetAtPath<RuleOverrideTile>(overrideTilePath);
    153.                 if (overrideTile.m_Tile == target)
    154.                     overrideTile.Override();
    155.             }
    156.         }
    157.  
    158.         private void OnDrawHeader(Rect rect)
    159.         {
    160.             GUI.Label(rect, "Tiling Rules");
    161.         }
    162.  
    163.         public override void OnInspectorGUI()
    164.         {
    165.             EditorGUI.BeginChangeCheck();
    166.             tile.m_DefaultSprite = EditorGUILayout.ObjectField("Default Sprite", tile.m_DefaultSprite, typeof(Sprite), false) as Sprite;
    167.             tile.m_CheckAllTilemaps = EditorGUILayout.Toggle("Check All Tile Maps", tile.m_CheckAllTilemaps);
    168.             tile.m_DefaultGameObject = EditorGUILayout.ObjectField("Default Game Object", tile.m_DefaultGameObject, typeof(GameObject), false) as GameObject;
    169.             tile.m_DefaultColliderType = (Tile.ColliderType)EditorGUILayout.EnumPopup("Default Collider", tile.m_DefaultColliderType);
    170.             if (EditorGUI.EndChangeCheck())
    171.                 EditorUtility.SetDirty(tile);
    172.  
    173.             serializedObject.Update();
    174.             EditorGUI.BeginChangeCheck();
    175.             var baseFields = typeof(RuleTile).GetFields().Select(field => field.Name);
    176.             var fields = target.GetType().GetFields().Select(field => field.Name).Where(field => !baseFields.Contains(field));
    177.             foreach (var field in fields)
    178.                 EditorGUILayout.PropertyField(serializedObject.FindProperty(field), true);
    179.             if (EditorGUI.EndChangeCheck())
    180.                 serializedObject.ApplyModifiedProperties();
    181.  
    182.             EditorGUILayout.Space();
    183.  
    184.             if (m_ReorderableList != null && tile.m_TilingRules != null)
    185.                 m_ReorderableList.DoLayoutList();
    186.         }
    187.  
    188.         internal virtual void RuleOnGUI(Rect rect, int arrowIndex, int neighbor)
    189.         {
    190.             switch (neighbor)
    191.             {
    192.                 case RuleTile.TilingRule.Neighbor.DontCare:
    193.                     break;
    194.                 case RuleTile.TilingRule.Neighbor.This:
    195.                     GUI.DrawTexture(rect, arrows[arrowIndex]);
    196.                     break;
    197.                 case RuleTile.TilingRule.Neighbor.NotThis:
    198.                     GUI.DrawTexture(rect, arrows[9]);
    199.                     break;
    200.                 default:
    201.                     var style = new GUIStyle();
    202.                     style.alignment = TextAnchor.MiddleCenter;
    203.                     style.fontSize = 10;
    204.                     GUI.Label(rect, neighbor.ToString(), style);
    205.                     break;
    206.             }
    207.             var allConsts = tile.m_NeighborType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy);
    208.             foreach (var c in allConsts)
    209.             {
    210.                 if ((int)c.GetValue(null) == neighbor)
    211.                 {
    212.                     GUI.Label(rect, new GUIContent("", c.Name));
    213.                     break;
    214.                 }
    215.             }
    216.         }
    217.  
    218.         internal virtual void RuleTransformOnGUI(Rect rect, RuleTile.TilingRule.Transform ruleTransform)
    219.         {
    220.             switch (ruleTransform)
    221.             {
    222.                 case RuleTile.TilingRule.Transform.Rotated:
    223.                     GUI.DrawTexture(rect, autoTransforms[0]);
    224.                     break;
    225.                 case RuleTile.TilingRule.Transform.MirrorX:
    226.                     GUI.DrawTexture(rect, autoTransforms[1]);
    227.                     break;
    228.                 case RuleTile.TilingRule.Transform.MirrorY:
    229.                     GUI.DrawTexture(rect, autoTransforms[2]);
    230.                     break;
    231.             }
    232.         }
    233.  
    234.         internal void RuleNeighborUpdate(Rect rect, RuleTile.TilingRule tilingRule, int index)
    235.         {
    236.             if (Event.current.type == EventType.MouseDown && ContainsMousePosition(rect))
    237.             {
    238.                 var allConsts = tile.m_NeighborType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
    239.                 var neighbors = allConsts.Select(c => (int)c.GetValue(null)).ToList();
    240.                 neighbors.Sort();
    241.  
    242.                 int oldIndex = neighbors.IndexOf(tilingRule.m_Neighbors[index]);
    243.                 int newIndex = (int)Mathf.Repeat(oldIndex + GetMouseChange(), neighbors.Count);
    244.                 tilingRule.m_Neighbors[index] = neighbors[newIndex];
    245.                 GUI.changed = true;
    246.                 Event.current.Use();
    247.             }
    248.         }
    249.  
    250.         internal void RuleTransformUpdate(Rect rect, RuleTile.TilingRule tilingRule)
    251.         {
    252.             if (Event.current.type == EventType.MouseDown && ContainsMousePosition(rect))
    253.             {
    254.                 tilingRule.m_RuleTransform = (RuleTile.TilingRule.Transform)(int)Mathf.Repeat((int)tilingRule.m_RuleTransform + GetMouseChange(), 4);
    255.                 GUI.changed = true;
    256.                 Event.current.Use();
    257.             }
    258.         }
    259.  
    260.         internal virtual bool ContainsMousePosition(Rect rect)
    261.         {
    262.             return rect.Contains(Event.current.mousePosition);
    263.         }
    264.  
    265.         private static int GetMouseChange()
    266.         {
    267.             return Event.current.button == 1 ? -1 : 1;
    268.         }
    269.  
    270.         internal virtual void RuleMatrixOnGUI(RuleTile tile, Rect rect, RuleTile.TilingRule tilingRule)
    271.         {
    272.             Handles.color = EditorGUIUtility.isProSkin ? new Color(1f, 1f, 1f, 0.2f) : new Color(0f, 0f, 0f, 0.2f);
    273.             int index = 0;
    274.             float w = rect.width / 3f;
    275.             float h = rect.height / 3f;
    276.  
    277.             for (int y = 0; y <= 3; y++)
    278.             {
    279.                 float top = rect.yMin + y * h;
    280.                 Handles.DrawLine(new Vector3(rect.xMin, top), new Vector3(rect.xMax, top));
    281.             }
    282.             for (int x = 0; x <= 3; x++)
    283.             {
    284.                 float left = rect.xMin + x * w;
    285.                 Handles.DrawLine(new Vector3(left, rect.yMin), new Vector3(left, rect.yMax));
    286.             }
    287.             Handles.color = Color.white;
    288.  
    289.             for (int y = 0; y <= 2; y++)
    290.             {
    291.                 for (int x = 0; x <= 2; x++)
    292.                 {
    293.                     Rect r = new Rect(rect.xMin + x * w, rect.yMin + y * h, w - 1, h - 1);
    294.                     if (x != 1 || y != 1)
    295.                     {
    296.                         RuleOnGUI(r, y * 3 + x, tilingRule.m_Neighbors[index]);
    297.                         RuleNeighborUpdate(r, tilingRule, index);
    298.  
    299.                         index++;
    300.                     }
    301.                     else
    302.                     {
    303.                         RuleTransformOnGUI(r, tilingRule.m_RuleTransform);
    304.                         RuleTransformUpdate(r, tilingRule);
    305.                     }
    306.                 }
    307.             }
    308.         }
    309.  
    310.         internal static void SpriteOnGUI(Rect rect, RuleTile.TilingRule tilingRule)
    311.         {
    312.             tilingRule.m_Sprites[0] = EditorGUI.ObjectField(new Rect(rect.xMax - rect.height, rect.yMin, rect.height, rect.height), tilingRule.m_Sprites[0], typeof(Sprite), false) as Sprite;
    313.         }
    314.  
    315.         internal static void RuleInspectorOnGUI(Rect rect, RuleTile.TilingRule tilingRule)
    316.         {
    317.             float y = rect.yMin;
    318.             EditorGUI.BeginChangeCheck();
    319.             GUI.Label(new Rect(rect.xMin, y, k_LabelWidth, k_SingleLineHeight), "Rule");
    320.             tilingRule.m_RuleTransform = (RuleTile.TilingRule.Transform)EditorGUI.EnumPopup(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), tilingRule.m_RuleTransform);
    321.             y += k_SingleLineHeight;
    322.             GUI.Label(new Rect(rect.xMin, y, k_LabelWidth, k_SingleLineHeight), "Game Object");
    323.             tilingRule.m_GameObject = (GameObject)EditorGUI.ObjectField(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), "", tilingRule.m_GameObject, typeof(GameObject), false);
    324.             y += k_SingleLineHeight;
    325.             GUI.Label(new Rect(rect.xMin, y, k_LabelWidth, k_SingleLineHeight), "Collider");
    326.             tilingRule.m_ColliderType = (Tile.ColliderType)EditorGUI.EnumPopup(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), tilingRule.m_ColliderType);
    327.             y += k_SingleLineHeight;
    328.             GUI.Label(new Rect(rect.xMin, y, k_LabelWidth, k_SingleLineHeight), "Output");
    329.             tilingRule.m_Output = (RuleTile.TilingRule.OutputSprite)EditorGUI.EnumPopup(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), tilingRule.m_Output);
    330.             y += k_SingleLineHeight;
    331.  
    332.             if (tilingRule.m_Output == RuleTile.TilingRule.OutputSprite.Animation)
    333.             {
    334.                 GUI.Label(new Rect(rect.xMin, y, k_LabelWidth, k_SingleLineHeight), "Speed");
    335.                 tilingRule.m_AnimationSpeed = EditorGUI.FloatField(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), tilingRule.m_AnimationSpeed);
    336.                 y += k_SingleLineHeight;
    337.             }
    338.             if (tilingRule.m_Output == RuleTile.TilingRule.OutputSprite.Random)
    339.             {
    340.                 GUI.Label(new Rect(rect.xMin, y, k_LabelWidth, k_SingleLineHeight), "Noise");
    341.                 tilingRule.m_PerlinScale = EditorGUI.Slider(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), tilingRule.m_PerlinScale, 0.001f, 0.999f);
    342.                 y += k_SingleLineHeight;
    343.  
    344.                 GUI.Label(new Rect(rect.xMin, y, k_LabelWidth, k_SingleLineHeight), "Shuffle");
    345.                 tilingRule.m_RandomTransform = (RuleTile.TilingRule.Transform)EditorGUI.EnumPopup(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), tilingRule.m_RandomTransform);
    346.                 y += k_SingleLineHeight;
    347.             }
    348.  
    349.             if (tilingRule.m_Output != RuleTile.TilingRule.OutputSprite.Single)
    350.             {
    351.                 GUI.Label(new Rect(rect.xMin, y, k_LabelWidth, k_SingleLineHeight), "Size");
    352.                 EditorGUI.BeginChangeCheck();
    353.                 int newLength = EditorGUI.DelayedIntField(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), tilingRule.m_Sprites.Length);
    354.                 if (EditorGUI.EndChangeCheck())
    355.                     Array.Resize(ref tilingRule.m_Sprites, Math.Max(newLength, 1));
    356.                 y += k_SingleLineHeight;
    357.  
    358.                 for (int i = 0; i < tilingRule.m_Sprites.Length; i++)
    359.                 {
    360.                     tilingRule.m_Sprites[i] = EditorGUI.ObjectField(new Rect(rect.xMin + k_LabelWidth, y, rect.width - k_LabelWidth, k_SingleLineHeight), tilingRule.m_Sprites[i], typeof(Sprite), false) as Sprite;
    361.                     y += k_SingleLineHeight;
    362.                 }
    363.             }
    364.         }
    365.  
    366.         public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
    367.         {
    368.             if (tile.m_DefaultSprite != null)
    369.             {
    370.                 Type t = GetType("UnityEditor.SpriteUtility");
    371.                 if (t != null)
    372.                 {
    373.                     MethodInfo method = t.GetMethod("RenderStaticPreview", new Type[] { typeof(Sprite), typeof(Color), typeof(int), typeof(int) });
    374.                     if (method != null)
    375.                     {
    376.                         object ret = method.Invoke("RenderStaticPreview", new object[] { tile.m_DefaultSprite, Color.white, width, height });
    377.                         if (ret is Texture2D)
    378.                             return ret as Texture2D;
    379.                     }
    380.                 }
    381.             }
    382.             return base.RenderStaticPreview(assetPath, subAssets, width, height);
    383.         }
    384.  
    385.         private static Type GetType(string TypeName)
    386.         {
    387.             var type = Type.GetType(TypeName);
    388.             if (type != null)
    389.                 return type;
    390.  
    391.             if (TypeName.Contains("."))
    392.             {
    393.                 var assemblyName = TypeName.Substring(0, TypeName.IndexOf('.'));
    394.                 var assembly = Assembly.Load(assemblyName);
    395.                 if (assembly == null)
    396.                     return null;
    397.                 type = assembly.GetType(TypeName);
    398.                 if (type != null)
    399.                     return type;
    400.             }
    401.  
    402.             var currentAssembly = Assembly.GetExecutingAssembly();
    403.             var referencedAssemblies = currentAssembly.GetReferencedAssemblies();
    404.             foreach (var assemblyName in referencedAssemblies)
    405.             {
    406.                 var assembly = Assembly.Load(assemblyName);
    407.                 if (assembly != null)
    408.                 {
    409.                     type = assembly.GetType(TypeName);
    410.                     if (type != null)
    411.                         return type;
    412.                 }
    413.             }
    414.             return null;
    415.         }
    416.  
    417.         private static Texture2D Base64ToTexture(string base64)
    418.         {
    419.             Texture2D t = new Texture2D(1, 1);
    420.             t.hideFlags = HideFlags.HideAndDontSave;
    421.             t.LoadImage(System.Convert.FromBase64String(base64));
    422.             return t;
    423.         }
    424.  
    425.         [Serializable]
    426.         class RuleTileRuleWrapper
    427.         {
    428.             [SerializeField]
    429.             public List<RuleTile.TilingRule> rules = new List<RuleTile.TilingRule>();
    430.         }
    431.  
    432.         [MenuItem("CONTEXT/RuleTile/Copy All Rules")]
    433.         private static void CopyAllRules(MenuCommand item)
    434.         {
    435.             RuleTile tile = item.context as RuleTile;
    436.             if (tile == null)
    437.                 return;
    438.  
    439.             RuleTileRuleWrapper rulesWrapper = new RuleTileRuleWrapper();
    440.             rulesWrapper.rules = tile.m_TilingRules;
    441.             var rulesJson = EditorJsonUtility.ToJson(rulesWrapper);
    442.             EditorGUIUtility.systemCopyBuffer = rulesJson;
    443.         }
    444.  
    445.         [MenuItem("CONTEXT/RuleTile/Paste Rules")]
    446.         private static void PasteRules(MenuCommand item)
    447.         {
    448.             RuleTile tile = item.context as RuleTile;
    449.             if (tile == null)
    450.                 return;
    451.  
    452.             try
    453.             {
    454.                 RuleTileRuleWrapper rulesWrapper = new RuleTileRuleWrapper();
    455.                 EditorJsonUtility.FromJsonOverwrite(EditorGUIUtility.systemCopyBuffer, rulesWrapper);
    456.                 tile.m_TilingRules.AddRange(rulesWrapper.rules);
    457.             }
    458.             catch (Exception)
    459.             {
    460.                 Debug.LogError("Unable to paste rules from system copy buffer");
    461.             }
    462.         }
    463.     }
    464. }
    465.  
     
  8. duhfux

    duhfux

    Joined:
    Jul 12, 2019
    Posts:
    1
    Hi, I tried your code out as I'm looking for a similar thing to OP but it tells me there is a problem with line 316 of RuleTile.cs


    foreach (Tilemap map in tileMaps)


    It says
    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. UnityEngine.RuleTile.GetMatchingNeighboringTiles (UnityEngine.Tilemaps.ITilemap tilemap, UnityEngine.Vector3Int position, UnityEngine.Tilemaps.TileBase[]& neighboringTiles) (at Packages/com.unity.2d.tilemap.extras/Runtime/Tiles/RuleTile/RuleTile.cs:316)
    3. UnityEngine.RuleTile.GetTileData (UnityEngine.Vector3Int position, UnityEngine.Tilemaps.ITilemap tilemap, UnityEngine.Tilemaps.TileData& tileData) (at Packages/com.unity.2d.tilemap.extras/Runtime/Tiles/RuleTile/RuleTile.cs:111)
    4.  
    and

    Code (CSharp):
    1. Error running GetTileData for new TileNullReferenceException: Object reference not set to an instance of an object
    2. UnityEngine.RuleTile.GetMatchingNeighboringTiles (UnityEngine.Tilemaps.ITilemap tilemap, UnityEngine.Vector3Int position, UnityEngine.Tilemaps.TileBase[]& neighboringTiles) (at Packages/com.unity.2d.tilemap.extras/Runtime/Tiles/RuleTile/RuleTile.cs:316)
    3. UnityEngine.RuleTile.GetTileData (UnityEngine.Vector3Int position, UnityEngine.Tilemaps.ITilemap tilemap, UnityEngine.Tilemaps.TileData& tileData) (at Packages/com.unity.2d.tilemap.extras/Runtime/Tiles/RuleTile/RuleTile.cs:111)
    4.  
    Any idea why?

    Cheers
     
  9. Oziry

    Oziry

    Joined:
    Dec 27, 2016
    Posts:
    1
    Inside RuleTile.cs

    Code (CSharp):
    1.         /// <summary>
    2.         /// Checks if there is a match given the neighbor matching rule and a Tile.
    3.         /// </summary>
    4.         /// <param name="neighbor">Neighbor matching rule.</param>
    5.         /// <param name="other">Tile to match.</param>
    6.         /// <returns>True if there is a match, False if not.</returns>
    7.         public virtual bool RuleMatch(int neighbor, TileBase other)
    8.         {
    9.             if (other is RuleOverrideTile)
    10.                 other = (other as RuleOverrideTile).m_InstanceTile;
    11.  
    12.             switch (neighbor)
    13.             {
    14.                 case TilingRule.Neighbor.This: return other == this;
    15.                 case TilingRule.Neighbor.NotThis: return other != this;
    16.             }
    17.             return true;
    18.         }
    change line
    Code (CSharp):
    1.    case TilingRule.Neighbor.This: return other == this;
    to
    Code (CSharp):
    1.    case TilingRule.Neighbor.This: return !(other is null);
    And you will by able to use multiple sets of tiles on same grid
     
    Last edited: Dec 2, 2020
    HuldaGnodima likes this.
  10. Mazemace

    Mazemace

    Joined:
    Jan 4, 2013
    Posts:
    7
    You should change both lines, otherwise it will still check for the same tile when the 'NotThis' case is used (when the X is used when defining Rule Tiles)

    Code (CSharp):
    1.     switch (neighbor)
    2.             {
    3.                 case TilingRule.Neighbor.This: return !(other is null); ;
    4.                 case TilingRule.Neighbor.NotThis: return (other is null);
    5.             }
    6.             return true;
     
    JohnSearle and HuldaGnodima like this.
  11. kukamed

    kukamed

    Joined:
    Jan 23, 2013
    Posts:
    14
    If you want just to connect two tiles with different type, I coded this extension. The type is defined by a string. Same string = same type if they both are ExtendedRuleTile.

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Tilemaps;
    4.  
    5. [CreateAssetMenu(fileName = "Extended Rule Tile", menuName = "2D/Tiles/Extended Rule Tile")]
    6. public class ExtendedRuleTile : RuleTile
    7. {
    8.  
    9.     public string type;
    10.     public override bool RuleMatch(int neighbor, TileBase other)
    11.     {
    12.         if (other is RuleOverrideTile)
    13.             other = (other as RuleOverrideTile).m_InstanceTile;
    14.        
    15.         ExtendedRuleTile otherTile = other as ExtendedRuleTile;
    16.        
    17.         if (otherTile == null)
    18.             return base.RuleMatch(neighbor, other);
    19.  
    20.         switch (neighbor)
    21.         {
    22.             case TilingRule.Neighbor.This: return type == otherTile.type ;
    23.             case TilingRule.Neighbor.NotThis: return type != otherTile.type;
    24.         }
    25.         return true;
    26.  
    27.     }
    28. }
     
  12. Austinkht

    Austinkht

    Joined:
    Mar 17, 2020
    Posts:
    1
    Bro this works so well you're a god. For those who don't understand just create a new script named ExtendedRuleTile and right click -> create -> 2D -> tiles -> extended rule tile
     
    Kilroy4082 and kirbyderby2000 like this.
  13. csondika

    csondika

    Joined:
    Jul 14, 2018
    Posts:
    2
    Works perfectly! Just don't do the same mistake as me. If you're not using spaces in script names, delete the spaces in the attribute, too:

    [CreateAssetMenu(fileName = "ExtendedRuleTile", ...

    This script makes my life easier :)
     
    celechii likes this.
  14. TiTaTomte

    TiTaTomte

    Joined:
    Jun 9, 2016
    Posts:
    1
    Sorry for bringing this topic up again. I have found other solutions which basically are doing the same but nothing works reliable for me. Basically I need at least two different tile sets to interact with each other. The thing is: With the solution posted above, it works in one out of ten times. All the other times Unity just gives me an endless spinner and nothing more happens. That's what I did:
    - Used `ExtendedRuleTile` for two different kind of Tiles
    - Both have the same `type`
    - Tiles are placed through code, not editor

    Hope that someone has a solution for this.