4

I am creating a 2D platformer in Unity and I am using ruletiles from the "2D Extras" add-on. I am currently trying to let two ruletiles connect with each other even if they have a different texture. Here you can see an example for the Ruletiles which I want to connect. I want to connect the lighter and darker grass tiles. They are in two seperate ruletile files.

How can I manage this?

desertnaut
  • 57,590
  • 26
  • 140
  • 166
J. Kreller
  • 150
  • 1
  • 10
  • [This unity asset](https://assetstore.unity.com/packages/tools/sprite-management/advanced-rule-tiles-118786) (advanced rule tiles) has been around a long time. It does exactly what you're asking for and is as easy to use as the regular Rule-Tile from 2d-assets unlike hard-coding each tile to be able to detect dark grass, etc, this allows you to make several types of relations between any tile type. – BB AKA ZINN Dec 19 '19 at 11:41

4 Answers4

2

There are several ways you can approach this, dependent mostly on how you visually want to connect the tiles in question. The way I generically approached this problem was by altering the ruletile so that it makes a distinction between:

  • same tile type (this)
  • another tile type (not this)
  • an emtpy tile (empty)

I believe the implementation is fairly trivial and can be done by editing the original ruletile.cs in the followingplaces:

public enum Neighbor { DontCare, This, NotThis, Empty }

And most importantly here:

 // When this returns true, that means that the rule does not match
    // IE this is testing if any of the rules are broken
    // If the rule matches that means that the sprite associated to the rule will be the new main sprite
    // These are the rules which are being checked if they are broken
    // This -    should only be triggered for the same tile type,
    //           which is not the case when the tile is not this
    // NotThis - should only be triggered for another tile, which is not empty
    //           IE broken when it is this or null(empty)
    // Empty -   should only be triggered when there is no tile
    //           IE broken when it is this or not null(empty)
    private bool RuleBroken(TilingRule rule, int index, TileBase tile)
    {
        return (rule.m_Neighbors[index] == TilingRule.Neighbor.This && tile != this)
            || (rule.m_Neighbors[index] == TilingRule.Neighbor.NotThis && (tile == this || tile == null))
            || (rule.m_Neighbors[index] == TilingRule.Neighbor.Empty && (tile == this || tile != null))
            ;
    }

What remains is to make a change into the editor file so that you can actually toggle between 4 and not 3 enum states and then draw a graphic of choice in the grid.

If you want to use my changed version you can find the ruletile.cs and ruletileeditor.cs in this gist.

Note with this implementation your ruletile will need quite a lot more rules to describe each tile.

  • Thank you for your answer. I inspected your code and I understand your approach, but I am not sure how you ask if the neighbor is empty or not. How is this working? If you are okay with this I will use your code, but I also want to understand it nevertheless. – J. Kreller Jun 22 '18 at 20:49
  • Yeah feel free to use it. :) It took me a while to understand what is really happening the first time too. The way I understand the class in general is that every time the ruletile in the grid gets a refresh it will check what it needs to be by looping over each of the tilingrules to return the first one that actually matches your rules. In other words the first one which has not broken the rules. Which also means that the order of your tilingrules in the editor can be significant. The brokenrule function which was edited is used to find a situation where the rule does not match. – Alexander van der Zalm Jun 24 '18 at 14:41
  • The brokenrule function itself checks each of the neighbors in its tilingruleset (the editor tilingrule) for the corresponding neighbor. If the tile (which is the actual neighbor) is breaking the rule, then it returns true and the rule is broken, making the function look for the next tile. For the actual breaking of the rules see the comments. Hope it helps. – Alexander van der Zalm Jun 24 '18 at 14:46
1

A contributor to the 2d Tilemap Extras included a selective override of a rule tile.

Examples can be found here:

https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@1.6/manual/CustomRulesForRuleTile.html

In particular is Siblings Tiles 1.

This creates a new Rule Tile that functions identical to a rule tile except that it also includes rule tiles that are also in the expandable list.

I didn't want to go through my million and a half rule tiles and change all the rules from the green arrow "This" to the #3 "Sibling" so I just used it to override "This" to include "Sibling".

public class SiblingTile : RuleTile<SiblingTile> {
    public List<TileBase> Siblings = new List<TileBase>();
    
