sega 500 gui & menus jeff “ezeikeil” giles [email protected] jgiles
TRANSCRIPT
So far
We’ve spent lots of time futzing with the game itself and making it do all sorts of new, weird and wonderful things.
…but it’s menus & stuff look so UT it’s not funny.
Today
We’re going to take our first steps into changing UT2003’s front end.
E.g. we start playing with and customizing the menu system.
The goal?
Nothing overly complex of fancy today.
Simply to get a mutator dialogue box to display to the screen with some buttons and information.
The goal?
I’m going to take the adrenalin bounce mutator we made a few classes back and build a menu for it.
Ultimately, were going to have the jump height adjustable from this menu.
The goal?
Thus illustrating, in it’s infancy, how to pass information to the game from the menus.
And back again…
Before we code…
There are a tone of GUI classes already created for us. Thus, most of the core functionality is already in place.
We just have to sift through it to find what we need…no small task.
Have a boo…
Take a look at what you have…for a change lots of classes…very well documented …
Having a peak at what we need today, we’re just going to walk down the inheritance chain to checkout all the assets we get.
XInterface.GUI
GUI is an abstract class that holds all of the enums and structs for the UI system
It also has its own controller, specificly for the GUI.
xInterface.GUIComponent
GUIComponents are the most basic building blocks of menus.
This class has some weirdness in it…the inclusion of cpptext.
A long list of delegates for the core functionality of the menus.
From here we have access to the player via pawn owner.
xInterface.GUILabel
GUILabel - A text label that get's displayed. By default, it uses the default font, however
you can override it if you wish.
Our “Draw Text” tool
xInterface.GUIMultiComponent
GUIMultiComponents are collections of components that work together.
When initialized, GUIMultiComponents transfer all of their components to the GUIPage that owns them.
xInterface.GUIPage
GUIPages are the base for a full page menu. They contain the control stack for the page.
This is the base class from which we derive our menu.
Also has a bunch of page specific delegates.
xInterface.GUIButton
GUIButton - The basic button class
With 2 function stubs of interest event ButtonPressed();
// Called when the button is pressed; event ButtonReleased();
// Called when the button is released; Defines the core button functionality
Getting started…
If you recall from our class on mutators, there was a default property named:
ConfigMenuClassName=“”
Which we left uninitalized. This is actually what’s going to be the “handle” to your menu.
What menu to use…
In our mutator we simple tell this to point at our menu type.
ConfigMenuClassName="eze.EzeBounceConfig"
Building your menu
As promised, deriving from GUIPage, we declare our menu.
class EzeBounceConfig extends GUIPage;
And this is where it gets a bit weird…
Building your menu
In out default properties, we add the definition of the button.
WinLeft=0.25 //placement multipliers from top leftWinTop=0.3WinWidth=0.5 //multipliers to window's w&h valuesWinHeight=0.4 //relative to the screen resolutionbRequire640x480=True
These are the widows own properties.
Building your menu
And the dialogue box…which is in fact a button…which is in fact a window…
Begin Object Class=GUIButton name=DialogBackgroundWinWidth=1.0WinHeight=1.0WinTop=0WinLeft=0bAcceptsInput=falsebNeverFocus=trueStyleName="ComboListBox"bBoundToParent=truebScaleToParent=true
End ObjectControls(0)=GUIButton'DialogBackground'
Building your menu
What we get?
Ooo…exciting…
The screen coord layout
The placement numbers are multipliers which work very similar to UV coords.
0,0
1,1
Building your menu
So what the heck is going on, that button declaration came totally out of left field!
Ok, yeah. This might seem a little weird to you (it sure did to me when I first saw it), but don’t worry, it is really simple!
Building your menu
UT2003 has completely changed the way the menus and button system works…It’s much simpler…despite being what appears to be a twisted form of object orientation…
In the classic UT, we had to create around 3 or 4 classes just to make one window, basically what all this code does is create this window for you using only one class.
Here’s what’s going on…
Begin Object Class=GUIButton name=DialogBackgroundWinWidth=1.0WinHeight=1.0WinTop=0WinLeft=0bAcceptsInput=falsebNeverFocus=trueStyleName="ComboListBox"bBoundToParent=truebScaleToParent=true
End ObjectControls(0)=GUIButton'DialogBackground'
Begin a new object The objects nameClass type
Sets Controls[0] to the DialogBackround.Should match name .
End this object
Controls is an array of elementsthat make up this window
Kinda like the aliases we looked at in the User.ini.
Here’s what’s going on…
Begin Object Class=GUIButton name=DialogBackgroundWinWidth=1.0WinHeight=1.0WinTop=0WinLeft=0bAcceptsInput=falsebNeverFocus=trueStyleName="ComboListBox"bBoundToParent=truebScaleToParent=true
End ObjectControls(0)=GUIButton'DialogBackground'
Window width & height 1 == full
Top left of screen
Accept inputs, falsefor backgroundstyle to use
Here’s what’s going on…
Basically, Begin Object creates an object, names it, manipulates its variables and then assigns it to something.
Here’s what’s going on…
But look just a smidge closer…
WinLeft=0.25WinTop=0.3WinWidth=0.5 WinHeight=0.4 bRequire640x480=True
0.25
0.30.4
0.5
These values are relative to the whole viewable area.
Here’s what’s going on…
And looking at the other block… Begin Object Class=GUIButton name=DialogBackground
WinWidth=1.0WinHeight=1.0WinTop=0WinLeft=0 0,0
1,1
Here’s what’s going on…
Why does this happen?
Two very helpful parameters…
bBoundToParent=truebScaleToParent=true
Here’s what’s going on…
In effect the coordinates are relative to the parent window’s dimensions.
Thus placement of the window is almost like doing a SetPos with the canvas in the HUD…almost.
Here’s what’s going on…
Lets add an OK button.
Adding another button
Do another begin / end object set.
Begin Object Class=GUIButton Name=OkButtonCaption="OK"WinWidth=0.2WinHeight=0.04WinLeft=0.4WinTop=0.6
bBoundToParent=true bScaleToParent=true
OnClick=EzeOnClick //delegate assignEnd ObjectControls(1)=GUIButton'OkButton'
Hey a button
Wow…
Hey a button
But first, notice the placement is relative to the dialogue box…
Begin Object Class=GUIButton Name=OkButtonCaption="OK"WinWidth=0.2WinHeight=0.04WinLeft=0.4WinTop=0.6
0.2
0.40.6
0.04
Here’s what’s going on…
Most of this is the same, but here’s the changes
Begin Object Class=GUIButton Name=OkButtonCaption="OK"WinWidth=0.2WinHeight=0.04WinLeft=0.4WinTop=0.6OnClick=EzeOnClick
End ObjectControls(1)=GUIButton'OkButton'
A caption
Control is Incremented by one
Onclick is Assigned a value
Here’s what’s going on…
Caption: is simple text for the button.
Control: is an array, we just add this
functionality to the next index. Onclick:
is a delegate that we point at afunction…which is run when clicked.
Here’s what’s going on…
Ours, simply closes the window.function bool EzeOnClick(GUIComponent Sender){ Controller.CloseMenu(); return true;}
This would be where you save to an ini or execute you changes from the mutator.
Let me show ya something
Now there’s obviously a connection between the buttons and the declarations in our page…but what are they?
See if you can spot the similarities.
Let me show ya something
Begin Object Class=GUIButton name=DBWinWidth=1.0WinHeight=1.0WinTop=0WinLeft=0bAcceptsInput=falsebNeverFocus=trueStyleName="ComboListBox"bBoundToParent=truebScaleToParent=true
End ObjectControls(0)=GUIButton'DB'
defaultproperties{
StyleName="RoundButton"
bAcceptsInput=truebCaptureMouse=TruebNeverFocus=false;bTabStop=trueWinHeight=0.04bMouseOverSound=trueOnClickSound=CS_Click
}
Our class GUIButton class
Let me show ya something
Let me make it a bit more obvious…
Same thing, I’ve just included some of the inherited defaults in the GUIButton and removed repeat declarations.
Let me show ya something
Begin Object Class=GUIButton name=DBWinWidth=1.0WinHeight=1.0WinTop=0WinLeft=0bAcceptsInput=falsebNeverFocus=trueStyleName="ComboListBox"bBoundToParent=truebScaleToParent=true
End ObjectControls(0)=GUIButton'DB'
defaultproperties{
WinTop=0.0WinLeft=0.0WinWidth=1.0WinHeight1.0
bAcceptsInput=false bNeverFocus=false
StyleName="RoundButton"bNeverFocus=false;
bBoundToParent=falsebScaleToParent=false
}
Our class GUIButton class
Let me show ya something
Hmmm…same variable names are showing up…
Could it be…that were simply setting defaults?
Looks to me like that’s exactly what’s going down.
Adding some text
Now that we’ve gone through the above, this is actually stupid easy…
Just following the same pattern the next object is:
Adding some text
Begin Object class=GUILabel Name=DialogTextCaption="On the bounce!!!"TextALign=TXTA_CenterTextColor=(R=220,G=180,B=0,A=255)TextFont="UT2HeaderFont"WinWidth=1.0WinHeight=32.0WinLeft=0.0WinTop=0.3bBoundToParent=truebScaleToParent=true
End ObjectControls(2)=GUILabel'DialogText'
GUILabel class
New caption Text align
Text colour
The font
Adding some text
What about pictures?
Sure no problem after poking around for a suitable texture…
Cool! Finally cool!
So how did we do that?
Once again, very similar to the above…
Begin Object class=GUIImage Name=aPic WinLeft=0.25 WinTop=0.3 WinWidth=0.5 WinHeight=0.4
Image=Material'ExitScreen.splash.QuitA2'ImageColor=(R=255,G=255,B=255,A=255);ImageRenderStyle=MSTY_NormalImageStyle=ISTY_Scaled
End ObjectControls(3)=GUIImage'aPic'
GUIImage class
The image
Colour
Render styleImage draw style
But there’s a problem…
Where did the “On the Bounce!!!” caption go?
Here’s a hint…
Actually it’s still there…
Order counts!
Like so may things in programming…order counts!
Just swap the controller indexes around some so that the image is drawn before the title…
Controls(2)=GUIImage'aPic‘…Controls(3)=GUILabel'DialogText'
Nuh-huh…order counts
Next up
Ok, starting to look different…which is good. But can we make some noise with it.
In short, yes. But it is a bit finicky in terms of how.
GUI Noise Makers
The easies way to get the GUI to make some sounds is like this… You have to call a function declared in the GUI controller
PlayerOwner()
GUI Noise Makers
Ok, sounds simple enough, bu tlook at what it does.
It accesses the VIEWPORT to get a hold of the player.
function PlayerController PlayerOwner(){
return Controller.ViewportOwner.Actor;}
GUI Noise Makers
Ok, why do we have to go this route?
Well, in short, GUIControllers are completely different that the regular controllers. As a matter of fact, they are not even an actor. They derive from Interactions…
GUI Noise Makers
But, from here you can call the play sound function.
So, I’m going to start by making noise with every keystroke…making is sound like an old style typewriter
Not going to do anything with the keystrokes yet…
GUI Noise Makers
Simple as that….now try the keyboard
Delegate bool OnKeyType(out byte Key, optional string Unicode) { PlayerOwner().PlayOwnedSound(sound'MenuSounds.MS_MouseOver',
SLOT_Interface,1.0); return false;}
GUI Noise Makers
So, running with is a bit, I override another function:
function PlayOpenSound(){
PlayerOwner().PlayOwnedSound(sound'PickupSounds.LargeShieldPickup',SLOT_Interface,1.0);}
GUI Noise Makers
Doing this will cause a nice shield charge sound to play when the menu opens.
Yeah, simple…But, since we have access to the GUIController, we can also change the default sounds…
GUI Noise Makers
Try adding this to the PlayOpenSound function:
Any idea what this will do?...other than make some noise I mean.
Controller.MouseOverSound=Sound'WeaponSounds.BExplosion3';
GUI Noise Makers
Ok, that one is a bit of a keeper…changing all the GUI sounds…
Definitely going to need that!
That’s all for today
This should be more that enough to wet the appetite…
Tomorrow, we’ll look at how to get information into and out of the menus.