AAIController implementation is too character-specific

Short Version & Use case

I’m trying to implement a simple flight-based AI, however the AAIController class appears to be geared exclusively towards character-based pawns.

Problems of specificity

AAIController::MoveToActor AI|Navigation|MoveToActor
The blueprintable function for MoveToActor (and MoveToLocation) have some overly-specific arguments:

  • bCanStrafe which is specific to character movement
  • bProjectDestinationToNavigation which is specific to using the navMesh navigation system.

MoveTo, RequestMove, FindPathForMoveRequest (from IAICharacterMovementInterface)
These three functions all use FAIMoveRequest, which also has character implementation-specific properties for bCanStrafe and bProjectGoalOnNavigation.
Happily, these functions all come from IAICharacterMovementInterface, so the specific implementation is expected, even if it’s not welcomed by being on the root AI class.

The most annoying aspect is that the base BehaviorTree classes all have direct types of AAIController in arguments, so if you implemented your own AAIController you couldn’t use those base BT nodes.

Whys this a problem?

So what, right? They point to AAIController, so they’re probably for Characters anyway, so it doesn’t help me to try and use them.
The problem with that thinking is you’re making anything other than character-based AI a second-class citizen.

AAIController is the most low-level AI Controller implementation. The only way to implement another type of controller would be to make an entirely different class of AI Controller extended from AController.
Because I expect future improvements to the AI system to happen on the AI Controller, I’d quite like to use the AI Controller itself as a basis for my AI, rather than going totally off-piste and building my own implementation from scratch.

It doesn’t make any sense for UE4 (nor my project) to have two completely different AI code-trees simply because movement mechanics are different between the two.

Workarounds

We can totally just accept this is the way things are and implement our own methods of side-stepping the problem.
This is also the absolute the worst way to work when you want an engine that’s capable of providing you with the basics to get off the ground, and just let you code.

Some possible ways around the problem:

  • Implement the AI directly in the Pawn and bypass use of a Controller all together
  • Implement your own AIController based on AController, and port baiscally everything unrelated to characters across.
  • Implement a derivative of AAIController and create all your own functions for movement, overriding as much of the base character-related implementation as possible.

Some examples of this in the real world:

I personally think these are all pretty poor solutions simply because the architecture ties peoples hands behind their back.

Possible solutions?

A pretty obvious fix going in would have been to move more of the character-related code to an Interface, and to have a Character AI class be extended from AAIController.

Unfortunately we’re a little bit buggered because lots of code already relies on AAIController specifically (including code we can’t see, that people have implemented), and this includes blueprints which - so far as i can tell - don’t take kindly to refactoring functions onto interfaces, or relocating single specific functions to other classes gracefully (read: everything breaks).

From a refactoring standpoint this feels like a similar issue that was raised in Suggestions for a refactor of Character Movement’s Netcode, for better diversity., in that refactor tools for blueprint and interfaces aren’t sufficient to make any low-level change a non-breaking change. Either the refactor tools need improving, or general-use classes need to be built with refactorisation in mind (I’m not sure how you could hope to do that without a ton of interfaces?).

Anyway, a more realistic option would be to make the AI Movement a component in itself, with pathfinding being a member of that component.

A good use case for this is with my flight AI, which might want to have its own pathfinding system which takes its own query filters and movement parameters (bCanAfterburn, bIsLanding, bTaxi). To avoid putting a stack of arguments on a moveTo function, we can instead create a MoveRequest from a factory method on the AIMovementComponent (or similar) which we modify with our options, and then make requests with that object (predict movement, do movement).

Similarly we might have a JetPackDude AI, who can navigate on the navmesh but also make “leaps” using his jetpack. some kind of compound movement navigation resolve might be required under the AIMovementComponent before the full path (across multiple nav systems) can be resolved (and you’d want to feed that compound system something like bCanJetPack, fJetpackRange).

Why now?

It doesn’t seem right for anything-but-characters to be second-class in UE4, and with lots of AI things on the roadmap it seems like now would be an ideal time to address these issues, before more code is written which can only support a single type of Pawn.

I did give it a go myself, but because of the BC-breaking nature of any change I make, I couldn’t figure out how exactly to proceed. Just dropping a PR on the Github repo doesn’t seem like a viable way forward.

Help from an AI programmer would be much appreciated, and knowing the direction this is all likely to head would also be fantastic.

You are not alone, I’ve been going through the same issues trying to create a flight ai for a game I’m working on. You have expressed just about every pain point I’ve encountered over the last few months.

My choice from a high level architecture view, Start a completely new AI hierarchy, that can coexisting with the existing one. This would be the least breaking method going forward. Once the new class structure is stable enough depreciate the existing AAICharactor classes.

The utter lack of documentation around all AI components has meant I’ve resorted to reading the engine source to figure out what is going on, and that is no easy task.

@V1nce there’s no problem using pawn instead of character with IAController.

There is a plugin that someone made available for Flight AI, for free. http://www.drunkonnectar.com/3d-pathfinding-ue4/

On his github the latest is only 4.13 but I downloaded the zip for the source code and built for 4.14 and there weren’t any problems.

It uses Pawn, not character, with a floating movement component. I haven’t done much with it beyond integrate it into my game at this time, but I set a bunch of targetwaypoints in a level and tagged them. Then the npc’s could then select a random one in a task, and they fly to them using the plugins flyto task, correctly pathing.

It didn’t look like it was working at first because I scaled the nav manager actor in the level. To grow the size of the volume you need to use the details panel and adjust the voxel size and count, otherwise you’ll wonder why nothing is working. He logs well, which is how I figured out that was a problem.