    public override bool RuleMatch(int neighbor, TileBase tile) {
        // Direct override of rule tile's "this" check with an inclusion of those in Siblings list.
        switch (neighbor) {
            case TilingRuleOutput.Neighbor.This: 
                return tile == this || Siblings.Contains(tile);
        }
        return base.RuleMatch(neighbor, tile);
    }
}

Really nice and simple.

Kmsxkuse
  • 11
  • 1
0

I had the same problem but I saw the package code has been updated, so I applied the same solution to the last version of the package (for Unity 2019.2.0f1). I've put it here in case anyone needs it: https://github.com/Scorpin/RuleTiles-differentiating-between-another-tile-and-empty

Most relevant parts of the added code:

In RuleTile.cs added the new type of neighbor, at line 57 (class Neighbor):

public const int Empty = 3;

And how manage it (more basic implementation of RuleMatch):

Change at 413 to:

case TilingRule.Neighbor.NotThis: return (tile != m_Self && tile != null);

And adding after:

case TilingRule.Neighbor.Empty: return tile == null;

In RuleTileEditor.cs, added the icon at line 28:

private const string s_OtherTile = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAABNmlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjarY6xSsNQFEDPi6LiUCsEcXB4kygotupgxqQtRRCs1SHJ1qShSmkSXl7VfoSjWwcXd7/AyVFwUPwC/0Bx6uAQIYODCJ7p3MPlcsGo2HWnYZRhEGvVbjrS9Xw5+8QMUwDQCbPUbrUOAOIkjvjB5ysC4HnTrjsN/sZ8mCoNTIDtbpSFICpA/0KnGsQYMIN+qkHcAaY6addAPAClXu4vQCnI/Q0oKdfzQXwAZs/1fDDmADPIfQUwdXSpAWpJOlJnvVMtq5ZlSbubBJE8HmU6GmRyPw4TlSaqo6MukP8HwGK+2G46cq1qWXvr/DOu58vc3o8QgFh6LFpBOFTn3yqMnd/n4sZ4GQ5vYXpStN0ruNmAheuirVahvAX34y/Axk/96FpPYgAAACBjSFJNAAB6JQAAgIMAAPn/AACA6AAAUggAARVYAAA6lwAAF2/XWh+QAAAAdElEQVR42qyTOxKAIAxEWcbCK1B5/2NZcQW7ZwMNMCROTMM3S/YBAlIkjt3iJT29f8P5SUASdRgDGvdlK7m0trZ5U2BMBrQTySkYEwNA/ZSV56li6xpXltwWrGQ3g7KxE4boYrCDGa7ABXH1An+zoOh3fgcAkVNC6ZGUg/8AAAAASUVORK5CYII=";

Added to the array of textures at 47 (and changed the number of items of the array to 11 in the line 37):

s_Arrows[10] = Base64ToTexture(s_OtherTile);

And to finish, moved the "NotThis" GUI to the item 10 in the array, so we can add the "Empty" and use for it the icon number 9, at line 198, like this:

case RuleTile.TilingRule.Neighbor.NotThis:
     GUI.DrawTexture(rect, arrows[10]);
     break;
case RuleTile.TilingRule.Neighbor.Empty:
     GUI.DrawTexture(rect, arrows[9]);
     break;

And that's all :)

Thank you very much for your solution Alexander :)

Scorpin
  • 1
  • 2
0

I used Kmsxkuse's solution but I had to modify it a bit because it wasn't working for me.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
using static UnityEngine.RuleTile.TilingRuleOutput;

namespace Assets.Scripts.Tiles
{
    [CreateAssetMenu]
    public class PathHighlightTile : IsometricRuleTile<Neighbor>
    {
        public List<TileBase> Siblings = new List<TileBase>();

        public override bool RuleMatch(int neighbor, TileBase other)
        {
            switch (neighbor)
            {
                case Neighbor.This:
                    {
                        return other == this || Siblings.Contains(other);
                    }
                case Neighbor.NotThis:
                    {
                        return other != this && !Siblings.Contains(other);
                    }
            }

            return base.RuleMatch(neighbor, other);
        }
    }
}
Remoba
  • 147
  • 1
  • 8