Improvements to AI behaviour - vision, searching and animations

Hi all,

I’m sure most of my questions have answers out there but finding exactly what I need is proving difficult so I was wondering if you lovelies can help me find the way

Currently working on some AI behaviour for a game - it’s your classic see player, chase player, search for player behaviour tree. I’ve got all the basics down and I’m currently struggling with just a bit of (hopefully) simple expansion so here goes:

  1. When the AI is searching for the player I have him moving to a random point in nav radius, however this means that he can frequently move near a solid object (wall, box, door etc) that he can’t see through but he’ll just stare at. Can I put another function at the end of the BT loop to effectively say “rotate the AI so that he’s looking down a clear path, not into a wall”

  2. Within the same BT section, when the AI is finding it’s random point in nav radius how can I put in some calculations to effetively give me a minimum distance between points? The max radius is all good so he doesn’t wander the entire map, but it’s also silly that he’ll pick a random point that’s 5 units away from his current position. I’d prefer to say pick a random point that’s within 300 units of last known player location and also make sure your next point is at least 100 units away from your last random point

  3. I use an enum for AI state - unaware, suspicious, alert - to catch the state of the AI and do stuff on the BT. I’ve successfully cast the blackboard enum variable to the character BP and I use it to then update the character’s colour so that when the BB enum is alert, the character turns red etc. I’m however having real trouble trying to also feed the BB enum value into a variable for the animation blueprint. I want to use the state enum to change the anim state so that if he’s alerted or suspicious he’ll raise his weapon and patrol/chase with it aimed at the player. The statements I have to change the anim state work fine as if I manually change the enum value in the preview I get sent down the correct channel but I can’t figure out how to pass the blackboard variable value over to the anim graph (as above, I’ve cast it to the character fine)

Sorry, I know this is long and involved, just want to try and iron out as many trivial things as I can before I try and build out to more complexity

Hi, you could watch through this here https://www.youtube.com/watch?v=iY1jnFvHgbE

Can I put another function at the end of the BT loop to effectively say “rotate the AI so that he’s looking down a clear path, not into a wall

  1. Sure, you should be able to use RotateToFaceBBEntry, I don’t know though whether it needs a location or direction. Ofc you need to find the location you want to look at (e. g. linetraces that don’t hit something).

how can I put in some calculations to effetively give me a minimum distance between points?

  1. Try to find a random location in range, check whether that location has the minimum distance, if not repeat.

Generally speaking, for finding the next point consider using an EQS query, instead of just choosing random points.

  1. I send it from AI controller to the pawn (and set to repnotify since I’m using MP) and then from the repnotify function call an interface function inside the AnimBP.

Thanks for the reply - 1 and 2 are well on their way now, tbh I’d actually forgotten about EQS after my first playaround But I’ve got that to a point now where it’s more usable and looks better than the standard random

As for point 3, I’m still struggling a bit with this, mainly because I don’t know what function/s to use on the animBP to only update it’s state variable when the blackboard variable changes. Maybe I’m going about it the wrong way?

As for interfaces, these are confusing me quite a bit. I’m reading the docs but it’s just not clicking. I’d create a new BP interface, put a bunch of input/output variables in and then what? Is this like a bit of a global storage/access medium that I could replace all my casts with?

Focusing on changing the AI state enum - my BT sets my blackboard key of AI state to an enum of my choosing depending on the route taken. Then in my AI controller I’m already setting my AI state enum value by way of - - etc (although at the moment that’s on tick as I’m using that to set a vision cone colour and I don’t know how else to run that function only when the enum value changes)

So instead would call the new BP interface within the AI controller and set it’s AI state input to be the controllers AI state value? Then the output would be?

If you have any graphs I could look at or any other videos that might help, but it’s really blowing my mind at the moment. I’m sure it’ll click eventually

To get the new AI state enum from your behavior tree task where you set it to the AnimBP you could go through the ControlledPawn. So you could call a function/event inside the ControlledPawn from where you then call a function/event inside your AnimBP (from your skeletal mesh → GetAnimInstance). So BT Task → ControlledPawn → AnimBP.

Now there are basically two ways to do BT Task → ControlledPawn and ControlledPawn → AnimBP. One way would be, inside your BT Task get the ControlledPawn and cast it to your pawn, then call the function/event you have in there. At least two problems with that:

(-) Imagine you want to change out that pawn or use it for different pawns. Now your BT Task will break and you will also need to adjust it.

