GithubHelp home page GithubHelp logo

Rule Override Tile about 2d-extras HOT 34 OPEN

Krummelz avatar Krummelz commented on June 1, 2024 2
Rule Override Tile

from 2d-extras.

Comments (34)

gamercoon avatar gamercoon commented on June 1, 2024 3

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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024 1

@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.

ChuanXin-Unity avatar ChuanXin-Unity commented on June 1, 2024 1

@johnsoncodehk

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.

edwardrowe avatar edwardrowe commented on June 1, 2024

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.

Krummelz avatar Krummelz commented on June 1, 2024

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.

edwardrowe avatar edwardrowe commented on June 1, 2024

from 2d-extras.

edwardrowe avatar edwardrowe commented on June 1, 2024

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.

RuleTileWithSiblings.zip

from 2d-extras.

ChuanXin-Unity avatar ChuanXin-Unity commented on June 1, 2024

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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@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.

ElnuDev avatar ElnuDev commented on June 1, 2024

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.

longtran2904 avatar longtran2904 commented on June 1, 2024

Hey, I know this post is old but I had some questions (I used the example script in the wiki):

  1. How to change the icon in the inspector from 3 to some sprite like the default arrow or X
  2. When I drew, the tile update was slow and noticeable. How to make it faster and update instantly?

from 2d-extras.

ChuanXin-Unity avatar ChuanXin-Unity commented on June 1, 2024
  1. 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.

  1. 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.

longtran2904 avatar longtran2904 commented on June 1, 2024

@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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@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.

longtran2904 avatar longtran2904 commented on June 1, 2024

@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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@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.

longtran2904 avatar longtran2904 commented on June 1, 2024

@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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

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.

longtran2904 avatar longtran2904 commented on June 1, 2024

@johnsoncodehk Thank you, I think I will just leave it there.

from 2d-extras.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@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.

longtran2904 avatar longtran2904 commented on June 1, 2024

@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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@longtran2904 You can use Tilemap.RefreshAllTiles, But if the tilemap size is relatively large, you need to consider performance.

from 2d-extras.

longtran2904 avatar longtran2904 commented on June 1, 2024

@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.

longtran2904 avatar longtran2904 commented on June 1, 2024

@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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@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 avatar longtran2904 commented on June 1, 2024

@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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@longtran2904 Neighbor.Sibling Is 3, Neighbor.This is 1 and progress in base.RuleMatch(neighbor, tile)

from 2d-extras.

longtran2904 avatar longtran2904 commented on June 1, 2024

@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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@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:
image
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.

longtran2904 avatar longtran2904 commented on June 1, 2024

Oh, I understand now. Thanks for replying!

from 2d-extras.

ChuanXin-Unity avatar ChuanXin-Unity commented on June 1, 2024

@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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@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.

螢幕截圖 2020-08-28 下午3 25 22

from 2d-extras.

ChuanXin-Unity avatar ChuanXin-Unity commented on June 1, 2024

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.

johnsoncodehk avatar johnsoncodehk commented on June 1, 2024

@ChuanXin-Unity Fortunately RuleTile has already dealt with this problem and the way it works is:

  1. RuleTileA and RuleTileB each provide their own neighbor positions(Set_A, Set_B) (code)
  2. When TileMap uses RuleTileA and RuleTileB, merge Set_A and Set_B into the new neighbor positions set (Set_C). (code)
  3. When executing RefreshTile, base on refresh position, get the "other tile that may be affected" with the reverse position of Set_C. (code)
  4. 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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.