AI bot spawning

So I have a level blueprint that spawns an array of AI bots, the TDM player starts are split into team 0 team 1. What team a player is in depends on which start point the player spawns at. At least that is how it’s supposed to work. Currently the AI controller is not always attaching to the bots, and periodically I will spawn in the middle of the enemy team because I will sometimes spawn at a team 1 location and all the bots are team 0. The randomization of player spawn location is intended. The AI bots are not being split between both teams though even though each start point has a variable which can be set to team 0 or team 1. How can I ensure that the AI controller is always attached to the bot pawns? How can I blueprint so that the AI pawns are split into teams as intended? 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?

Thi is my blueprint.

Ok, so I have re-written the blueprint rather extensively since my original post, the blueprint is now large enough that it requires 3 screenshots to be readable. In the first one I am getting the number of players on the map and taking the modular division value as an amount of players on each team and then adding the remainder to team 0. This way if there are 11 players team 0 will have 6 players and team 1 gets 5 players as an example. Otherwise each team gets an even number of players and bots.

I then transition into a sequence that defines each team.

Team 0 is defined by a while loop that will spawn a bot for every missing player on the team to a maximum of 19 bots.

After resetting the value of team count, team 1 is defined the same way with the same conditions.

The problem here is as it was before, every spawned bot is on the same team regardless of which loop is running when they are spawned. Again, this leads to a situation where a player is spawning surrounded by hostile bots, except with the new blueprint this time it is guaranteed to occur. I have to admit, spawning and being surrounded by hostiles was quite funny at first, but it lost it’s appeal very quickly. Now it’s just annoying. I also fixed the lack of bot AI posession by adding a GetActorRotation node and connecting it from the array into the rotation of the spawn AI node.

Hi a.clements,

It looks like you’re using the ShooterGame project… so I’ll contextualise in some places :slight_smile:

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:

  1. 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
  2. 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).
  3. 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! :smiley:

Hi thanks for the reply and feedback. I was wondering if I should modify the player state to make the TeamNum callable in blueprints, but i was unsure if it was a UPROPERTY or a UFUNCTION. Thanks for clearing that up.

No worries!

You’ll probably notice that you still can’t choose the team for a real player. In that scenario, we can also open up the ChooseTeam function to be called or overridden.

Open up ShooterGame_TeamDeathMatch.h, locate the following:

/** pick team with least players in or random when it's equal */
int32 ChooseTeam(AShooterPlayerState* ForPlayerState) const;

And then modify it to look like so:

/** pick team with least players in or random when it's equal */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Team)
int32 ChooseTeam(AShooterPlayerState* ForPlayerState) const;

You’ll have to open up ShooterGame_TeamDeathMatch.cpp, and replace:

int32 AShooterGame_TeamDeathMatch::ChooseTeam(AShooterPlayerState* ForPlayerState) const

With:

int32 AShooterGame_TeamDeathMatch::ChooseTeam_Implementation(AShooterPlayerState* ForPlayerState) const

You can then override this function within your GameMode like you did with ChoosePlayerStart (so long as it subclasses ShooterGame_TeamDeathMatch). This function will be called by the C++ gamemode to allocate a Team Number to your players and bots.

So, now that we’re able to directly modifying the team of Bots and Players, and we can change their spawn location, we can then just use our original solution to spawn bots (and change teams/spawn location through our ChoosePlayerStart and ChooseTeam overrides):

Hi thanks again for the feedback. Something weird is happening. I have modified (read copy and pasted) the code in the ShooterPlayerState.h as you suggested, but the SetTeamNum and GetTeamNum functions are not callable even though they are set to blueprint callable within the code. I have saved the C++ code and recompiled within the ue4 editor several times and the functions are just not there.

Those two functions will only show in the context of a ShooterPlayerState object. Are you making sure that you get a reference to the player state of a controller, and are casting that player state to a ShooterPlayerState?

For example, Set and Get Team Num will only show in the context menu if you’re dragging from a reference to a ShooterPlayerState object:

Otherwise, if so, you may have to close the editor and recompile from Visual Studio, as the hot reload system sometimes doesn’t cascade the changes on a class.

Hi thanks for the prompt reply. This is what I am seeing in regards to blueprint.

I’m not sure why as the relevant lines of code are the same as the lines of code you posted in one of your replies.

Do you mind attaching the ShooterPlayerState.h file in a reply?
Also, in Visual Studio, have you tried hitting Build->Clean Solution and then rebuilding?

Rename the .h to .txt and attempt another upload :slight_smile:

Also, for the rebuild, try deleting the following files and folders within your project:

  • .vs/
  • Binaries/
  • Build/
  • DerivedDataCache/
  • Intermediate/
  • Saved/
  • ShooterGame.sln
  • ShooterGame.VC.db

Now regenerate the Visual Studio project files by right clicking ShooterGame.uproject in your project folder and click Generate Visual Studio project files.

I have been trying to upload the header file, but I am getting an ‘The attachment is not permitted because the file type is invalid’ error. I have also tried to attach as a code block but it is to long. I have tried rebuilding the solution in visual studio and both in my project and also in the generic shooter game i get a lot of “file not found” because the header file paths are not absolute paths.

It’s probably something really stupid like you have to have absolute paths.

link text Other than the renamed variables it is the same as the ShooterPlayerState.h file.

Hmm, it looks fine. I assume you modified the original ShooterPlayerState.h and tested that as well.

Did the regeneration and rebuild work this time?
If not, how did you refactor the ShooterGame project? I assume in Visual Studio you did a Search and Replace for Shooter with IwoJima on the current project and then renamed any files?

I did regenerate and build.The regeneration worked without a problem, but when it came to building the DLL’s it fails with the error “could not be compiled. Try rebuilding from source manually”. Yes I did do a search and replace and renamed the source files. it is odd though because when I do the same process with the shootergame example everything works as it should.

I have reverted back to using the standard ShooterGame components rather than trying change the variable names etc. I hope I won’t get in trouble from my educational institution, but it won’t work any other way. I have been following your posts to the letter, but there is one node I cannot find. Any suggestions?

107595-node.png

You have to right click the input node, and select Add call to parent function :wink:

107878-2016-09-21_08h01_17.png

Hmm, sounded like you may not have modified the ShooterGame.build.cs files or may have missed modifying ShooterGame.Target.cs?

It’s working now. if you are following this tutorial then don’t forget to set the world override to none. It won’t work otherwise.

107899-blueprint.png