EnableBehavior() and SendEvent() on the same frame

abc123

Member
If you enable behavior and send event in the same scope, it won't be received. Probably because HasReceivedEvent tasks register their events OnStart.

223.PNG

I fixed this issue just by moving to OnAwake inside the task, and it works.

C#:
namespace BehaviorDesigner.Runtime.Tasks
{
    public class HasReceivedEventFixed : HasReceivedEvent
    {
        public override void OnAwake()
        {
            base.OnStart();
        }
    }
}

It is convenient to initialize variables in the tree with events when enabling, so I suggest to make this default.
 
The reason why this is within OnStart instead of OnAwake is so that the event is only received when the task is active/reevaluted. This prevents it from receiving the event when the task shouldn't.

If you want to ensure the task can receive the event during the same frame you could tick the behavior tree before the event is sent.
 
Last edited:
The reason why this is within OnStart instead of OnAwake is so that the event is only received when the task is active/reevaluted. This prevents it from receiving the event when the task shouldn't.

If you want to ensure the task can receive the event during the same frame you could tick the behavior tree before the event is sent.
Ok, I see why it's on start. I'll just use my custom class then.

Ticking the tree before the event doesn't make much sense, because I use events not as conditional tasks, but to initialize/sync variables inside external behavior. They just hang under Repeater/Parallel branch, constantly listening. For example, if I want to enable behavior when character is set up and ready for action:
C#:
private void OnCharacterSetUp()
{
    _bhvTree.EnableBehavior();
    _bhvTree.SendEvent<object, object, object>("OnEnabled", _character.Config.SightAngle, _character.Config.SightRange, _character.CurrentWeapon.maxRange);
}

private void OnWeaponChanged()
{
    _bhvTree.SendEvent<object>("OnWeaponChanged", _character.CurrentWeapon.maxRange);
}

I could do
C#:
private void OnCharacterSetUp()
{
    _bhvTree.EnableBehavior();
    ((SharedFloat)_bhvTree.GetVariable("SightAngle")).Value = _character.Config.SightAngle;
    ((SharedFloat)_bhvTree.GetVariable("SightRange")).Value = _character.Config.SightRange;
    ((SharedFloat)_bhvTree.GetVariable("WeaponRange")).Value = _character.CurrentWeapon.maxRange;
}

private void OnWeaponChanged()
{
    ((SharedFloat)_bhvTree.GetVariable("WeaponRange")).Value = _character.CurrentWeapon.maxRange;
}
But I don't want to hard-code string variable names, cache Shared variables, do null checks and all that. Events are much more flexible. Anyway, after prototyping is over, I will have to use an option with the least load on performance, probably a blackboard. Events mean unboxing, I haven't yet measured how much it costs. Or may be there is a better way to do it? Variable Synchronizer isn't an option in my case.
 
Last edited:
I've done some tests, and decided to refrain from using <object> events. They produce garbage on sending and almost 10 times more garbage on receiving. So I came up with my custom EventListener class, which seems perfect for my needs (updating variable values). Its basically an Action which always returns running, registers events on awake, unregisters on complete, and can handle events in two ways: only when active or in ghost mode. Of course now I have to create subclasses for different types, but its not a big deal, I only need float and bool really. But it allocates nothing and is still more flexible than assigning individual shared variables.
 
That makes sense. I have tried to figure out a way to avoid boxing with the event system but wasn't able to because the task inspector needs to be able to select any variable type.
 
Top