Hello again. It has been a few weeks since my last blog posting, and I am back again to do another. This time around, I am going to explain the Flash-UDK Workflow and take you through something I created for my most recent project. I will be showing you how I made a custom HUD in Flash, with a checkpoint counter on it, and how I got it to work in UDK. Let's begin.
First off, I will show you the final product:
As you can see, the HUD does not really do anything except display the number of checkpoints the player has left to use in the lower right hand corner. This differs drastically from the normal UDK HUD which has things like health, ammo and what weapon is currently equipped. For our custom game, all we needed was this very simple HUD. Getting this working in UDK, however, is another challenge.
There are two ways to get a HUD in UDK. The first way, and not the recommended way, is to create your HUD, import it into UDK and use Kismet to open a GFxMovie to display your HUD. It works, but it can be very buggy and unpredictable. Especially if you need to use other Flash elements in your game. The way I did it, and the recommended way to do it, is to create your HUD, import it into UDK and make a custom gametype script that will tell UDK to use your HUD and not the default one.
I am going to start by showing you the Flash first. Below is a picture of the HUD from the above picture open in Adobe Flash:
You cannot see anything as the text is colored white, but it is contained within those two blue boxes in the lower right. The left box holds text that is static, meaning it does not change. The right bow contains a dynamic text field, which means it can change at some point. Now, I have named the dynamic field CPLeft. Let's see the actual ActionScript that controls this HUD:
stop();
var checkpointsLeft:Number;
var keyListener = {};
function setCheckPointsLeft()
{
checkPointsLeft = 5;
_root.CPLeft.text = checkPointsLeft.toString();
}
function useCheckPoint()
{
if(checkPointsLeft > 0)
{
checkPointsLeft--;
}
else
{
checkPointsLeft = 0;
}
_root.CPLeft.text = checkPointsLeft.toString();
}
setCheckPointsLeft();
keyListener.onKeyDown = function()
{
var theKey = Key.getCode();
if(theKey == 88)
{
useCheckPoint();
}
};
Key.addListener( keyListener );
NOTE: This code is attached to the stage itself, not the text fields. If you are unfamiliar with Flash, I suggest you look up how to add code to things in Flash.
Don't worry, I will go through this code line by line to show you how it works. First of all, the stop() call just makes sure that Flash will not advance to another frame. The next two lines are variable declarations, one being a Number (like int in most other languages) and the other being just another variable. In Flash, you do not need to give your variable types if you don't have to, which is why the keylistener variable has no type.
The next two functions are purely declarations and implementations. They do nothing on their own. They are put in that spot to make sure they are declared before use. The function setCheckPointsLeft does a very simple job. It sets the global variable checkPointsLeft to 5, then assigns the number 5 to be written in the dynamic text field I described earlier. The call to the dynamic text field is quite simple, _root tells the code we are accessing the root of the stage (where the text fields are located), CPLeft is that name of the dynamic text field (can be thought of as an object) and text is the name of the variable that holds the text that will be written in the dynamic text field. The toString() function call turns the data in the variable checkPointsLeft into a string so it can be displayed by the text field. So, in simple terms, it sets the field to display the number 5.
The function useCheckPoint does what its name implies, it uses a checkpoint and subtracts it from the number left. The if-else statement is a bit of error checking to make sure the number displayed in the text field does not go below 0. Then the line _root.CPLeft.text = checkPointsLeft.toString() uses the same idea as the above description.
Then we call the setCheckPoints() function so it is run when the Flash is started (i.e. when UDK draws it on the screen). The next line is another function declaration. However, it is a little different from the other two. In this case, we are telling the keyListener variable to use its onKeyDown function in a certain way, which we define below that line. We create a variable to hold the key code that is returned from the keyListener. We then check it against whichever key we want that will serve as the button to decrease the amount of checkpoints we have left (meaning the key we setup in UDK as the set checkpoint key). 88 is the ActionScript keycode for the letter x, without having the shift button pressed. Then, after that function declaration, we tell Flash to add our custom keyListener and use it to determine if we have pressed the x button or not.
Now, that wasn't so bad. Let's move on to the UDK stuff. First of all, you would need to save your Flash file in this path (assuming you installed UDk to your C drive, otherwise change that letter to the drive letter where UDK is installed): C://UDK/UDKGame/Flash and put it into a folder with a different name. In my case, the file path of my Flash files was C://UDK/UDKGame/Flash/CPFlash. Now that it is saved, you will need to publish the Flash using FlashPlayer 8 and ActionScript 2.
Now, inside UDK, you would open the Content Browser and click the Import button on the lower left. Navigate to the place where your Flash is stored, and double click the file you want to import. UDK will create a package for you with the name being the same as the folder where the Flash is stored, then click OK. Now, we step into UnrealScript.
This part is not very hard. If you are familiar with how to write a custom gametype and get it to work in UDK, you can skip this step.
What we are going to do is create a custom gametype that will use our HUD instead of the default UDK HUD.
class NewGame extends UTDeathMatch;
DefaultProperties
{
HUDType = class'MyMod.NewHUD'
bUseClassicHUD = true
}
The first line tells us the name of the class (MUST BE THE SAME AS THE FILE NAME) and what it extends from. In our case, NewGame is extending from UTDeathmatch. DefaultProperties can be found in all UDK scripts, and is the place where variables can be set. So, we are telling UDK that our HUDType is of a class we are going to create soon called NewHUD. Then, the bUseClassicHUD = true tells UDK that we are using a classic HUD. Save this file.
Next, make two new text files in the MyMod folder and name them NewHUD.uc and NewGFxHUD.uc. Open NewGFxHUD and paste in this code:
class CPGFxHUD extends GFxMoviePlayer;
function Init(optional LocalPlayer PC)
{
Start();
Advance(0.0f);
}
function UpdateHUD()
{
}
DefaultProperties
{
MovieInfo = SwfMovie'<insert name of package where your Flash is stored in UDK>.<name of Flash file>'
}
Again, we are first defining the class and what it extends from. Then, our Init function tells UDK to start the Flash file with Start() (this MUST be called) and to keep executing the function with Advance(0.0f). Putting something larger than 0 in the parameter list of Advance() will make UDK sleep for that duration before continuing.
UpdateHUD does nothing, but if we wanted to have dynamic health bars or ammo counters, we would put code in that function to update those specific HUD elements when needed. The DefaultProperties is where we tell UDK what Flash file to use for the HUD. We are setting it to the Flash file you imported into UDK earlier. Save this file.
Open NewHUD.uc and paste in this code:
class NewHUD extends UTHUDBase;
var NewGFxHUD HUDGFx;
simulated function PostBeginPlay()
{
super.PostBeginPlay();
HUDGFx = new class 'NewGFxHUD';
HUDGFx.setTimingMode(TM_Real);
HUDGFx.Init(LocalPlayer(PlayerOwner.Player));
}
event PostRender()
{
HUDGFx.UpdateHUD();
}
DefaultProperties
{
}
We are declaring our class and what it extends from, and then creating a variable named HUDGFx that is of type NewGFxHUD. Remember the class we created earlier with the same name? We are using an object of that class to tell UDK to draw our HUD. The function PostBeginPlay is defined next. The first line super.PostBeginPlay() tells UDK to call the PostBeginPlay function and continue from that point. Then, we set the variable to a new object of type NewGFxHUD. Then, we set the timing mode of the HUD to Real, which tells UDK to keep updating the Flash, no matter what. We then call the Init function from our previous class and tell it to set the HUD to the Player so that they can see the HUD.
The next function is not really a function, but an event. This even calls the UpdateHUD function from the previous class. DefaultProperties can be left blank in this class, but it has to be present or UDK will error out.
Now, save that file and close it. You will now have to navigate to UDK/UDKGame/Config and open DefaultEngineUDK.ini. Navigate to the line that says ;ModEditPackages=MyMod and remove the semi-colon. This tells UDK to include scripts you wrote in the MyMod folder, as it does not do this by default. Then save that file.
Open UDK and you will be prompted to rebuild the scripts. Click yes and let it run. If we did everything right, it will have no errors or warnings. Now, open UDK again and it will load the editor. Under the View menu, click World Properties. Then, expand the Game Type field and change both dropdowns to NewGame. Then start the game by clicking the green arrow in the upper right corner. I would suggest testing this on a pre-built level. If everything worked, you should see your HUD on screen. Press the X button to make sure our HUD updates the number of checkpoints left.
I hope you enjoyed this tutorial. The above example can be applied to anything that has to be counted down (or up, by changing the Flash) on key press. We used it to keep track of the number of checkpoints left, but you can edit it to be anything you want.