Hello! I had originally posted this on the C++ forum, but it seems no one really replied, so I’m trying again here. I’m referencing C++ here, but explaining with blueprints is just as helpful. Any help is appreciated!
I’ve read most of the documentation about controllers and pawns, but I’m still not sure how I should handle this. In my game there are multiple types of enemies, let’s use a swordsman and an archer for example. The swordsman attacks by getting close to the player and playing an animation, whereas the archer gets within range and not only plays an animation, but draws his bow, and spawns a projectile. Is a good way of handling this to create a base enemy AI pawn-class, “ABaseEnemy”, which contains the virtual function “Attack()”, and from there derive “ASwordsmanEnemy” and “AArcherEnemy” both which override that function? And then from the AI Controller cast to ABaseEnemy and if it succeeds, execute Attack()? If that’s the case then what logic (aside from sensing components and the like) should the controller handle?
Should I be having separate AI Controllers for each class of enemy? Or should they have different behavior trees and tasks? Carrying over with the same example, the way the archer attacks is very different from the way the swordsman attacks. You need the archer to get within range, aim, pull back on the bow, and fire. The way he reacts is also different from the way the swordsman reacts when an enemy gets close. Should I have the three aforementioned pawn classes for the enemies, and a single AI Controller to handle basic things like sensing, and then implement the attack functions as blackboard tasks rather than as a new player controller or built-in to the pawn class?
For instance, the swordsman pawn would simply have an “Attack()” function, whereas the archer pawn would have a “Draw()”, “Aim()”, and “Fire()”, function. Instead of creating a new AI controller to call these functions, is it appropriate to call them from the tree? In other words, to turn these into blackboard tasks? So I could have separate trees for each class, the one for the swordsman would just get close to the player and perform the “SwordsmanAttack” task which would just call the “Attack()” function if the cast to the Swordsman succeeds. The Archer tree would also get close to the player, but then call the “ArcherDraw”, “ArcherAim” and “ArcherFire” tasks with some time delayed in-between, which I can use the “Wait” task for. This way I would end up with multiple trees and multiple pawns, but not a lot of AI Controllers.
Again, there’s the first way of doing it, handling most of the AI logic in the pawn classes. The tree would be very high-level and mostly consist of “get in range => attack”, and then completely implementing the “Attack()” function in both classes. So the archer, when “Attack()” is called, would call “Draw()”, “Aim()”, and “Fire()” with built-in timers in-between. I’m not sure if I like this method too much, because even though I’m getting rid of multiple trees and tasks, I feel like I shouldn’t be handling AI logic within the pawn itself. I remember reading a post from an Epic staff member in which he compared the pawn and AI controllers to a marionette and its puppeteer, I think that AI logic being handled like this in the pawn might go against that design principle.
The last way of doing it is to have multiple AI controllers, all which have the “AI_Attack()” function. The “ASwordsmanController” class would handle getting close and attacking, whereas the “AArcherController” would handle “Draw()”, “Aim()”, and “Fire()”. I kinda like this method since it flows will with the marionette/puppeteer principle, but at the same time something about multiple controllers turns me off. I guess it’s the added weight of expecting the designers to pair up every specific pawn class to its specific controller, or nothing happening? I’m not sure what it is, but I can get over it if someone tells me this is the right choice :rolleyes:.
Thanks again and sorry for the wall of text!