Weird behavior with ChangeFilter

Hello,

I’m trying to make an ability trigger system. And while it does the job, I’m having some issues.

I have some 2 systems, one collect user input, and modifies a component, and the other has ChangeFilter for that component. The problem is that if the second one is executed with ‘Schedule()’, the ChangeFilter never stops being triggered, but if I execute it with Run(), it behaves as I expect.

So I just want to know if this is a bug, or that’s expected behavior. What’s happening?

A simple component

public struct ChangeableComponent : IComponentData
{
    public int Value;
}

The system with change filter

public class ChangeFilterTestSystem : SystemBase
{
    protected override void OnCreate()
    {
        EntityManager.CreateEntity(typeof(ChangeableComponent));
    }
       
    protected override void OnUpdate()
    {
        Entities.WithChangeFilter<ChangeableComponent>().
        ForEach((Entity entity, ref ChangeableComponent comp) =>
        {
            UnityEngine.Debug.Log("Triggered");
        //}).Schedule();
        }).Run();
    }
}

The user input collect system

public class ChangeFilterTest_InputSystem : SystemBase
{
    protected override void OnUpdate()
    {
        Entities.WithAll<ChangeableComponent>().
        ForEach((Entity entity) =>
        {
            if (Input.GetKeyDown(KeyCode.H))
                SetComponent(entity, new ChangeableComponent { Value = 1 });

        }).Run();
    }
}
1 Like

If you have with change filter then access it by ref in the same job, you are saying the component is changing again therefore it’s going to trigger again next frame that it has changed.

Eg. You can’t write to your change filter component, it should be marked read only so you can react to it.

I used to think that way, but why it is only triggered once with Run()?

Also I have another system

Entities.WithChangeFilter<TotalDamage>().
ForEach((Entity entity, ref TotalDamage damage) =>
{
    UnityEngine.Debug.Log("Dmg clear");
    if (damage.Value == 0)
        return;
    damage.Value = 0;
    damage.BiggerDamageElementType = DamageType.NONE;
}).Schedule();

which is also triggered once.

There is no consistency between this 3 examples

UPDATE

The last example provided now gets called multiple times, the same as the first example when executed with Schedule(). Maybe It was a mistake of my own.

So now it’s a consistent behavior. With Run() the code will be executed once per change, and with Schedule() will execute indefinitely.

If someone knows why is this happening, please help me. I don’t know whether I should rely on this pattern or not

I think this is what Turtle was saying. You are passing it in by ref, it is assumed the data has changed as soon as you do that regardless if you actually change the data

Yes, I know that, let’s assume that what you guys say is the expected behavior. But if I execute it with Run(), it doesn’t trigger it multiple times, just once. That confuses me, because makes me think one of those two ways of execute the code is not working properly

Sorry I misunderstood your post

Actually assuming both Run() and Schedule() should behave as currently only Schedule() does. What could be the best pattern to clear flags using WithChangeFilter?

Haha don’t worry, maybe I should make it clearer, but I don’t know how haha

For me at lest the filter component has never been the one that I need to write too but instead a change in it would write to a different component

eg filter for temperature change found =change tile color component.

The problem is that for example, in the Ability trigger system, some system set the index of the ability in the trigger component, then another system reacts to that change, and activates the ability indicated by the index, but that index has to be cleared, because if another entity in the chunk gets if trigger component modified, the first entity will also have its ability activated

Also, in the damage handler system, it’s the same problem, damage value has to be cleared, otherwise will receive damage if another entity in the chunk has its damage component modified

I mean, I could add another component to react with, instead of reacting to the one which holds the data, but feels like its complicating things. Reacting to a special component dedicated just to react, instead of reacting to a value change of the actual component I care about