210 likes | 405 Views
Artificial Intelligence in Unreal Tournament 2004. Spawning a custom bot and the AI/Weapon Interface David Bierbaum Fall 2005. AI/Weapon Interface.
E N D
Artificial Intelligence in Unreal Tournament 2004 Spawning a custom bot and the AI/Weapon Interface David Bierbaum Fall 2005
AI/Weapon Interface • First, check out this page here on the wiki which lists most of the functions that the AI uses available to override in the Weapon class, along with the comments (or lack thereof) that appeared above them in the UnrealScript itself. Most of what I cover in this tutorial is very sparsely documented, if at all.
AI/Weapon Interface • That page is for UT2003.. UT2004 added two more functions to the Weapon class: /*used by bot AI should return true if this weapon is able to heal Other*/ function bool CanHeal(Actor Other) { return false; } //called by AI when camping/defending //return true if it is useful to fire this weapon even though bot doesn't have a target //for example, a weapon that launches turrets or mines function bool ShouldFireWithoutTarget() { return false; }
AI/Weapon Interface • If you don’t override the CanAttack function, the WeaponFire class has the following function you can override: function float MaxRange() which just returns the maximum range of the weapon in UUs (find more info on Unreal Units and conversions here on the wiki.) The CanAttack function uses the MaxRange in its calculation to determine if the Bot is too far away to use the weapon or not.
AI/Weapon Interface • A good rule of thumb for SuggestDefenseStyle() is to return the opposite of SuggestAttackStyle(). For example if you return 1.0 for attack style (charge!) you would probably want -1.0 for defense (run!) • Another tip is – design such that you have to write as little AI/Weapon code as possible. If your weapons use the base classes that most closely approximate them (such as a sword and a shield gun, or sling and flak cannon) then you won’t need to override as many functions because if the AI treats your sling like a flak cannon you might not even notice the difference!
AI/Weapon Interface • Determining attack distances can be tricky. Using the log() function to write out how far away an enemy is very helpful, for example in the GetAIRating function: local Bot B; local float EnemyDist; B = Bot(Instigator.Controller); if ( B == None ) return AIRating; EnemyDist = VSize(B.Enemy.Location - Instigator.Location); log(EnemyDist); Through this method I discovered that the minimum distance a Bot will attack with is around 65 UUs (the two bots are right next to eachother at this point, any less and they will never attack!)
Spawning Custom Bot • First step is to override the basic Bot class or one of its super classes, such as xBot. For more info on this, check out this page on the wiki. • There are two ways you can define the spawn point for your Bot. One is to use the level editor to add a new start location. Start locations can only be located at NavigationPoints. All the NavigationPoints defined for a level are loaded into the following variable defined in LevelInfo: var const NavigationPoint NavigationPointList;
Spawning Custom Bot • I quickly discovered however that this list is READ ONLY and so you cannot create a NavigationPoint dynamically. This is fine if you want your Bot to always appear in the same location. • To dynamically spawn a Bot anywhere in the game during play, you must first override the RestartPlayer function from GameInfo.
Spawning Custom Bot • The Game calls RestartPlayer each time a Bot needs to be placed in the level, both after it dies and when it is first created. • By creating two vector members in your Bot class such as: var vector StartLocation; var rotator StartRotation; You can initialize these by passing in parameters to a SpawnBot function and use them as your Bot’s starting location instead of the usual PlayerStart returned in RestartPlayer. The actual spawning function will be covered at the end of this tutorial.
Spawning Custom Bot • In each spot in RestartPlayer that a PlayerStart is set you will have to check to make sure you are not dealing with your custom bot before returning that point. Remember the GameInfo deals with all bots and players in the game, so make a check like this: if (MyBot(aPlayer) == None) { aPlayer.Pawn.Anchor = startSpot; aPlayer.Pawn.LastStartSpot = PlayerStart(startSpot); } This will ensure that only if the casting aPlayer to MyBot fails (in other words we are not dealing with our custom bot) will we use the convensional PlayerStart.
Spawning Custom Bot The only place this is different is when the actual Spawn function is called to create the Bot in RestartPlayer. Here we want to use the new variables we declared like this: if (MyBot(aPlayer) == None) { if ( aPlayer.PawnClass != None ) aPlayer.Pawn Spawn(aPlayer.PawnClass,,,StartSpot.Location,StartSpot.Rotation); } else { if ( aPlayer.PawnClass != None ) aPlayer.Pawn = Spawn(aPlayer.PawnClass,,,MyBot(aPlayer).StartLocation,MyBot(aPlayer).StartRotation); }
Spawning Custom Bot • One thing to remember when overriding a function like RestartPlayer is that this function is actually overriden in several base classes. For example if your GameInfo extends xMutantGame, xMutantGame actually extends DeathMatch and DeathMatch extends GameInfo. Each of these has their own implementation of RestartPlayer that you want to change! So we cannot simply do as usual and create a new RestartPlayer, add our functionality to the beginning and then call Super.RestartPlayer(). What you do is copy the code from every implementation of RestartPlayer all the way down to the base class into your custom Bot class (in sequential order of course) and then never call Super.
Spawning Custom Bot • IMPORTANT – If you are adding bots during gameplay according to some trigger, you probably don’t want them to remain forever (in other words, respawn after they are killed) because then the level could eventually get overrun by your custom bots. There are two steps to take to avoid this.
Spawning Custom Bot • Override the Dead state in your custom Bot class, and replace the code for the MPStart and TryAgain states with the following: YourGameInfo(Level.Game).NumBots --; YourGameInfo(Level.Game).RemainingBots --; Destroy(); MPStart (MultiPlayer Start) and TryAgain are entered when the Bot is trying to respawn, so this will destroy it instead and tell the game we have one less bot. • (Note that you can always access an instance of the current Game in your Bot code by using Level.Game)
Spawning Custom Bot • This was the most elusive one for me – in the default properties for your GameInfo, be sure to include the following line: MinPlayers=0 This ensures that the Game will not create another Bot (a regular Bot, not your custom Bot because that was destroyed by the code in its Dead state) in order to fill the gap left by your custom bot.
Spawning Custom Bot • To set a target for your Bot, the easiest way would probably be to create a new SquadAI class. SquadAI has two functions: • bool SetEnemy( Bot B, Pawn NewEnemy ) • bool FindNewEnemyFor(Bot B, bool bSeeEnemy) • Which Bots use to aquire new enemies during game play. If you create a variable in your Bot class (such as var Pawn myTarget;) and have these two functions always return that then you can have total control over what target your Bot will have. Remember that SquadAI is used for every Bot in the game, so check if (MyBot(B) != None) before returning MyBot(B).MyTarget. • In order to change which SquadAI is used in your game depends on the Game type, for example in the DeathMatch Game type you would add a DefaultProperty such as: DMSquadClass=Class‘MySquadAI‘ • Remember (again) that this SquadAI will be shared between ALL Bots so make sure to make checks to make sure you are dealing with your custom Bot in each function.
Spawning Custom Bot • Finally after your Bot is completely set up, you will need the actual function to spawn it during the game. This is accomplished by writing 3 new functions in the GameInfo class. Simpy copy the functions ForceAddBot, SpawnBot, and AddBot from the GameInfo class and rename them to something such as SpawnMyBot. • This is different from overriding, because now you can add new parameters such as startLocation and startRotation to these functions, as well as changing their functionality to deal with just your new Bot class. • Now from anywhere in the code that has access to the current instance of GameInfo you can call the ForceAddMyBot function to create your Bot at a specified location!
Spawning Custom Bot Example code – function MyBot AddMyBot(vector location, rotator rotation) { local MyBot NewBot; NewBot = SpawnMyBot(location, rotation); ……. } function MyBot ForceAddMyBot(vector location, rotator rotation) { // add bot during gameplay if ( Level.NetMode != NM_Standalone ) //note, the following line may override what you have done with setting //MinPlayers = 0 for the default properties, test during net play to make sure MinPlayers = Max(MinPlayers+1, NumPlayers + NumBots + 1); return AddMyBot(location, rotation); }
Spawning Custom Bot function MyBot SpawnMyBot(vector location, rotator rotation) { local MyBot NewBot; local RosterEntry Chosen; local UnrealTeamInfo BotTeam; BotTeam = GetBotTeam(); Chosen = BotTeam.ChooseBotClass(“Custom Bot"); if (Chosen.PawnClass == None) Chosen.Init(); //amb NewBot = Spawn(class‘MyBot', , ,location, rotation); NewBot.StartRotation = rotation; NewBot.StartLocation = location; …. }