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:
- [PLUGIN] DoN’s 3D-Pathfinding / Flying-AI system (with full source!)](DoN's 3D-Pathfinding / Flying-AI system (with full source!) - Community Content, Tools and Tutorials - Unreal Engine Forums) implements a great 3d pathfinding AI, but implements its own BT nodes and various other low-level code to do so.
- A.I. Templates - Bot, Car, & +Flying AI by Peter Newton entirely side-steps the problem by implementing AI directly on the Pawn.
- This thread simply cries at the impossibility and lack of documentation.
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.