Hi a.clements,
It looks like you’re using the ShooterGame project… so I’ll contextualise in some places
How can I ensure that the AI controller is always attached to the bot pawns?
Generally, you’ll have to call Possess on the Pawn and then specify the spawned AIController.
However, by default the Spawn AIFrom Class node spawns a Pawn, an AIController (based on the specified AIControllerClass within the Pawn), possesses the spawned pawn with that spawned AIController (it calls SpawnDefaultController internally), and then runs the Behaviour Tree - so there’s no need to call another Spawn Default Controller or Possess as the Spawn AIFrom Class node does this internally.
How can I blueprint so that the AI pawns are split into teams as intended?
Unfortunately, your call to Set Members in GenericTeamId isn’t actually setting the TeamId (or doing anything at all) for your AI. Unfortunately, setting the GenericTeamId for all AIControllers (and any actor that implements IGenericTeamAgentInterface) hasn’t been opened up to Blueprints as of 4.13 and below, and requires a C++ solution. ShooterGame doesn’t actually use this interface (as ShooterGame was implemented before IGenericTeamAgentInterface), but instead sets and gets the team through a Player/Bot’s PlayerState (which too can only be modified through C++). To do this, you’ll have to use Solution 3, below.
Also, if I have 14 spawn points in the array, and I spawn at point 10, how can I exclude the point that I spawn at from being used as a spawn point by AI bots?
You could populate an ShooterTeamStart Array variable at the beginning of each match using Get All Actors of Class and setting the variable from that. Then as you spawn a new bot, you can call Remove Item on that array variable to remove the spawn point.
A few other things to note:
- In terms of the gameplay framework, it’s better to spawn your bots and players from a custom Game Mode Blueprint rather than the Level Blueprint - this means that you can reuse the spawn functionality between maps
- The ShooterGame PlayerStart’s already have a specified “Spawn Team”. When using the ShooterGame_TeamDeathmatch game mode, it will automatically choose a player start based on the Player/Bot’s team (specified through the Player/Bot’s PlayerState class).
- In terms of your blueprint, you should make functions to encapsulate and reuse implementation wherever you can i.e. you have two branches on your sequence that do the exact same thing for only two teams. Why not use a function so that you can have more than 2 teams and not have to duplicate effort when modifying behaviour?
So, with that mouthful of feedback… let’s get started!
Solution 1 - Use default ShooterGame_TeamDeathmatch team system (Super Easy, Console Command)
You can subclass ShooterGame_TeamDeathmatch with your own GameMode and set that to the GameMode of the map, then call the SetAllowBots console command to add a number of bots on BeginPlay:
ShooterGame will then spawn the specified number of bots, assign and balance the teams, and then start the game.
Solution 2 - Same as Solution 1, but from Command Line
You can start a ShooterGame instance with a particular map and number of bots from Visual Studio. ShooterGame will check the map load options when the game starts for the “Bots” option, and then spawn the specified number for you. This spawn will automatically assign and balance teams if you’re using the ShooterGame_TeamDeathmatch game mode (the Highrise map uses this).
Add this to the Command Arguments of your Project. The following will open the game without an editor, open the map “Highrise”, and add 5 bots to the game:
"$(SolutionDir)$(ProjectName).uproject" -game Highrise?Bots=5
Solution 3 - Minor C++ modification, open to custom team assigning
Since the ShooterPlayerState class doesn’t allow us to set/get the team ID of our Players/Bots, we need to be able to access this from BP. Within ShooterPlayerState.h, modify the SetTeamNum and GetTeamNum declarations to add the following UFUNCTIONs and Function Specifiers:
/**
* Set new team and update pawn. Also updates player character team colors.
*
* @param NewTeamNumber Team we want to be on.
*/
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = Team)
void SetTeamNum(int32 NewTeamNumber);
/** get current team */
UFUNCTION(BlueprintCallable, Category = Team)
int32 GetTeamNum() const;
This will make these functions callable from within BP; [BlueprintAuthorityOnly][3] will enforce that the function can only be called on the server (in a multiplayer game).
Next, we need to create a subclassed BP of the ShooterGame_TeamDeathmatch game mode and set this as your Game Mode. In this, we will do the spawn of your bots. Create a function called “SpawnBot” and then add the following in:
Note that the Teleport node is used as it checks for encroaching actors and adjusts the spawn location (be sure to also check the No Collision Fail on the Spawn AIFrom Class node). The FindPlayerStart node will internally call the ChoosePlayerStart function (within C++), and choose an appropriate player start that matches the team number (if we subclass the TeamDeathmatch game mode). This is why we need to set the Team Number before finding the player start.
On the match state changes, we can then spawn a number of bots in the WaitingToStart state, like so (you can do assigning of team numbers however you need):
If you want, you can also define how spawn points get chosen. Firstly, we need BP to be able to see what team the ShooterTeamStart is, so open up ShooterTeamStart.h and modify the int32 SpawnTeam; declaration by adding in a [BlueprintReadOnly][6] Property Specifier, like so:
/** Which team can start at this point */
UPROPERTY(BlueprintReadOnly, EditInstanceOnly, Category=Team)
int32 SpawnTeam;
Next, we can choose spawn points for Players and Bots by overriding the ChoosePlayerStart function within your GameMode. This is just an example implementation that will just choose the first found ShooterTeamStart that matches the team of that Player/Bot:
Hopefully this was comprehensive enough to nudge you in the right direction!