Announcement

Collapse
No announcement yet.

Getting reference to event dispatcher that lives inside a Decision Tree Task

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Getting reference to event dispatcher that lives inside a Decision Tree Task

    Interesting that there aren't a ton of recent posts on event dispatchers.

    I'm new to Unreal development and a lot of the courses are pretty useful including HTF? (https://www.youtube.com/watch?v=sEcoWGrF1Hg) and this Epic course from 2016 (https://www.youtube.com/watch?v=EM_HYqQdToE). I've also taken some of the basic blueprinting stuff.

    I'll admit that I am not surefooted when it comes to casting but I can't seem to make event dispatchers work between many AI Third Party Characters. Right now I am only working with one but the plan is:

    1. Have a bunch of AI Third Party Characters each running the same behavior tree.
    2. When the behavior tree executes a specific task, have it communicate this to all other AI Third Party Characters.
    3. Have all the other AI Third Party Characters evaluate if the sender is within a certain distance and line of sight and then do something!

    Here is the snippet from my AI Third Party Controller:

    Click image for larger version

Name:	BTT_ObserveFreeze.PNG
Views:	110
Size:	115.5 KB
ID:	1768249

    #2
    Not sure if that can be done. Also biggest reason to have AI, behavior trees and blackboards is performance when you get 100+ characters like that.
    So it may be impossible, or rather (quite common in unreal) you are fighting against design of engine.

    At some point I gave up on fighting all those references up and down (like AI, umg, animation trees etc.)
    And for every group of blueprints\actors i made single messaging system.
    Quite simple:
    - there is dispatcher in easily referenced blueprint like game mode,
    - and there is event that fires that dispatcher also in game mode
    - every actor that want to send message calls that event in game mode
    - everybody that needs such info hooks into dispatcher

    Dispatcher and event are simple, they pass over only 2 values:
    - who called it
    - and what is message.
    ofc. i Format messages so they are easily readable by other scripts, for that i create enumerators, but when they passed over i change them to string and back to enum.
    Easier to avoid silly spelling errors.

    Unreal really needs some global messaging system

    Comment


      #3
      Nawrot it makes sense to push it out to the game mode and I'll try that first. After struggling more, I think I confirmed that you CAN'T do it in the BT and BB for the reason you suggested, namely that the nodes are shared objects between all objects. Appreciate it!

      Drix AIBK uses a messaging system that uses the AI hearing sense which is a good hack but I'd rather the messaging be implicit.
      The other way I think is to have the DT change a key in the BB, then have the AI Controller detect that change and then call the event.... but that also seems like a work around whereas the DT ought to be the origination of the call. Also as a newbie, I am not too sure how to detect a BB value change.

      Behavior Tree Node Instancing Policy


      Behavior Tree Nodes (referred to here as "nodes") exist as shared objects, meaning that all agents using the same Behavior Tree will share a single set of node instances. This improves CPU performance while reducing memory usage, but also prevents nodes from storing agent-specific data. However, for cases where agents need to store and update information related to a node, UE4 provides three solutions:
      Instancing Nodes


      The node's bCreateNodeInstance variable, when set to true, will grant each agent using the Behavior Tree a unique instance of the node, making it safe to store agent-specific data at the cost of some performance and memory usage. Certain UE4 node classes, including UBTTask_BlueprintBase, UBTTask_PlayAnimation, and UBTTask_RunBehaviorDynamic, use this feature.
      Storing on the Blackboard


      A common solution is to store variables on the Blackboard. To do this, expose the name of the variable from your node, then fetch and store the Blackboard Key using that name during the node's initialization. You can then use the Blackboard Key to get and set the variable's value on your agent's Blackboard instance. This method supports variables of bool, float, FVector, int32, enum (stored as uint8) and UObject* types.
      Storing on the Behavior Tree Node


      You can create a custom struct or class to store variables inside the node's memory.

      Comment


        #4
        I ended up using the dispatchers in the game mode exclusively to solve my issue and it works fine. I've also learned the hard way (aka, had to redo a ton of casts) the need to modularize everything so that my components can be slapped onto whatever actor I need. Thanks Nawrot

        Comment


          #5
          Originally posted by HH4570 View Post
          the nodes are shared objects between all objects.

          The node's bCreateNodeInstance variable, when set to true, will grant each agent using the Behavior Tree a unique instance of the node, [...] Certain UE4 node classes, including UBTTask_BlueprintBase, [...] use this feature.
          Now I'm new to BTs myself and am still figuring them out. But it sounds like Tasks (in blueprint) are instanced.
          And I'm using an Initializing Task to bind to input that the user gives to the character. Such as Move to, attack, stop etc.

          Another thing you can do is to use a component that acts as a mediator between the BT and everything else. So the first thing the BT does is to set up a reference to the mediator and set it as a BB value. So now you can call the mediator from any BT node to inform about new data or events, say if the BT decides to attack a target and some system not in the BT needs to know about that.

          So a task that binds to dispatchers to funnel information from your character/controller to the BT (though you could also use Set Blackboard values).
          And a mediator component to funnel information from your BT to the character/controller.

          For example, I use the BT to determine if it is in range & LOS of a target, but I want my attack component to handle the combat (it's a pretty simple system), so BT can attack -> tell mediator to start attack -> Mediator dispatches Start Attack which the attack component listens to.
          When the target dies, the BT is informed about that through the mediator.

          Again, new to AI in general myself but it's working just fine.
          Though I don't know how I would solve the situation you have with wanting to communicate the action to other characters. Using my method, the BT task would tell the mediator to perform the communication, and then the proper channel does that communication. But just what that channel is and what it does, I'm not sure. If it is like a party of characters, like I have, I'd have the mediator inform some party manager, which then communicates that to each member.

          Comment


            #6
            That makes sense for internal communications and now that I've played with it, I definitely agree with offloading a majority of 'actions' to an actor component, storing facts to blackboard for quick retrieval, and then saving the BT for relatively simple logic decisions (or conversely, very frequent decisions). Most of my actions are are decided on the order of tens of seconds so I don't need the tick rate of a BT.

            Thanks for the extra ideas!

            Comment


              #7
              Originally posted by ste1nar View Post

              Now I'm new to BTs myself and am still figuring them out. But it sounds like Tasks (in blueprint) are instanced.
              And I'm using an Initializing Task to bind to input that the user gives to the character. Such as Move to, attack, stop etc.

              Another thing you can do is to use a component that acts as a mediator between the BT and everything else. So the first thing the BT does is to set up a reference to the mediator and set it as a BB value. So now you can call the mediator from any BT node to inform about new data or events, say if the BT decides to attack a target and some system not in the BT needs to know about that.

              So a task that binds to dispatchers to funnel information from your character/controller to the BT (though you could also use Set Blackboard values).
              And a mediator component to funnel information from your BT to the character/controller.

              For example, I use the BT to determine if it is in range & LOS of a target, but I want my attack component to handle the combat (it's a pretty simple system), so BT can attack -> tell mediator to start attack -> Mediator dispatches Start Attack which the attack component listens to.
              When the target dies, the BT is informed about that through the mediator.

              Again, new to AI in general myself but it's working just fine.
              Though I don't know how I would solve the situation you have with wanting to communicate the action to other characters. Using my method, the BT task would tell the mediator to perform the communication, and then the proper channel does that communication. But just what that channel is and what it does, I'm not sure. If it is like a party of characters, like I have, I'd have the mediator inform some party manager, which then communicates that to each member.
              ste1nar For the "Tell mediator to start attack" part of your setup, how does that work? Does it have to be a direct function call on a reference in the blackboard or is there some way for the BT or BTT to dispatch the event itself?

              Comment


                #8
                Originally posted by gWesson View Post

                ste1nar For the "Tell mediator to start attack" part of your setup, how does that work? Does it have to be a direct function call on a reference in the blackboard or is there some way for the BT or BTT to dispatch the event itself?
                It's a direct function call from the Attack Task to the Mediator component, which is stored in the BB. To me it makes the most sense to directly call functions from the BT but that's just subjective.
                But I feel like it would be tricky to dispatch/delegate call from the BT, because you'd first have to set up the listeners. So the first time you enter the Attack Task, there's nothing listening to it. And you can't really get to the Attack Task an Initialize Task which is the first task to run to set things up, or vice verse. If you want to listen to the Initialize Task then that is very possible, but then you can't get to it from the Attack Task. Unless you store the Initialize Task in the BB. hmm.
                Dispatching from the BT feels like a round about way when direct calls, at least should, be just fine. Surely the BT should be aware of the objects it can communicate with?, where as dispatchers are more used for agnostic communication.
                Last edited by ste1nar; 07-10-2020, 05:55 AM.

                Comment


                  #9
                  An addendum
                  I wrote that "So a task that binds to dispatchers to funnel information from your character/controller to the BT (though you could also use Set Blackboard values)."
                  Months after having implemented my BT, and a few weeks of not having time to work on the project, I'm now thinking that maybe it's just better practice to have a component set BB values than to use a BT task.

                  What I currently have is an Initializing Task that sets a reference to the mediator object in the BB, as well as binding events.
                  As the program runs, the Initializing Task receives calls with some data and it performs queries which results in it setting or not setting new BB values.

                  That is where I am now reconsidering. Instead of having a Task that gets called with new data and makes decisions, Instead use an actor component that gets called and makes the decisions and then updates the BB values with "Set Blackboard Value". So it now revers back to how it is usually handled in Epicstreams and other tutorials. But the nice things about having a component for that specific task is that you have one place to set BB values, so you don't Set BB Value all over the place, both in the controller and character.

                  This BB Value Setter object can be its own object or that logic could be added to the Mediator.

                  Here's a simplified chart, left is my current system, right is probably more proper.
                  Both cases needs to set a reference to the mediator in the BB for BT->Mediator communication to happen.
                  Click image for larger version  Name:	BBDesign.png Views:	0 Size:	18.9 KB ID:	1786412
                  Last edited by ste1nar; 07-10-2020, 06:32 AM.

                  Comment

                  Working...
                  X