(-) You’ve got a hard reference from your BT Task to that pawn. Meaning as soon as that BT is loaded into memory the pawn will be loaded into memory as well. Now imagine you want this to work for three pawns, so inside your BT Task you have a cast to each of them. Now all those three pawns will be loaded into memory, even if you only use one.

The other way would be to let your Pawn implement an interface where you have that function. Then inside your BT Task you can just call that interface function on the ControlledPawn. Now if you want to change the pawn, the new pawn just needs to implement that interface.

Same with ControlledPawn → AnimBP

I think I’m getting a bit of a better understanding, although it’s still not quite clicked. But from what you’re saying:

  1. I can create a BT task that will set the blackboard key for enemy state to the relevant value (this way I can do BT tasks based on the key) AND I can do a BT task to send this value to a BP Interface?

1a) The BP Interface, I’m assuming I set up a function in there with a single input (enemy state) and that’s it?

  1. I add the interface to the BP for the AI controller, the AI character and the AI AnimBP - this way, all 3 BPs can access the interface functions?

  2. The fact that my BT task sent a message to the BP Interface telling it that it’s input value for Enemy state is x would be all I’d need to do to send that value to other BPs?

  3. The AI controller BP is where the AI perception happens, so there’s an event on there to handle senses when perception changes - I assume I can call the BP Instance there to tell it what the enemy sense value should be on perception change? I don’t know what difference it would make whether I set the BP instance value via the BT or the AI controller

  4. The AI pawn - controlled by the AI controller - can retrieve the value from the BP Instance? And the AI AnimBP can retrieve the value from the BP instance? This I think is where I’m still not quite getting it. Do I literally just put in Event and then get the input value from the BP instance and maybe set it as the value for a variable local to the BPs? That way I can then use the local value on the AnimBP to (for instance) change the state machine paths

Sorry, I feel like I’m being severely dumb and asking you to spoon feed me beyond your requirements but thanks for taking the time to at least guide this

I would use a single BT task that sets the blackboard key and tells this to the ControlledPawn/AI Controller at the same time.

1a) The BP Interface, I’m assuming I set up a function in there with a single input (enemy state) and that’s it?

Yes

I add the interface to the BP for the AI controller, the AI character and the AI AnimBP - this way, all 3 BPs can access the interface functions?

You add to the BP and then inside that BP you implement the interface (e. g. inside the AI character you call the interface function to change the state of the AI on your AnimBP, and in your AnimBP I guess you just set an AI state enum variable to the value of what your interface function gets as input)

The fact that my BT task sent a message to the BP Interface telling it that it’s input value for Enemy state is x would be all I’d need to do to send that value to other BPs?

Not quite sure I understand what you mean =)

You call an interface function the same way you would call any other function/event on something.

For example here I call an interface function on my AnimBP (that’s the AnimationInstance here)

annotation_2021_11_12_call_interface_function

4-5

I don’t understand what you mean with BP Instance.

The AI pawn - controlled by the AI controller - can retrieve the value from the BP Instance? And the AI AnimBP can retrieve the value from the BP instance?

If you add an interface to an actor, then what you’re doing is you’re only adding those functions (without any implementation) to that actor. Nothing else, there are no shared variables. Each actor then needs to implement those interface functions.

So you if you want your AnimBP to get a new AI state, then you need to call the interface function on a reference to that AnimBP, same as you would do for any other function/event.

Sorry, I didn’t mean BP instance at all - I meant interface!

I think I just need to sit down and play at this point, but just to quickly follow up:

by this I mean that if the BT task were to tell the BP Interface that the enemy state was now “alert”, does that then mean that any other BP that accesses this interface and retrieves the Enemy state input would read it as being alert? So, in effect I have an AI controller BP, a character BP and an AnimBP - in all 3 BPs I could call the interface and get that input value and set it as a variable that’s local to each BP?

Calling the interface function, I think that’s where I’m struggling as well but it might be just my lack of understanding. I would have thought that the event would need to be called within the BP, it wouldn’t just happen by nature of it being in there? So i’m sitting here thinking “I want to update the animBP variable of enemy state WHEN the BT tells me it’s updated” and I’m assuming that needs some other function within the BP to then call the Instance function. Maybe it doesn’t?

You would call the interface function as you would call any other function on an actor =)

