2d-extras
2d-extras copied to clipboard
Is there a way to get information about neighbor tiles (for rule tiles)?
What I have:
What I want (notice the grey inner tiles on the edges):
I want to add another rule to have more specific inner tiles, but as far as I can tell you can't really get information about the neighbor tile's neighbors. Is there any way to do what I'm looking for in Unity?
Managed to somewhat figure it out, but I doubt it'll be performant:
First we grab the neighbor tiles' neighbors:
private void SetMatchingNeighboringTiles(ITilemap tilemap, Vector3Int position, TileBase baseTile, int xIndex)
{
if (baseTile == null) {
return;
}
neighboringNeighbors[xIndex] = new TileBase[NeighborCount];
int index = 0;
for (int y = 1; y >= -1; y--)
{
for (int x = -1; x <= 1; x++)
{
if (x != 0 || y != 0)
{
Vector3Int tilePosition = new Vector3Int(position.x + x, position.y + y, position.z);
neighboringNeighbors[xIndex][index] = tilemap.GetTile(tilePosition);
index++;
}
}
}
}
Then we check against them during rule checks:
public virtual bool RuleMatch(int neighbor, TileBase tile, int index)
{
switch (neighbor)
{
case TilingRule.Neighbor.This: return tile == m_Self;
case TilingRule.Neighbor.NotThis: return tile != m_Self;
case TilingRule.Neighbor.SkipATile:
TileBase[] baseTileArray = neighboringNeighbors[index];
TileBase baseTile = baseTileArray[index];
return baseTile != m_Self;
}
return true;
}
This doesn't work well on corners, as you can see here:

The only real way to do it is to build out a UI to select neighboring sprites to use as rules, which I feel like would take forever. Anyone have a better idea?
This seems to be a problem with the rule setting, as far as I know it doesn't even need to change the code.
For the lack of corners, add the rules in the picture to fill it.

That won't work - The tiles I'm talking about are surrounded on all sides.
What do you want to do is the dark part of this picture?

Yes - Using a single rule tile for the entire set.
I made a light part, the same as the dark part.
Maybe you don't need to be so complicated, but you can refer to it.

