Comments (34)
I ran into exactly the same problem. Your example scripts have helped me a lot! I found that @ElnuDev 's implementation is close, but had a few bugs when you're using both positive and negative neighbor rules. Here's my code, based on yours, fixing this behavior:
using System.Collections.Generic;
using UnityEngine.Tilemaps;
namespace UnityEngine
{
[CreateAssetMenu(fileName = "New Sibling Rule Tile", menuName = "Tiles/Sibling Rule Tile")]
public class SiblingRuleTile : UnityEngine.RuleTile
{
public List<TileBase> siblings;
public override bool RuleMatch(int neighbor, TileBase other)
{
switch(neighbor)
{
case UnityEngine.RuleTile.TilingRule.Neighbor.This:
return (siblings.Contains(other)
|| base.RuleMatch(neighbor, other));
case UnityEngine.RuleTile.TilingRule.Neighbor.NotThis:
return (!siblings.Contains(other)
&& base.RuleMatch(neighbor, other));
}
return base.RuleMatch(neighbor, other);
}
}
}
from 2d-extras.
@Krummelz For your case, this is an example of inheriting the RuleTile implementation:
[CreateAssetMenu]
public class MyTile : RuleTile {
public bool isWall;
public override bool RuleMatch(int neighbor, TileBase other) {
if (neighbor == RuleTile.TilingRule.Neighbor.This)
if (other is MyTile)
if (this.isWall && (other as MyTile).isWall)
return true;
return base.RuleMatch(neighbor, other);
}
}
from 2d-extras.
Thanks for pointing this out! It could be possible that the Tilemap has its own callback method for refreshing Tiles without the RefreshTile override that implements this for all types of Tiles (or some other way if needed) instead of the default radius. We would definitely check this out!
from 2d-extras.
I haven't used the RuleOverrideTile yet, but RuleTiles in general only look for copies of the same tile. It sounds like you are using two tiles that implement the same ruleset. Let's call them tile A and B. A has walls when Up and Down neighbors are matched, B has a window when they are matched.
Tilemap looks like this:
A A A
A B A
A A A
The thing is, as far as B is concerned, it doesn't have neighbors, because there are no tiles of B around it. So you'll get the Sprite for no matching neighbors on B, and "edges" from the neighboring A tiles.
Does that make sense? Rule Override Tile is designed to reuse rulesets on different tiles, not for alternate sprites within a ruleset.
For our project, we added what we call "Siblings" to the rule tile. It's a list of Tiles it considers as a match. A and B would be siblings, and you'd get the desired effect.
from 2d-extras.
You're right, that is what I'm doing, and what you say makes sense. Your solution sounds like what I need.. Would you be willing to share the code?
from 2d-extras.
from 2d-extras.
Here's a quick modification to RuleTile to do what I'm saying. You should be cautious about diverging from this repository too much, as it can make it hard to update. And this is actually pretty tricky to make work with Override tiles as it's a bit unclear how they should behave, so it only works for the base rule tile right now.
This required changes to both the RuleTile and RuleTileEditor scripts.
from 2d-extras.
I have added a quick and hopefully correct example of Rule Override Tile and Custom Rule that may help with this at 2d-techdemos:
Unity-Technologies/2d-techdemos@e10431a
I will check out RuleTileWithSiblings as well!
from 2d-extras.
@ChuanXin-Unity Thank you for this example, this is look good!
I found that comparing the methods of RuleTile and RuleOverrideTile is a bit complicated. I tried to add the m_OverrideSelf field to RuleOverrideTile to handle different connection situations.
I am not sure if this is better because it may become more difficult to understand. Can you look at it?
PR: #70
from 2d-extras.
Here's my script! It works like a charm. Based on @johnsoncodehk's script:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
[CreateAssetMenu(fileName = "New Sibling Rule Tile", menuName = "Tiles/Sibling Rule Tile")]
public class SiblingRuleTile : RuleTile
{
public List<TileBase> siblings;
public override bool RuleMatch(int neighbor, TileBase other)
{
if (siblings.Contains(other))
{
return true;
}
return base.RuleMatch(neighbor, other);
}
}
I hope this gets added into 2d-extras, it is a much needed feature!
from 2d-extras.
Hey, I know this post is old but I had some questions (I used the example script in the wiki):
- How to change the icon in the inspector from 3 to some sprite like the default arrow or X
- When I drew, the tile update was slow and noticeable. How to make it faster and update instantly?
from 2d-extras.
-
You would need to write a custom editor for your custom rule tile and override the following method below:
/// <summary> /// Draws a neighbor matching rule /// </summary> /// <param name="rect">Rect to draw on</param> /// <param name="position">The relative position of the arrow from the center</param> /// <param name="neighbor">The index to the neighbor matching criteria</param> public virtual void RuleOnGUI(Rect rect, Vector3Int position, int neighbor) { switch (neighbor) { case RuleTile.TilingRule.Neighbor.This: GUI.DrawTexture(rect, arrows[GetArrowIndex(position)]); break; case RuleTile.TilingRule.Neighbor.NotThis: GUI.DrawTexture(rect, arrows[9]); break; default: var style = new GUIStyle(); style.alignment = TextAnchor.MiddleCenter; style.fontSize = 10; GUI.Label(rect, neighbor.ToString(), style); break; } }
What you currently see is the default option in the switch, where a label with selected rule is printed. You can change it to an icon you have created by adding a new case with GUI.DrawTexture
.
- I am not certain what causes the slowness for your update. If possible, could you use the Unity profiler, record your tile update in editor mode and post the profiler data log here? This will help to determine what is causing the slow down for you.
from 2d-extras.
@ChuanXin-Unity If I draw the custom tile everything is normal but if I draw the custom tile and then draw a normal tile (sibling tile to the custom one) it won't update until I hover my cursor to its position.
Here the file:
siblingTIle.zip
from 2d-extras.
@longtran2904 RuleTile refresh can only be trigger by RuleTiles (RuleTile, OverrideRuleTile, CustomRuleTile...), "trigger by any tile" need Tilemap to add support.
At now, you can only replace "NonRuleTile" with RuleTile to avoid this problem.
from 2d-extras.
@johnsoncodehk Most of the time, I would use RuleTile but in some special cases that the rules didn't apply, I had to use the normal tile (which messed up the rule tile so I created a custom sibling tile for it). So I had a custom rule tile and a normal tile. Are there any other ways?
from 2d-extras.
@longtran2904 If you want to use normal tile, you need to inherit the normal tile class and override RefreshTile() to update RuleTile. Compared to this method, it is much simpler to switch to RuleTile.
You can talk about special cases to see if I can help.
from 2d-extras.
@johnsoncodehk Does this problem only appear visually and when I in play mode or instantiate the tilemap from a prefab it will be disappeared? If it's true then I can live with it.
from 2d-extras.
All tiles will be refreshed once when entering playback mode. Therefore, if you do not change the Tilemap at runtime, this problem will not occur.
The same problem occurs if the normal tile is placed at runtime.
from 2d-extras.
@johnsoncodehk Thank you, I think I will just leave it there.
from 2d-extras.
@ChuanXin-Unity Maybe we can consider refresh affected tiles by Tilemap, or create RuleTilemap for this problem?
I think tiles provide all affected positions, and refresh by Tilemap internal is better, but Tilemap is not open source, I cannot work for that.
And the efficiency problem mentioned by #163 can also be solved.
from 2d-extras.
@johnsoncodehk Hey, when my game starts, it spawns some tilemaps (which have rule tiles) and then copies it into a shared tilemap so sometimes the tiles don't get updated again. How to call RefreshTile() for all the tiles through code?
from 2d-extras.
@longtran2904 You can use Tilemap.RefreshAllTiles, But if the tilemap size is relatively large, you need to consider performance.
from 2d-extras.
@johnsoncodehk Thank you! It worked. My game is composed of different rooms (which have different tilemaps) and I copy it at the start of the level only so it will be fine (max: 50,000 tiles).
from 2d-extras.
@johnsoncodehk How the bool RuleMatch(int neighbor, TileBase tile) work? How the parameter neighbor gets passed? I want a rule which has a sibling tile in it and do I need to make another case for the switch (Neighbor.Sibling) or Neighbor.This is ok?
from 2d-extras.
@longtran2904 Neighbor.Sibling
and Neighbor.This
is also work. If you use Neighbor.This
, this is a very similar script: ExampleSiblingRuleTile.cs
And sorry, I can't use my English to say how RuleMatch() work...
from 2d-extras.
@longtran2904 If the neighbor is a sibling then will the int neighbor equal to 1 or 3? If it is 3 then how the case Neighbor.This work (This == 1)? (I used the example script sibling 1 in the 2d-extras wiki)
from 2d-extras.
@longtran2904 Neighbor.Sibling
Is 3, Neighbor.This
is 1 and progress in base.RuleMatch(neighbor, tile)
from 2d-extras.
@johnsoncodehk I knew that, but when the RuleMatch gets called if the tile gets checked is a sibling then the int neighbor parameter is 1 or 3?
from 2d-extras.
@longtran2904 The role of RuleMatch is not to judge the neighbor type. It is to check whether the rules in Tiling Rules match in order.
For example:
RuleTile will first determine whether A matches, so RuleMatch will be executed 4 times:
RuleMatch(This, GetOtherTile(0, 1));
RuleMatch(NotThis, GetOtherTile(-1, 0));
RuleMatch(NotThis, GetOtherTile(1, 0));
RuleMatch(NotThis, GetOtherTile(0, -1));
If all return true, the sprite of A will be displayed.
Otherwise, continue to determine whether B matches, so RuleMatch will be executed 5 times:
RuleMatch(This, GetOtherTile(0, 1));
RuleMatch(NotThis, GetOtherTile(1, 1));
RuleMatch(NotThis, GetOtherTile(-1, 0));
RuleMatch(This, GetOtherTile(1, 0));
RuleMatch(NotThis, GetOtherTile(0, -1));
If all return true, the sprite of B will be displayed.
Otherwise, the default sprite will be displayed.
from 2d-extras.
Oh, I understand now. Thanks for replying!
from 2d-extras.
@ChuanXin-Unity Maybe we can consider refresh affected tiles by Tilemap, or create RuleTilemap for this problem?
I think tiles provide all affected positions, and refresh by Tilemap internal is better, but Tilemap is not open source, I cannot work for that.
And the efficiency problem mentioned by #163 can also be solved.
For this issue, perhaps the Tilemap could have a default refresh radius parameter that users can specify if TileBase.RefreshTile has not been overridden instead of just the current cell. This is only useful if there is a mix of different types of Tiles with different Refresh ranges.
Alternatively, the base RefreshTile of the normal Tile needs to have its RefreshTile range increased to include all of the neighbours, same as the Rule Tile used.
from 2d-extras.
@ChuanXin-Unity If it is implemented by normal tiles, AnimatedTile/PipelineTile/TerrainTile... and so on all need to be changed, which is not conducive to the user to create custom normal tiles, and the user may not know that in order to support the use with RuleTile, you need to override RefreshTile to add related logic .
Therefore, I recommend implementing it by Tilemap. The advantage of setting with radius is that it is easy to understand. It is feasible to convert the neighbor positions of RuleTile to radius and provide it to Tilemap, but performance may be a problem.
This is an extreme example that will cause the Tilemap refresh radius to be 10(100 box), but ideally it should only be refreshed by 1 box.
from 2d-extras.
Yes, performance will be an issue if the radius is set unnecessarily. Having special cases such as the one as you set above also makes it unfeasible. The issue then would be how to track individual cases like this for refreshing, since the placement of a normal Tile can could trigger updates for Rule Tiles anywhere with extended neighbours.
from 2d-extras.
@ChuanXin-Unity Fortunately RuleTile has already dealt with this problem and the way it works is:
- RuleTileA and RuleTileB each provide their own neighbor positions(Set_A, Set_B) (code)
- When TileMap uses RuleTileA and RuleTileB, merge Set_A and Set_B into the new neighbor positions set (Set_C). (code)
- When executing RefreshTile, base on refresh position, get the "other tile that may be affected" with the reverse position of Set_C. (code)
- Check the neighbor positions of the "other tile that may be affected", if the other tile is indeed affected, execute refresh other tile. (code)
Since the calculation method is based on Tilemap, it is easy to migrate to Tilemap.
from 2d-extras.
Related Issues (20)
- Prefab brush is not showing or working both are error HOT 1
- Error message after install, package not loaded HOT 1
- GridInformationKey should implemented IEquatable<GridInformationKey> and override GetHashCode() to avoid GC alloc HOT 1
- 2D Game Kit errors HOT 5
- unity 2021 package missing files HOT 1
- GridInformation.cs error : The name 'HashCode' does not exist in the current context HOT 6
- Editor Warning On Creation: No script asset for RuleTile. HOT 4
- GameObject Brush paint incorrect position when Cell.Position.y < 0 HOT 2
- New transform of rule tile: rotate and mirror HOT 6
- 2021.1.22f1 HashCode Error HOT 2
- NullReferenceException when using Tile Palette HOT 3
- Gameobject-Tiles and Tile Palette Preview HOT 1
- Mixing Tiles HOT 3
- I have an error. HOT 1
- Error deleting RuleTile with GameObject during physics callback HOT 2
- RuleTile with sibling layer tilemaps? z-axis siblings? HOT 1
- PopulateRuleOverideTileWizard can't work right HOT 8
- Animated Tile Expansion
- Tilemap renderer cannot be refreshed after deletion HOT 2
- Request Special Rule Tile
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from 2d-extras.