Maybe for a moment forget about interfaces. Imagine you got your AnimBP, you got a reference to it and inside your AnimBP you got a function called “UpdateAIState” that takes a single input value, namely the new AIState. Now if you would want to tell that AnimBP about the new AI state, you get the reference to your AnimBP and call UpdateAIState on it.

Now you could also create a new interface, put a single function into it, lets call it “MyUpdateAIState” which again takes a single input value, the new AIState. Now let your AnimBP implement the interface. Now if you would want to tell that AnimBP about the new AI state, you would do the exact same as above, but instead of calling “UpdateAIState”, you just call “MyUpdateAIState”.

The difference now is, that in the first case the reference to the AnimBP you have, needs to be of the exact same class of where you have that function. So if you would just have a reference of type Actor (that actually points to the AnimBP), then you cannot call “UpdateAIState”, since Actor does not have that function. You would first need to Cast your reference to your AnimBP, only then can you call “UpdateAIState”.

But in the second case, even if you just have a reference of type Actor, you can still call “MyUpdateAIState” and it will work (again if that reference actually points to the AnimBP). So here you do not need to cast. That’s the difference.

Cause now imagine if you got AnotherAnimBP which should have the same functionality. So in the first case you would go into that class and add a “UpdateAIState” function to it. Now if you want your code to function with AnimBP and AnotherAnimBP, then you will now need to have two casts. One that tries to cast it to AnimBP and since it may be of AnotherAnimBP you need another cast to AnotherAnimBP (and one of the two will work, since I assume here that the Actor reference actually points to either an AnimBP or AnotherAnimBP).

But in the second case, nothing has changed. You just take that Actor reference and call “MyUpdateAIState” and because the Actor there has implemented the interface it has the “MyUpdateAIState” function and it will work (ofc your AnotherAnimBP needs to implement the interface).

Thanks man - you’re a legend for taking this much time out for me :slight_smile: I think I’m good on the understanding overall and I think I get the implementation of it although when it fails I might just ask for some quick advice on what I’ve done wrong

I believe I’ve got it working from BT/Controller to character so just need to sort it out to the animBP which should hopefully be the same

Ok, so yeah I thought I was getting it - turns out I’m nowhere near getting it, instead I think I’ve just confused myself even more. I’m getting way too frustrated with this at the moment, it just doesn’t make sense to me :sob: :sob: :sob:

So, what I have currently:

An AI controller with AIPerception. I have an On Target Perception Updated event that does all the checks for what it saw/heard and sets it’s own variable of CurrentState to be alerted or suspicious. It also runs a behaviour tree

The behaviour tree has a task on there of SetGuardStatus and this updates the AIStatus BB key to be the required enum value depending on the branch taken

An AI character that (at the moment) runs an event tick to get it’s AI controller, gets the blackboard and gets the AI_State key so that I can update a sprite colour depending on whether the AI is alert, suspicious or unaware (which works but not ideal)

An AI Anim BP that should switch between animation states depending on whether the AI is unaware, alert or suspicious

I create a new interface (GuardStatus_Interface) with one function that I’ve called Current Guard status and a single input of guard status with is set as an enum type of Guard Alert Status (which is an enum I’ve created to list the three states)

My assumption of what I want to now do is, when the behaviour tree is running and it goes down a path, the SetGuardStatus task runs to set the blackboard key. I then want to also send that value to the interface so it can be used by the AI controller, the AI character and the AI anim BP - I assume I can do that?

SO - my behaviour tree task will send a message to the interface and pass the updated guard status to that message correct? I therefore don’t need to implement the interface on the behaviour tree task, I just send the message? The problem with this is that the event I call on the behaviour tree to set the guard status fires on event receive execute AI, so using the current guard status message what would go into the target? The owner controller? The Controlled pawn? Something else?

Then I can access that message from other blueprints IF they implement the interface? So I (for instance) go into my AI AnimBP blueprint and implement the interface. I can then see the interface function on the left side.

In the event graph for the AnimBP I can then right click and add my current guard status event. I then want to update the Anim_AI_State variable on my animBP so that I can use that variable to go down different anim states, so from this custom event I could just do a Set Anim_AI_State and plug the guard status from the interface event in? Or as per your picture you come out of the interface task and get a message which I don’t have but then I also don’t have an animation instance object

I’m so close to just abandoning this completely because I just don’t get it at all

Here is an example of how you could do it with casting:

