Since early 2002, Shee Labs has been a group of individuals working to produce free game and media downloads. With ever-fluctuate staffing, Shee Labs continues working behind the scenes on various exciting projects for a bewildering array of areas. Shee Labs ceased to exist in September 2009.
This second tutorial will teach you how to write a mutator with configurable properties, and how to create and set up a user interface scene for it.
Posted by ambershee on Nov 30th, 2007
Basic Server Side Coding.
If you haven't done so already - you should probably check out the mutator basics in Unreal Learning #1: My First Unreal Tournament 3 Mutator, and do a bit of extra learning yourself based on that tutorial.
So we can write a mutator, that's great! We're well on our way to becoming Unreal Programming gurus. But wait? What if we want to let the players choose what they want instead of us? For this we need to use a mutator with a Configuration Menu. Configuration Menus are the menus that pop up when we select a mutator, and hit the configuration button in the mutator selection screen. They allow us to set predefined properties to what we want them to be for the particular game we're about to play.
By now, you should already have a good idea of how to set up your mutators, getting the right directory layout and how to create new classes - so we won't go over that again just yet.
Create the folders and a new mutator class just like you would for any other mutator, and don't forget to add it to the ModPackages in the ini files. This time I decided to call my folder PlayerTweak and the mutator PlayerTweakMutator. It always helps to have descriptive folder and class names - it helps you and the user know what they are, and helps prevent them from clashing with anyone elses mutators. Why PlayerTweak? Because that's what we're going to be doing - we're going to let the mutator customise some player statistics.
Time to do some scripting! Let's open up our new class file and set it up properly just like we did before. We're going to also want to create some variables. I chose to modify the player speed, jumping height and health values.
Notice something different from the way variables are normally declared? That's right - we've declared our variables as config variables. This means that their values can be modified by our configuration. It also means that instead of having their DefaultProperties defined by the mutator, we put them in the ini file instead. We will do this later. You'll also notice we've declared our class in a new way, we've added the config keyword to that too. We've also told it that the file it should put the configuration variables in for us is called PlayerTweak - or UTPlayerTweak.ini.
Now, we've been using the CheckReplacement technique to swap in and out our objects up until now. We could do it again, by creating a new type of UTPawn and changing what we want - but there's a better way! Instead, we're going to use a ModifyPlayer function instead. The ModifyPlayer function is useful, as it allows us to change a few properties without the need to create a new class for a new type of Pawn and overcomplicate things.
So what's going on? Well, we're going through every Pawn in the game, and resetting a few of their default variables. If we wanted to change some other features, we could go into Pawn.uc, and take a look and add them in. For now we'll stick with what we've got. Why are we dividing the Speed and JumpHeights by 100? Well that's because we're going to let the player specify a percentage of the speed and jump, rather than hard-setting an actual numerical value.
Now for the tricky, new bit.
This is where things start to get more difficult and our programming expertise are going to be stretched. In order for our configuration menu to appear in the mutators menu, we need to actually create the menu. This involves not only a bit of code, but we also need to use UnrealEd to make out menu scene. Don't worry, we'll be learning how to do all of that in good time. Create a new class for our interface. We will be extending the class UTUIFrontEnd - which is the basic class for all Unreal Tournament 3 menu systems.
Ok, so what do we need to put in it? Let's think. We're going to need to be able to set our player statistics. Seeing as they're all statistics that have a lot of values, we'll use a slider. You could spend some time looking at what other types of widgets there are (hint, use UnrealEd and create a new UIScene in the Generic Browser, and see what's on offer). The sliders are useful because we can set a minimum value, a maximum value and our player can select anything between that minimum and maximum. Let's create some sliders - we can declare them just like variables. We are also going to make them transient. This means that the variables will carry over between things like level changes, which is a useful feature.
Fantastic, we're on our way. Now that we have these sliders, we need to determine what values they'll actually have. If we go into the class UTUISlider.uc, we'll find out that they have a variable in there called SliderValue (it's a UIRangeData). UIRangeData is a special variable that contains other variables - just like a struct in many programming languages. We don't need to worry about how these are made, but you can take a look at them by reading through the unrealscript source now if you like, as they aren't very complicated.
We will set our values when the player opens the configuration menu - for that we can use the SceneActivated event. This means we will tell our sliders what the sliders in the menu scene are called so that we can find them, and we will also tell them what our minimums and maximums are.
Wow, that's a lot! We can actually cut a lot of that into less code, but for now we'll keep it easy and simple to understand. It shouldn't make a difference to how the unrealscript compiler handles the code anyway. You'll notice how we told our code what our sliders are called - we need to use the FindChild function to find the slider (e.g sliSpeed). In order for the FindChild function to work, we need to cast it into the correct type of class - UTUISlider. If you don't know how type casting works, then don't worry about it just yet, we'll cover that in an advanced unrealscript tutorial - for now we just want to know how to get configuration menus working.
Ok, so we have sliders. But now we have new problems to solve - we can play with the sliders all we like, but we'll never get anywhere without a button to tell the game that we're finished choosing our player statistics. For that we look at the ButtonBar .
The ButtonBar appears in many UT menu scenes. It is the set of buttons that will appear at the bottom of almost every menu in the game. We will want one of these to set our variable values for use in the game and we'll want one to get out of the menu. We might as well make the same button do both jobs for us, so we'll use the SetupButtonBar function that's been given to use by UTUIFrontEnd and tell it to make us a button.
Wow, that was easy! But how does it know what to do? The Button Bar is actually going to be already there in the menu scene ready for us, all we had to do was add a button using the AppendButton function. We told it which string to use (in this case we used the already existing 'Back' string). We also needed to tell it what function to call when we press it. This is the OnButtonBar_Back function that we're about to write - and the already existing scripts will handle the rest of the complicated stuff for us.
We're now finished with code. There certainly was a lot of it, wasn't there? So what does this new function do? Well, let's take a good look at it. To keep the unrealscript compiler happy, our OnButtonBar_Back function has to return a bool - so we just returned true because the function (probably!) worked. We also wrote code to found out what values the player chose to play with, and used them to set the variables in our mutator, so that it can use them in the ModifyPlayer function - we did this by getting the value of the slider with the GetValue function - then set the default value of our mutator class for each variable. Very clever, very clever indeed. If you're not very sure what is going on, now is a very good time to step back and read through all this code we've been writing. It's not actually all that complicated, it just looks that way!
With the code out of the way, there's just one big thing left to do - we need to create a fancy new menu for the mutator! We should be able to build our scripts now in the unrealscript compiler - do that now, and if it doesn't work, step back and take a look at the errors.
No errors? Great! Menu time!
Now we need to open the Unreal Editor. We can find that in our start menu - or we can create a new shortcut with the editor command switch just like we did when we created a short cut to make our scripts. The first thing we see is the Generic Browser - handy, because that's exactly what we're looking for.
Back click in the window of the Generic Browser, and select the New UIScene Option. What is a UIScene? It's a special type of object - it's a menu in the game, or any other type of 2d interface in fact. You should now be faced with this window:
Ok, we need to create a new package to put our UIScene in. Naughty, yes, we could do it with just one package for our entire mutator - but we won't worry about that just yet. We want to get it working now and we can worry about that when we have learnt more about Unreal. Call the package UTUI_Scenes_PlayerTweak. If we wanted to put it in a folder inside the package, we could give it a group name, but since our package is going to be very empty, we won't bother with unecessary organisation. Give the menu the name PlayerTweakConfigMenu. Now we need to tell the editor that our UIScene isn't just a UIScene, but it's also a UTUIFrontEnd_PlayerTweakMenu. Go into the UISceneClass drop down list in the options and look for our UTUIFrontEnd_PlayerTweakMenu class. We're ready to create it now, so we'll hit the OK button.
Ok, we should now be looking at this rather daunting UIScene Editor. Fortunately, it's very easy to work with and we'll be done in no time at all!
We need to create a background. I just created a plain black one with a title in Paint, but you could make yours as fancy as you please. We also need to import it - so minimise the UIScene Editor, and back to the Generic Browser. Go into the File Menu in the top left. Once we've found what we want to import, we hit OK and it's another menu like we had when we had to create something! Great, we can import the image to our menu scene package file that we created. Make sure to select TEXTUREGROUP_UI from the LODGroup options. We don't want any funny visual artifacts on our nice 2d interfaces. Remember that any texture you import must be in a power of two size (512x512, 1024x1024, 512x2048 etc) - otherwise UnrealEd cannot import it for you. If you're lazy, you can just use mine if you want:
Back to the UIscene Editor!
We can create our background from our newly imported texture by creating a widget. A widget is any kind of object we can put in our UIScene. All we have to do is back click in our currently rather empty grey area (this is where we draw our menu scene) and go into Place Widget and select an Image widget. It will magically create a new widget - but yuck! It's a horrible green square. Never fear - use the red squares in the corners to stretch it out to cover the scene - I'll cover the whole of the blue area for now - the blue area is the dimension of the screen at the chosen resolution - I'm not worry about that, so I'll leave it to 1024x768. Then we need to look in the properties on the right hand side. With your image selected in the Generic Browser, go into the properties and find the ImageRef property. You will need to expand the Image section and then the StyleOverride sections in order to find it. When you hit the little green arrow, it should turn that nasty green block into your background. Hurray!
Now we're going to add those sliders we need to let the player choose what stats they want in their game. Let's place some more widgets. This time, let's create a UTUISlider and call it sliSpeed - just what we called it in our code - it's all linking together now! Make sure you click in the grey area in the window, otherwise it will create the widgets as 'children' of your background, which is wrong, and the code might not find what it is looking for. Resize it using the red squares once more and move it somewhere useful. Once you've done that, we're going to need three more sliders - sliJumpHeight, sliHeath and sliMaxHealth. Got them all resized and aligned? Great! We also need to let the player know what each slider is. We've got a widget for that too - the label widget. Create yourself a label widget and move it somewhere near one of the sliders. We want to change the text, so we'll find the MarkupString property in DataSource section of the Data category. Change it to something useful, and create three more labels for the other remaining sliders.
Getting better? Good. We have only a few things left to do. We need to create that button bar. This is also nice and easy. Let's create yet another widget - this time it's a UTUIButtonBar. We have to call it pnlButtonBar, because this is what the unrealscript source written by Epic calls it - just like the way we named our sliders. Very cleverly, it has put it at the bottom of the menu scene for us already, just like in all the other Unreal Tournament 3 menus. If you expand it in the Scene Tools on the left hand side, you'll see it automatically creates and positions all the buttons for it too. Score!
We're going to do one quick last thing before we finish our scene. We're going to add some UISafeRegionPanel widgets. We will use these to enforce the correct aspect ratio on our menu scene, and if we give them names that the unrealscript source written by Epic recognises, it will automatically apply the same menu animations to the menu scene as all the other Unreal Tournament 3 menus. Add two of these widgets. We will call one of them pnlSafeRegion, and the other pnlSafeRegionLong. Both of these widgets are also automatically positioned and scaled.
Clearing up the scene, we're going to make our widgets children of the UISafeRegionPanels. We can do this because the code will be able to determine where our widgets are when they children of these. In the Scene Tools, select the pnlButtonBar and drag it into pnlSafeRegionLong. Then we need to one by one drag all the remaining widgets into the pnlSafeRegion. If any of your sliders and labels disappear behind your background, don't worry! When you have finished dragging all the widgets in, you can back click on your background, and go into Reorder Widget and select Send to Bottom.
Close the Unreal Editor, and when it prompts you, save your new package file in your My Documents/My Games/Unreal Tournament 3/ UTGame/Unpublished/CookedPC directory, in a new directory called Packages.
All we need to do is tie our finished menu scene and mutator to the localisation and configuration (the int and ini) files .
Ok, let's create that int file. Go into your My Documents/My Games/Unreal Tournament 3/ UTGame/Unpublished/CookedPC/Localization folder and create a new file called PlayerTweak.int (or modify it if you already have it). We don't need a lot in here, just a couple of lines, which you should be able to guess, because you've done it before.
And that's it, we're done! All you should need to do now is run the game using that short cut you made before (with the -useunpublished command switch) and the mutator should appear in game. Go ahead, try giving yourself super jump, hardly any speed and different health. When you start the game, you should immediately notice the difference!
If you have problems at this point, it is always a good idea to consult the log files. These are generated every time you run the game, and are normally found in the Program Files/Unreal Tournament 3/UTGame/Logs directory. Opening them up with a text editor will help show you what errors have been caused.
Have fun with your spiffy new player statistics! By request, the source code and assets can now be downloaded from here.