Yep, this is one of the items I mentioned in the “Workarounds” examples: “[PLUGIN] DoN’s 3D-Pathfinding / Flying-AI system (with full source!)”, and is one of the ones I took a look at weeks before posting this.

I don’t think it’s acceptable to implement the AI directly onto the pawn and totally side-step the controller framework. This isn’t a suitable answer if I want to use perception and behaviour tree components, or iterate development and replace the AI classes, or implement the AI (or variations of the AI and behaviours) across multiple pawns easily.

There’s another problem here in that AAIController’s over-specific design has led to DON’s path finding and flyTo methods being totally detached from the AIController and existing navigation framework, implemented instead as its own classes and blueprint calls.
Ideally DON pathfinding system would just be another navigation component which can be bolted onto an AIController, and modularised into the 3d path finding (nav), path following (movement), and behaviours (BT and nodes) needed to make basic flight AI.
That approach would lead to greater customisation and iterative development of the 3d nav capabilities.

I can of course implement my own AAIController class, but as I said in the OP, I’d just be rolling my own system and tacking on various parts of UE4’s framework. I’d quickly fall out of lock-step with UE4s own development because I’m way off-piste.
Ideally the AI system (the AAIController) in UE4 should be able to support all types of Pawn, not just characters.

As it stands, I’m likely going to have to make my own SuperAwesomeAbstractAIController to avoid the current specificity of AAIController, and then totally refactor my code when UE4’s AI module capabilities are expanded for use cases besides dude-with-a-gun.

@V1nce

It is not sidestepping the controller framework at all, because I use the same AIController that I have for another set of npc’s.

It uses as it’s base the floating movement component which is part of pawn, that isn’t available as Character. Character is meant as a walking around agent. Nothing to do with the Controller.

I have no problems with other BehaviorTree nodes using the DoN plugin. His FlyTo node extends from BehaviorTree task. I’d take a another look at the AIController, because it is not hard linked with Character.

3D space requires a different pathing solution than navigable 2D space. In a perfect world, as a user of the API you wouldn’t know the implementation details of the difference, but honestly if they made the system and handled it a certain way people would be clamoring for it to be either different or more modifiable. AI is always tightly coupled with design in games and there is not a generic solution that will satisfy everyone. That is why there are AI designers and programmers.

I’ve used Unreal for so long that I’m very comfortable with their framework, 9 years. The basics of it didn’t change from UE3 to UE4. A lot of the gripes with UE3 was that is was very easy to make a FPS game and requires serious mangling to do other types. They first opened it up by pushing C++ into UnrealScript which made it easier to extend from a more generic base, and UE4 furthered that with the components.

If Character doesn’t fit your needs, you need to extend Pawn and make your own. AIController works just fine Pawn and subclasses of it. That doesn’t mean you have to throw AIController out and start that from near scratch as well.

If you do decide you need your own Controller I would not worry about getting off branch from UE4’s. If they ever add a more abstract version, or include 3D navigation you could use it in your next project but keep your system for your current game.

On the contrary, the blueprint nodes and functions on AAIController that relate to movement have explicit arguments which relate directly to character navigation.
By “character navigation” I mean the navmesh implementation, which is only suitable for character (run about on a flat surface) navigation.

This over-specificity in both the function arguments and the components implies AAIController is not suitable for anything but characters.
Yes, that’s only an implication, but as AAIController is also the lowest level AI Controller you can implement without rolling your own solution it means any extension you make from AAIController will be cluttered (or hindered) by character-specific code.

There’s already such a thing as a pathfinding nav component, so I don’t see why 3d-space pathfinding can’t be implemented as another nav component. The real question is what interfaces are required by: 1. the abstract pathfinding, 2. the specific pathfinding

As I’m sure you know, there are plenty of ways to write decoupled code so you don’t have specific implementations all over the place, or not enough power. Factory methods to provide request objects that can be used to execute tasks is a simple enough way to side-step enormous or implementation-specific functions.

The trade-off is always a question of DX (how obvious is it that you have to do X to make this system work) and and how much time you can spend doing development ($$$).

No it just means if I extend the base AI Controller, which is AAIController, and implement my own AAIFlyingController, I will have a lot character-specific stuff hanging off the side of my class (moveTo functions) which is in no way the right way to do things.

[/quote]

I get where you’re coming from; Yes, I could just roll my own. Yes, I could extend the current one and work around the specifics. but this is not about what I can do, this is about the fact that UE4s AI system starts off being implementation specific and does not allow me the flexibility I desire.

I’m willing to plough my own time into this (so the cost is free), but I need to know what Epic consider the correct direction. I don’t want to put a weeks worth of work into a PR that will never see the light of day because I went against the grain. I merely require some direction as this is such as a low-level system.

Personally, I’m leaning towards making AAIController extend from a new non-character-specific class, and re-engineering the inheritance and cast checks in the BT nodes.
I haven’t tried it out, but I feel this would avoid breaking everyone’s current Blueprints and behaviours (no changes to interfaces) while also opening up the AI system to broader implementations.

@V1nce

I agree with what you are saying as far as a complete system. My suggestions were just on what could be done now, that would work, as I have 3D pathfinding working and am using the AI controller, with a pawn with the floating movement component.
@MieszkoZ will need to jump in and speak to properly abstracting UE4’s AI so it provides a more general case and if that’s something he will even consider

Thanks , I appreciate your efforts, and if I was just willing to put up with the current state-of-play, I’d absolutely be going down one of the paths you suggested. Really though, I’m dying to see this system improved so people like me can get off the ground running much more easily, without being confused by all these little hiccups along the way.