and how you could do the same as above, but using an interface instead of casting

In the example using an interface, the AI controller, the AI character and the AnimBP implement that interface.

Thanks man, I don’t think that looks too dissimilar to what I nearly had but I just feel like I’m getting it wrong

In terms of the interface, I’m guessing it’s just a single input of New AIState? And the input is an enum that’s the specific enum I’ve created to hold the various states?

I think another point I was getting confused on is execution - ordinarily if I use an event node (typically it would have been a custom event) I actually need to call the event. So something simple I’ve been using is Event tick > run custom event, then further down I have event custom event and then that does stuff. But the Event Set AIState that you have, is that just sitting on the graph, not actually being called by anything? How does it know to run? When does it run? etc

In terms of the interface, I’m guessing it’s just a single input of New AIState? And the input is an enum that’s the specific enum I’ve created to hold the various states?

Yes

So something simple I’ve been using is Event tick > run custom event, then further down I have event custom event and then that does stuff.

Same with the interface. Instead of “run custom event” you do “run interface event/function”, then inside the object where you’re calling the interface function on, you need to implement the interface. That means that object then has the interface event/function and then that does stuff when it gets called from your code.

But the Event Set AIState that you have, is that just sitting on the graph, not actually being called by anything? How does it know to run? When does it run?

What you do is you call the interface event/function on something (target). Then it executes the interface event/function that is inside that target.

Ok, definitely making a bit more sense now I think. So I believe I only have one last question (for now but I will leave you alone!)

I start on the BT task and send a message to Get AIState with the new enum set in there that I want to use on the tree for decisions and then want to also pass that same state to all places where I might want to use it. So the message being sent triggers the event elsewhere to start

Is there not a way (or is there a reason why) that the AI controller and character graphs can’t just do what the animBP does and run that custom task to set AI - in other words, do interfaces not work in terms of something sends a message to the interface and all BPs that have that interface can then just use it?

I get that each step in your images is referencing a specific target each time which is why I need to send another message, but my assumptions when I started this was that: BP A sends a message to the interface. BP B, C, D and E can all just receive that message if they implement the interface regardless of what they are or what they do

in other words, do interfaces not work in terms of something sends a message to the interface and all BPs that have that interface can then just use it?

No, you send something directly, but you do not need to know what it is that you’re sending to =)

So for example you don’t care whether it is an AIController, an AICharacter, or … So if BP A wants to send/execute something via interface to BP B, C, D and E, then you need to execute the interface function with B as target, then with C as target, same for D and E.

Another method of communication inside UE are event dispatchers. Basically an event dispatcher broadcasts an event, and everyone that listens will receive it. So BP A broadcasts, and if BP B, C, D, and E would listen (to exactly this event dispatcher) then they would all receive the event. In your case the event dispatcher would have one input, the AI State.
In order to listen to an event dispatcher, you need to bind an event to it. So BP B, C, D and E would need to get a reference to the event dispatcher from BP A (of this exact instance of BP A) and then bind to it. So in your case, the AnimBP would need to get a reference to the AI controller and access the event dispatcher from it. For event dispatcher you could take a look at this here Event Dispatchers explained - Finally!

Generally speaking for structuring code, you got a couple of different tools there (like inheritance, components, interfaces, event dispatcher, cast). What you use depends on what you’re trying to do, but generally I find it good to think about what you would need to adjust when you want to delete/change/expand something. For example if you would use casting in your case, then e. g. if you would want to switch out the animation blueprint, you would not only make changes inside the new animation blueprint (like implementing an interface or binding to an event dispatcher), but you would also need to go into the code where you call the event on the animation blueprint.

Or if you send the AI State from the controller to the AnimBP, then now your AnimBP also gets information from the controller (and not only from the pawn). Now that means that you have two classes/places you need to keep in mind there instead of only one (if you do everything from the pawn). Same if you would do Behavior tree task → AnimBP. And ofc AI controller and Behavior Tree do not exist on clients, so it will all break if you ever want to use it in multiplayer…

And you could also do right click on an asset → Reference Viewer / Size Map to see what that references, what will be loaded into memory as soon as this asset is loaded into memory. So if you never took into consideration how to structure your assets and what references what, then you may/will end up bloating memory usage and loading times.

And you could also look at this here Blueprint Kickstart | Course and Breaking Down the Components of Gameplay | Course (and ofc generally the online learning courses if you haven’t already).