That rule tile doesn't have any of your dark tiles on it.
Sorry, I misunderstood what you mean. You must currently implement it using two RuleTile.
Just a suggestion, if draw the gray edge on the outer tile, the whole thing will be very simple.
Hi, did johnsoncodehk's suggestions help?
No, they didn't.
It sounds like you need a RuleTile which takes into consideration tiles which are two cells away or if a neighboring tile is an edge.
Yes - Is there any way to do that through the API (specifically the edge part). The two cells away is something I wrote but doesn't really work very well for some edge cases. I can live with two rule tiles, but it's simply not possible for the two tiles to interact with one another (in a way where it knows facts about the other tile's rules).
For example, here's what it looks like with two rule tiles:

The issue is that the outer side is assuming it's a single row, when it should be thinking it's just the edge.
If I change the following line:
case TilingRule.Neighbor.This: return tile == m_Self;
to:
case TilingRule.Neighbor.This: return tile == m_Self || tile != null;
I get behavior that I can somewhat work around (using two rule tiles). It'd still be really nice to be able to encapsulate all of this inside a single rule tile.
Hey you can use Override Rule Tile to fix the problem with this problem
Yes - Is there any way to do that through the API (specifically the edge part). The two cells away is something I wrote but doesn't really work very well for some edge cases. I can live with two rule tiles, but it's simply not possible for the two tiles to interact with one another (in a way where it knows facts about the other tile's rules).
For example, here's what it looks like with two rule tiles:
The issue is that the outer side is assuming it's a single row, when it should be thinking it's just the edge.
Has there been any update to this?
I'm creating a rule where the tile's sprite changes depending on what the neighbours tile positions have on another tilemap so I'm in need of getting the neighboring tile position inside of RuleMatch.
I also have this issue and was hoping there'd be a solution out there. I'm surprised to see there isn't. It seems like a pretty common thing to do. My current workflow involves two rule tiles to get this effect, which is very tedious as it requires me to "repaint" all the interiors of the tilemap with the darker shade.
I ended up getting a working solution. But it required writing a lot of conditionals (neighbor Sprite checks) - there were quite a few edge cases. I won't share the code since it's very ugly (maybe later I'll clean it up), but I can post some details here when/if I get time.
Unity's recent "Extend Neighbors" rule tile allows you to check multiple tiles over ("2 away" or more), which effectively allows us to fix this issue.
I went ahead and made the rules and got it working. It's tedious, as I needed to duplicate the same tile result under multiple different rules (often 5 sets of rules to catch all the edge cases for a single sprite - which I needed to do 4 times for each wall, and then a few more times for corners).
I think it would be more elegant if the rules included a way of doing "or" style checks (similar to what is found in the "Advanced Rule Tile" asset on the asset store - which currently only supports 3x3 Rule Tiles, but it at least has the concept of an "or" rule which I think would make this current process less tedious).
Regardless, the issue should be resolved now.
@chalmersgit Nice hear that it is useful to you, but it is not new, it is a feature from one year ago XD: https://github.com/Unity-Technologies/2d-extras/pull/140
Maybe you can try Sibling Rule Tile from here: https://github.com/johnsoncodehk/rule-tile-extras
It is a simple but supports Extend Neighbors implementation.
@johnsoncodehk yeah true haha. Just felt kinda new given the age of this thread and how recent I found it :p
Yeah I tried that siblings one. It has an issue (at least I couldn't figure out) that stopped me from using it. The problem is that the "sibling" is a TileBase type. But when you draw from your palette, the tiles I believe are from the rule type type, not specific to the tile you selected in the sibling GUI.
So the sibling rule only worked if I drag+dropped that specific tile into the palette. So effectively it was useless.
In concept, if the siblings script worked as I had hoped, it would fix this issue. Please let me know if this is possible. That would save a lot of time.
@chalmersgit this is a design limitations, some related discussion here: https://github.com/Unity-Technologies/2d-extras/issues/67#issuecomment-680804320
I really want to solve it, but it is "close source". :(
@johnsoncodehk ah ok. Thanks for sharing.
I wonder what the intended use case for the sibling was then.
To be clear, in referring to "siblings 1": https://docs.unity3d.com/Packages/[email protected]/manual/CustomRulesForRuleTile.html
Siblings 2 makes complete sense (making two sets of rule tiles interact with one another), but siblings 1 doesn't practically work with rules+palette.
@chalmersgit this is no a good example from here... https://github.com/Unity-Technologies/2d-extras/pull/38
the problem is, we know RuleTile can only refresh by RuleTile, so we should do:
public class MyTile : RuleTile<MyTile.Neighbor> {
public List<RuleTile> sibings = new List<RuleTile>(); // <-- not allow TileBase
public class Neighbor : RuleTile.TilingRule.Neighbor {
public const int Sibing = 3;
}
public override bool RuleMatch(int neighbor, TileBase other) {
// should support RuleOverrideTile
if (other is RuleOverrideTile) other = (other as RuleOverrideTile).m_InstanceTile;
switch (neighbor) {
case Neighbor.Sibing: return sibings.Contains(other);
}
return base.RuleMatch(neighbor, other);
}
}
@johnsoncodehk interesting. I'll check it this evening and report back.
@chalmersgit You need to add the two Siblings Tile 1 to each other to get the same behavior as Siblings Tile 2, so yes it is not practical, just for as an example.
@johnsoncodehk Just a minor fix to make your code compile:
public class MyTile: RuleTile<MyTile.Neighbor>
{
public List<RuleTile> sibings = new List<RuleTile>();
public class Neighbor : RuleTile.TilingRule.Neighbor
{
public const int Sibing = 3;
}
public override bool RuleMatch(int neighbor, TileBase other)
{
// should support RuleOverrideTile
if (other is RuleOverrideTile) other = (other as RuleOverrideTile).m_InstanceTile;
RuleTile otherRuleTile = other as RuleTile; // <-- Need to cast it to make it compatible with siblings list
switch (neighbor)
{
case Neighbor.Sibing: return sibings.Contains(otherRuleTile);
}
return base.RuleMatch(neighbor, other);
}
}
But regardless, the list is of type RuleTile, which means it only lists entries of entire rule sets (whereas before when it was a list of type TileBase, it would at least allow me to select the specific tile I want to react to (e.g., a wall tile)).
If you have incompatible rules, say for example if your fill tile does not have all 8 connections as green arrows, inner corners will not work. But if you properly set up that fill tile it will work! :) Make sure your fill tile has 8 green arrows :)