Jan 02, 2016
Thus far
We’ve played with both Scripted events and sequences for our pawns and found them to be quite similar.
Not to mention that we had some fun making our bot get squashed and run in circles.
Today
Although the current actions list is quite extensive, without surprise, there is some functionality which is not included.
So we’re going to build our own.
What are we building?
Well, one of the few things thing that I found I could not do in the list was cause the creation of a pawn with AI.
Sure, we could create the pawn, but there was no attachment to a controller.
What are we building?
I’ve found 2 methods that work well to create a pawn at a specific location and both are fairly straight forward.
The first relies almost exclusively on the current functionality for adding bots
What are we building?
The second, has a bit more to it, but if gives more direct control over the pawns creation.
Setup
Both of the classes derive from scriptedaction
class Action_CreateBot extends ScriptedAction;
These will auto-magically appear in the pull down menu of our actions list in the editor.
About Scripted Actions
Actually, setting up a new action is really quite easy.
All the magic happens around one function:
function bool InitActionFor(ScriptedController C)
About Scripted Actions
Although there are a one or two cases where it is not used, it’s fairly safe to say that anything we want to implement will be called from here.
The easy way
Starting by getting the bot into the game at any player start point, we just need to call a few function that already exist in our game type.
The easy way
The trick lies in accessing these functions. The ones we’re after are:
SpawnBot. RestartPlayer.
Both of which are defined in the Deathmatch game type.
The easy way
The nice part is that any gametype we use will have this class as a parent.
The easy way
So how do we get there from here?
As you already know, the game is accessible from just about anywhere in the code.
It just takes some typecasting.
The easy way
I our case, we’re given a controller, which can see the level.
And the level knows about the game, so we can safely cast to our parent class.
DeathMatch(c.Level.game)
The easy way
Cool! So getting our bots in is just a matter of
accessing these functions…
local bot newBot;
newBot = DeathMatch(c.Level.game).SpawnBot();c.Level.game.RestartPlayer(NewBot);
The easy way
And this spawns our dude into the level when this script is hit.
And because we’re using the existing functions, all the initializations are taken care of for us…gota like that!
The easy way
But spawning him at a random start point is not always what we want.
So how to we tell UT where to spawn something?
The easy way
Well UT’s spawn function takes parameters, one of which is location…should be simple no?
Just do an allactors search and match up a tag to an object…simple.
The easy way
But there’s a problem. We don’t have an allactors iterator…can’t see it, can’t access it.
We derive off object, not actor where all our iterators are defined.
…so, does this mean we’re screwed?
The easy way
Well, no…the cost of admission just goes up.
Object has a single iterator for us to use.
AllObjects
<Shudder>
The easy way
So we can get to any object’s tag, we just have to iterate over ALL the objects in the game.
Expensive.
The easy way
It works just like any other iterator
native(197) final iterator function AllObjects
(class baseClass, out Object obj);
The easy way
So how do we us it then…
And inside the InitActionFor function:
var(AIBOT) name loctag; // what we’re looking for
The easy way
foreach AllObjects(class'Actor', A){ if( a.Tag==locTag ) { newBot = DeathMatch(c.Level.game).SpawnBot(); c.Level.game.RestartPlayer(NewBot); NewBot.SetLocation(A.Location); newbot.Pawn.SetLocation(A.Location) }}
The easy way
And TA-DA! It’s in! Spawn anywhere.
But there’s a *small* catch to this method.
The restartplayer call causes a spawn at the player start in the conventional manner
The easy way
An then we set the players location to where we want him.
However, this leaves the spawn effect at the start point.
The easy way
So this works, but we can’t have this effect happening where the player can see it.
On to method number two, which is quite similar. We just change the guts of the InitActionFor function.
A better way
And add a bit more in the set up.
This method also allows us to define which controllers and pawn we want to use.
var(AIBOT) name objecttag; //Where to spawnvar(AIBOT) class<bot> bType; //What Bot typevar(AIBOT) class<Pawn> pType; //What pawn Type
A better way
And in the InitActionFor function, we redefine it as:
function bool InitActionFor(ScriptedController C){ local bot newBot; local Pawn newPawn; local actor A;
local UnrealTeamInfo BotTeam; local RosterEntry Chosen;
A better way
The UnrealTeamInfo and RosterEntry are needed to properly initialize the bot.
Everything else is a handle to an object that we’ll create here.
A better way
We keep the same search method…not much of a choice
foreach AllObjects(class'Actor', A) { if( a.Tag==locTag ) {
A better way
Spawn a pawn and a controller at the desired loaction, based on our search result (Actor a)
newBot = c.Level.Spawn(bType,,,a.Location, a.Rotation);newPawn = c.Level.Spawn(pType,,,a.Location, a.Rotation);
A better way
So they’re both in the game but need to be added to the game roster and initialized
BotTeam=DeathMatch(c.Level.game).GetBotTeam();Chosen =BotTeam.ChooseBotClass();Chosen.Init();
DeathMatch(c.Level.game).InitializeBot(newBot,BotTeam , Chosen);
A better way
And lastly, we need the controller to own the pawn. This is done with the Posses function.
newBot.Possess(newPawn);
A better way
Give it a run, it works nice…
...but there’s something
missing…
A better way
The pawn comes in, with a brain, great!
But where the heck is the gun!
A better way
And, notice that once you frag them, spawn back into the game with all there toys.
So we missed something…
A better way
Actually, just one line…ain’t that always the way?
If you route through the deathmatch class you find this
function AddDefaultInventory( pawn PlayerPawn )
A better way
And it does, just what the name says…
Accessing it is nothing new as well,
DeathMatch(c.Level.game).AddDefaultInventory(newPawn);
A better way
Lastly, in our default properties, we need to set the ActionString to something meaningful.
ActionString="Spawn EzeBot"
A better way
As best I can tell, the action string is used exclusively for debug purposes and this is the text that is thrown to the screen.
This is done from the ScriptedSequence object. Beyond that, I’ve not found any functionality.
That’s all well and good…
But, continually adding bots is only so much fun.
The real fun lies in killing them off
Killing our pawn
So, from our scripted sequence, time to do that.
This part is actually really easy if you controller already has a pawn associated to it…Well go over how to get one in a minute if it doesn’t.
Killing our pawn
So the new action I defined as:
class Action_Gib extends ScriptedAction;
function bool InitActionFor(ScriptedController C){ c.Pawn.ChunkUp(c.Pawn.Rotation, 1.0); c.Pawn.PlaySound(sound'PlayerSounds.Final.Giblets1');
return true; //must return true or UT crashes}
Killing our pawn
Yup, that’s the whole thing.
The actual kill is build into the pawn already, it’s just a matter of accessing it.
Can you guess what it does?
Killing our pawn
Before:
Killing our pawn
After:
SPLAT!
However, I am a bid confused as to how he got 2 skulls though…
Killing our pawn
Yup, it turns our pawn into a bunch of giblets.
The best thing is, you can now just add this to the list of Scripted events for your AIScript.
Killing our pawn
So long as you have a pawn associated with it that is…
Integration into Gameplay
Ok, I believe that it’s great for telling stories to the player and watching things happen.
But can the player interact with it?
In a word, yes.
Integration into Gameplay
And this is where the code monkeying starts to take place.
One of the fun levels in the original UT was DM-pressure…
Integration into Gameplay
You had to go into a pressure chamber to get the rocket launcher.
Integration into Gameplay
However, while in there, you could be sealed in by the push of a button which would cause the player to pop like a ballon.
Tones-o-fun !
Integration into Gameplay
You guessed it, we’re going to build something similar.
Just an FYI, something like this is much better suited to a volume or a zone, this is just one possible example.
Integration into Gameplay
The first thing I did was set up a level so the we could test this out fairly easily…
Starting with an oddly shaped brush which was built using the deintersect tool.
Integration into Gameplay
I’m going to use this so I can box my enemy pawn into a corner…
Then, used a transparent texture so I could see the bot get splattered.
Integration into Gameplay
I also left a gap on the edges so I could use a translocator to get out.
But too small for a bot…
Integration into Gameplay
Now, over to the code.
The search method we’re going to use here is very similar to the one we just used to find the matching tag.
Using all objects <shudder>
Integration into Gameplay
But we have to put some logic in to to a “radius search”.
class Action_FindNewPawn extends ScriptedAction;
var()int radius;
DefaultProperties{ radius=128}
Integration into Gameplay
Next up is a simpler iteration over all the objects <shudder> and test to find the closest one.
Integration into Gameplayfunction bool InitActionFor(ScriptedController C){ local pawn p, closest; local float dist, closestDist;
foreach allobjects(class'Pawn', p) { if(closest!=none) { dist=Vsize(p.location-c.Location); if(dist < closestDist) { closest=p; closestDist=dist; } }
Integration into Gameplay
else { closest=p; closestDist=Vsize(p.location-c.Location); } }
if( closestDist<radius) c.Pawn=closest;
return true; //must return true or UT crashes}
Integration into Gameplay
Nothing overly fancy…
But, now the fun stuff…it’s ready to use.
Jumping back to the editor, we set up our scripted triggers.
Integration into Gameplay
I place the scripted trigger in my room and the activation trigger a safe distance away.
Activation trigger
Scriptedtrigger
Integration into Gameplay
And then set up my scripts:
Integration into Gameplay
Wait for the activation trigger
Integration into Gameplay
Spawn some special effects for show…
Integration into Gameplay
Find the closed pawn which is within 230 UU’s
Integration into Gameplay
And then call my ready-made gib function.
Integration into Gameplay
Lastly…do it again
Integration into Gameplay
Do an addbots console command and …poof!
Caged like a rat!
Integration into Gameplay
Then when you hit the trigger,
ZAP!...no more bot.
That’s all folks
We’ve now customized our own action scripts, hope ya found it fun…