Scaleform Technical Guide
The Scaleform GFx integration in Unreal Engine 3 enables the use of interfaces and menus built in Adobe Flash Professional to be used as heads-up displays (HUDs) and menus. This document is a technical guide for programmers using the Scaleform GFx system. It will cover the major classes involved in implementing a a HUD or menu as well as explain their place in the process and their use.
Scaleform and GFx are registered trademarks of the Scaleform Corporation. Scaleform GFx © 2010 Scaleform Corporation. All rights reserved..
Adobe and Flash are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries.
GFxUI Components Reference
GFxUI components make up the basic system for display Scaleform GFx interfaces. The whole process begins with a new HUD subclass. This HUD class is responsible for setting up a GFxMoviePlayer, which is in turn responsible for playing a GFxMovie. The GFxMovie contains any number of GFxObjects which handle displaying information to and interacting with the player.
The entire Scaleform GFx system revolves around the use of the GFxMovie. This is the actual Swf movie object exported from Flash and imported into the engine that contains the timeline, the ActionScript code, image resources, CLIK objects, etc. The interface the player interacts with is created by playing and manipulating these movies. As such, the majority of this document explains how to perform these actions within the confines of Unreal Engine 3.
The GFxMoviePlayer class is the base class of all classes responsible for initializing and playing a Scaleform GFx movie. This class will be subclassed in order to implement specialized functionality unique to the individual movie for which the player is responsible. The HUD class may have any number of these referenced at any time to play the various elements of the interface for your game.
GFxMoviePlayer Properties (click to view)
GFxMoviePlayer Functions (click to view)
The ExternalTexture struct stores a mapping between a movie's image resource ("Linkage" identifier on an image resource in the movie) and an Unreal texture resource. This allows runtime remapping of the images used by the movie. Any texture with a linkage identifier can be replaced at runtime using
See Swapping Images at Runtime for more information.
- Resource - The linkage identifier of the texture.
- Texture - The texture mapped to the linkage identifier.
Sound Theme Bindings
The SoundThemeBinding struct binds a sound theme name to an actual UISoundTheme to handle sound events from objects in this movie. Sound events can be fired by CLIK widgets or manually by the artist. Each event contains a theme name, and an event to play. This mapping binds the theme names specified by the artist to a UISoundTheme asset, which then binds event names to various sound cues or actions.
See UI Sound Themes for more information.
- ThemeName - Name of the sound theme, specified by the artist in the movie.
- Theme - Sound theme to handle sound events for the
The GFxWidgetBinding struct associates a CLIK widget instance in a movie with a particular UnrealScript subclass of GFxObject, add the widget's Flash name here, and specify the class. This will cause the GFxObject parameter of WidgetInitialized() to be created as the appropriate subclass.
See Widget Initialization and Binding for more information.
- WidgetName - The name of the Widget in the movie.
- WidgetClass - The GFxObject subclass to link to the
The GFxObject class represents an element in a GFxMovie. This can technically be anything from a variable object to a function object to a displayable object, such as graphics or CLIK components.
GFxObject Properties (click to view)
GFxObject Functions (click to view)
ASDIsplayInfo struct stores properties of display objects for easy and quick manipulation at runtime using. The display info for any GFxObject can be obtained, modified, and then re-applied to the object using the
Object Interface functions described above.
For more information on working with display info, see the Working with Widget Display Info section.
- [X/Y/Z] - The position of the
- Rotation - The rotation of the
GFxObject around the Z-axis.
- [X/Y]Rotation - The rotation of the
GFxObject around the horizontal and vertical (X and Y) axes.
- [X/Y/Z]Scale - The scale of the
- Alpha - The opacity in the range of [0, 1] of the
- Visible - If TRUE, the
GFxObject is visible. Otherwise, it is hidden.
- has[X/Y/Z] - TRUE if the
GFxObject has X, Y, and/or Z position information.
- hasRotation - TRUE is the
GFxObject has rotation information.
- has[X/Y]Rotation - TRUE is the
GFxObject has X and/or Y rotation information.
- has[X/Y/Z]Scale - TRUE is the
GFxObject has X, Y, and/or Z scale information.
- hasAlpha - TRUE is the
GFxObject has alpha information.
- hasVisible - TRUE is the
GFxObject has visibility information.
ASColorTransform struct stores color transformation information for manipulation using the
Object Interface functions described above. The color transformation information is used to adjust the color values in display objects, such as text or images.
- multiply - A
LinearColor that is multiplied by the original color of the display object.
- add - A
LinearColor that is added to the display object's color after being multiplied.
UnrealScript and ActionScript
The main concepts and techniques used to communicate back and forth between UnrealScript and ActionScript are detailed below.
Call ActionScript functions from UnrealScript
There are multiple ways to call ActionScript functions from UnrealScript, but the two preferred methods are outlined below:
Method 1: Wrapped UnrealScript function
This method is the preferred method of calling ActionScript functions from UnrealScript. To make the call, you "wrap" the ActionScript function call with an UnrealScript function with the same parameters and return value. Under the hood, the Scaleform GFx ActionScript functions (ActionScriptVoid, ActionScriptInt, ActionScriptFloat, ActionScriptString, ActionScriptWidget, each named for their respective return type) look at their calling function's parameter list, and pass those parameters on to the ActionScript runtime. So, for example, if you wanted to call the following ActionScript function:
From UnrealScript, you would create the following function:
public function MyActionScriptFunc(param1:String,param2:Object):Void
// Do something awesome!
When called, the integration code will check the parameter list of CallMyActionScriptFunc, and convert those parameters to their Scaleform equivalents, and call the function in ActionScript. For ActionScript functions with return types, the other ActionScript* methods in GFxObject can be used. The return values for these functions will be the return values from ActionScript.
Method 2: Using Invoke
This method requires more overhead with memory, because of the structs that must be created to pass in as parameters and return values. It also requires more tedious code to set up. Finally, you can't pass GFxObject parameters back and forth to ActionScript. Since we can't use the example above because of the GFxObject parameter, we'll use a new ActionScript function for this example:
function CallMyActionScriptFunc(string Param1, GFxObject Param2)
The corresponding UnrealScript to call this function via the Invoke method would be:
public function MyActionScriptFunc(param1:String,param2:Number):Bool
// Do else something awesome!
While this method is more tedious, it doesn't strictly require a new function definition to call the ActionScript method, so it can be useful for one-off occasions when you don't wish to subclass GFxObject just to add a function. Also, it is the only method to call ActionScript functions with variable parameter lengths.
function bool MyFunction()
local ASValue RetVal;
local array<ASValue> Parms;
Parms.Type = AS_String;
Parms.s = Param1;
Parms.Type = AS_Number;
Parms.n = Param2;
RetVal = Invoke("MyActionScriptFunc", Parms);
Calling UnrealScript functions from ActionScript
Simple Method: Useful for event notifications and other one-off situations.
Calling UnrealScript functions from ActionScript is much simpler than the converse. From ActionScript, simply use the ExternalInterface's Call method with the name of the function you want to call in UnrealScript, and all the parameters you wish to pass. These parameters will be converted to their UnrealScript equivalents by the engine. The function will be looked up by name on the corresponding GFxMoviePlayer instance. For example, in ActionScript:
The above code would then look for a function called "MyUnrealScriptFunction" in the current GFxMoviePlayer instance in Unreal, convert the parameters to the parameters of that function in UnrealScript, and call the function. Note that the UnrealScript function's parameter list is authoritative in this case, so if the parameters passed from ActionScript cannot be cast to the UnrealScript function's parameter types, they will be NULL or their default value, as appropriate.
Intermediate Method: Useful for hooking function calls in ActionScript.
Any ActionScript function can be forced to callback to an UnrealScript delegate. To do this, you use the ActionScriptSetFunction() function wrapper. As an example, say that you want any ActionScript calls to the function DoFancyThings() in the movie root to call a delegate in UnrealScript, you could set that up with the following code in GFxMoviePlayer:
ExternalInterface.call("MyUnrealScriptFunction", param1, param2, param3);
With the above code, after SetupASDelegate() is called from InitializeMoviePlayer(), all ActionScript calls to DoFancyThings() will be routed to the UnrealScript function DoFancyThings(). The parameters will be auto-converted from ActionScript types to Unreal types, as defined by the delegate, so the programmer is responsible for making sure they match.
Note that as with other ActionScript wrapper functions, ActionScriptSetFunction() grabs the delegate information from the calling function's parameters, in this case SetupASDelegate(). The calling function can have other parameters besides the delegate, but the first delegate encountered in the parameter list will be used as the callback.
Furthermore, in the above example, we set the function on the root of the movie. This can just as easily be any other object within the movie. There are also equivalent functions in GFxObject that set function delegates directly on the ActionScript object they reference. They operate in the same manner, only without the need to specify an object explicitly in the ActionScriptSetFunction() call.
class MyDerivedGFxMoviePlayer extends GFxMoviePlayer;
// Called from elsewhere in script to initialize the movie
// Sets up our delegate to be called from ActionScript
// Code goes here...
function SetupASDelegate(delegate<FancyThingsDelegate> d)
local GFxObject RootObj;
RootObj = GetVariableObject("_root");
ActionScriptSetFunction allows you to execute an UnrealScript function when an ActionScript function is called in the Flash file.
Example: In our hypothetical Flash file, we call a function in ActionScript called MyASFunction(), but the actual function does not exist in ActionScript, and we want to instead fire off an UnrealScript function called DoThis() whenever this function is called on in the Flash file.
Uses: This has many uses. One good use would be when you are sharing the same Flash file between many views, but want each view to handle a specific situation differently. You can then write a DoThis() function for each view in UnrealScript that does something different, without making a unique Flash file for each view. And thus, any time MyASFunction() gets called in the shared Flash file, it will execute a unique DoThis() function in UnrealScript depending upon which view you are on.
Define the delegate.
delegate int MyDelegate(bool bTrue);
Create the DoThis() function in UnrealScript. Its parameters and return type must match the delegate definition from Step 1. This is the function that will be executed, whenever MyASFunction() is called in ActionScript.
function int DoThis(bool bTrue)
local int myNum;
myNum = 5;
`log("Do this UnrealScript function? " @ bTrue);
Next, create the UnrealScript function that does the swapping magic. This function expects the delegate we wrote in Steps 1 and 2 as a parameter. It then caches a refernce to _global, which encompasses the entire movie, including _root. Then, it uses ActionScriptSetFunction to specify that any time MyASFunction() is called in the Flash file, to instead execute the delegate we passed it, in this case - DoThis().
function SetMyDelegate( delegate<MyDelegate> InDelegate)
local GFxObject _global;
_global = GetVariableObject("_global");
Now we simply need to execute the SetMyDelegate function, and pass it the DoThis() function.
SetMyDelegate(none); // clear it first
Finally, in ActionScript, call the MyASFunction somehow.
The result should be that, whenever MyASFunction() is called in the Flash file, the Unreal log will print out:
// MyASFunction() does not actually exist in ActionScript.
// Calling it here will instead execute the DoThis() function
// found in UnrealScript.
Do this UnrealScript function? True
Getting the original size of the SWF
A code snippet which shows how you would get the original size of the SWF file in Unrealscript.
var GFxObject HudMovieSize;
simulated function PostBeginPlay()
HudMovieSize = HudMovie.GetVariableObject("Stage.originalRect");
`log("Movie Dimensions: " @ int(HudMovieSize.GetFloat("width")) @ "x" @ int(HudMovieSize.GetFloat("height")));
Working with Widgets
Using GFxObject Subclasses
Standard practice for complex widgets that need code-driven interaction is to make a subclass of GFxObject in UnrealScript to encapsulate the desired functionality, wrap calls into ActionScript functions, and add state tracking / animation playing information specific to the movie clip. To do this, simply subclass GFxObject, and add in your functionality.
This allows you to add ActionScript function calls using the preferred UnrealScript wrapper method (Method 1, as mentioned above in "Call an ActionScript function from UnrealScript"), as well as make helper functions containing timeline commands, like GotoAndPlay(), for movie clip control.
In order to get a reference to an ActionScript widget using your new subclass, you can use either of the two methods outlined below.
Method 1: Using GetObject()
The simplest way to get a widget reference of your desired subclass is to specify a class as the second parameter of GetVariableObject() (if called from a GFxMoviePlayer) or GetObject() (if called from a GFxObject). This causes the reference returned to be a newly constructed instance of the specified class. Unfortunately, since this is an optional parameter, and not the first parameter in the function, the return type is not coerced, and therefore must be manually cast, as below:
Method 2: Using WidgetBindings or SubWidgetBindings
Widgets that have WidgetInitialized() callbacks can be added to the GFxMoviePlayer's WidgetBindings array if they are handled by the movie player itself, or by GFxObject's SubWidgetBindings array if they are forwarded to a GFxObject via SetWidgetPathBinding(). When WidgetInitialized() gets called for that widget, it will have a class passed in of the appropriate subclass of GFxObject, as specified in the WidgetBindings / SubWidgetBindings array. For details on this, see the "Use WidgetInitialized() and WidgetBindings for initialization and binding" section below.
local MyGFxObjectSubclass Widget;
Widget = MyGFxObjectSubclass( GetVariableObject("MyGFxObjectName", class'MyGFxObjectSubclass' );
To instance a widget from UnrealScript, simply call GFxObject's AttachMovie() with a symbol name, and an instance name. Note that the movie clip symbol must be in the library to be instanced! If you have another clip in the scene of the same type you wish to create, however, this will always be the case. Use the following code as a template. In this example, we create a new button with an instance name of "mc_MyNewButton" by creating a new instance of the button symbol called "btn":
Note that in this case, we create the button relative to the root of the movie. Since AttachMovie() is a function of GFxObject, however, you can create the new movie clip instance as a child of whatever you would like (the GFxObject that you called AttachMovie() from will be the new object's parent). The object can then be manipulated the same as any other GFxObject.
local GFxObject MyNewWidget;
MyNewWidget = GetVariableObject("_root").AttachMovie("btn", "mc_MyNewButton");
Widget Initialization and Binding
Often, it is useful to have a callback when certain widgets are initialized on the timeline. This is especially important when widgets do not exist on the first frame! Since ActionScript has no knowledge or a widget until its first appearance on the timeline, we have added a WidgetInitialized() callback that gets fired when ActionScript creates a widget.
NOTE: If you don't have the Component Inspector in your current layout, you can bring it up by selecting "Component Inspector" from the Window menu, or by hitting Shift + F7
- The WidgetInitialized() is only called for CLIK widgets that have enableInitCallback set to true! This is to cut down on the ActionScript to engine calls, as not all widgets (such as those which are decorative or rarely changed by code) will need this functionality. The call can be manually added though, see "Exceptions" below for details on how to add this.
- To set up the callback in ActionScript, first select the widget that you would like the callback for. It is recommended that if you have a widget that contains several smaller widgets, that you set the callback on the parent widget, then manually operate and store references to the child widgets within the parent widget's scope. This is because WidgetInitialized() passes in the name of the widget, which could be shared across different clips in the movie, and so two separate widgets may end up sharing the same name in the WidgetInitialized() callback. Widgets can also be differentiated from one another with the path, which gets passed into WidgetInitialized() along with the name.
- With the widget selected, look for the enableInitCallback variable in the Component Inspector, and set it to true.
Now, any time the widget that you marked is created in a movie, UnrealScript will receive a call to the containing GFxMoviePlayer's WidgetInitialized() event, with the name of the widget, the full path of the widget, and a GFxObject pointing to the widget.
Sometimes in order to encapsulate functionality, it is useful to have the reference created and passed into WidgetInitialized() be a subclass of GFxObject instead of a normal GFxObject. For instance, if you know you have a special "scoreboard item" widget that contains a label, an image, and some other data, you can subclass GFxObject and encapsulate functionality for changing the label and image of the widget via UnrealScript functions. To bind certain widget names to specific GFxObject subclasses, use the WidgetBindings in GFxMoviePlayer if the widget is handled via your movie player's WidgetInitialized() function. If, for example, you want to have all WidgetInitialized() calls with the name 'MyScoreboardWidget' pass in a reference to a constructed ScoreboardWidget UnrealScript class (which derives from GFxObject), you can add an entry to the WidgetBindings in the _GFxMoviePlayer_'s default properties, as below:
- Save the FLA, and publish the SWF (File->Publish or Shift + F12), and import it into the engine as normal.
In cases where functionality for major container widgets is compartmentalized by subclassing a widget, such as a menu panel or a scoreboard widget, it may also be useful to forward WidgetInitialized() calls to that widget for all of its children. In the above example, for instance, it would be useful to let ScoreboardWidget receive all the WidgetInitialized() calls for all of its child elements, such as the player name labels, icons, etc. To do this, you can use GFxMoviePlayer::SetWidgetPathBinding(GFxObject WidgetToBind, name Path). When you pass a widget and a path, any widgets that have initialization callbacks that are within that path will be forwarded to the specified widget. Note that the class for any of these forwarded widgets will be defined in the SubWidgetBindings array of the GFxObject that handles their initialization. This array has the exact same format as GFxMoviePlayer's WidgetBindings array, but works for widgets initialized via forwarding.
In the above example, you could override ScoreboardGFxMoviePlayer's WidgetInitialized() function to set up path forwarding, such that when MyScoreboardWidget is initialized, all of its children's WidgetInitialized() calls will be forwarded to it. Here's the example code to set that up:
class ScoreboardGFxMoviePlayer extends GFxMoviePlayer;
In the above example, all future widgets that have enableInitCallback set to TRUE in the Component Inspector will be routed to MyScoreboard's WidgetInitialized() event to be handled. To remove the binding for a given path, simply call SetWidgetPathBinding() again with None as the first parameter.
NOTE: It is sometimes useful to log out whether or not widgets were processed by WidgetInitialized() so that widgets erroneously marked with enableInitCallback but not handled by UnrealScript can disable the callback. To search for such widgets, set GFxMoviePlayer's bLogUnhandledWidgetInitializations to true. This will log out any widgets (as well as their full paths) that have enableInitCallback set to true, but for which WidgetInitialized() on either the movie or the widget bound to their path returns false. Note that this means to use this debugging, you should be sure to return TRUE in WidgetInitialized() any time you handle a widget!
Exceptions to CLIK widget requirement: It is possible to use the WidgetInitialized() interface on non-CLIK widgets by writing some custom ActionScript. The WidgetInitialized callback is implemented through hooking into the CLIK_loadCallback function, so if you have a non-CLIK widget that you would like to have call WidgetInitialized(), add the following lines of ActionScript to the widget:
class ScoreboardGFxMoviePlayer extends GFxMoviePlayer;
/** Reference to our scoreboard GFx widget, so we can send all its children's initializations to it. */
var ScoreboardWidget MyScoreboard;
event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget)
if( WidgetName == 'MyScoreboardWidget' )
MyScoreboard = Widget;
if( _global.CLIK_loadCallback )
_global.CLIK_loadCallback(this._name, targetPath(this), this);
Working with Widget Display Info
The quickest (and best for performance) method of manipulating a GFxObject_'s properties, such as location and visibility, is using the _DisplayInfo method. A quick example below should be enough to get you started:
var GFxObject MyGFxWidget; // Assume this gets set somewhere
local ASDisplayInfo DI;
DI = MyGFxWidget.GetDisplayInfo();
`log("MyGFxWidget is at (" $ DI.x $ ", " $ DI.y $ ")");
// Set some new properties for the widget
DI.x = 200;
DI.y = 200;
DI.Visible = true;
CLIK Component Event Callbacks
To set up an UnrealScript function to get called when a CLIK component event happens, do the following:
Get a reference to the CLIK widget you want to set the delegate on, either via the WidgetInitialized(), or via GetObject(). NOTE: This reference must be a GFxCLIKWidget. You can specify the subclass for the widget either by using WidgetBindings if you're getting the reference via WidgetInitialized(), or by specifying the class in GetWidget(), e.g. GFxCLIKWidget( GetWidget("MyWidgetName", class'GFxCLIKWidget') ).
To add a callback, call AddEventListener(name type, delegate listener) on the reference to the CLIK widget. Note that type here must be in the form of 'CLIK_' followed by the CLIK event name (case sensitive!). For example, to hook into the "press" event, you would pass in 'CLIK_press' as the type. A full listing of CLIK events are in //depot/UnrealEngine3/Development/Flash/CLIK/gfx/events/EventTypes.as.
local GFxCLIKWidget MyWidget;
MyWidget = GFxCLIKWidget( GetObject("MyWidgetID", class'GFxCLIKWidget') );
function OnClickHandler(GFxCLIKWidget.EventData params)
// Do something...
One option is to simply replace the movie clip with a blank movie clip.
The other method requires an Invoke which can then remove movie clips you no longer need.
var GFxObject RootMC
// cache the _root timeline.
RootMC = GetVariableObject("_root");
// create a new movie clip at depth 0
// called 'InstanceName' on the _root timeline,
// using a symbol from the library that has
// the linkage ID of 'LinkageID'.
RootMC.AttachMovie("LinkageID", "InstanceName", 0);
// replace the 'InstanceName' movie clip with an empty movie clip at depth 0.
var GFxObject RoomMC, MyMovieClip;
var array<ASValue> args;
var ASValue asval;
// Attach the movie clip to the _root
RootMC = GetVariableObject("_root");
MyMovieClip = RootMC.AttachMovie("LinkageID", "InstanceName");
// Remove the movie clip
asval.Type = AS_Boolean;
asval.b = TRUE;
args = asval;
MoviePlayer Focus, Priority and Input Handling
If your movie needs to receive controller/keyboard input, make sure you give focus to a CLIK widget when your movie starts. The initial setting of focus is responsible for initializing the input system. Do not use Key.onPress in parallel with CLIK's input system, otherwise messages will be sent to both CLIK and your movie.
Only one movie player per LocalPlayer has focus (This is stored in the PlayerStates array in FGFxEngine). Focus for a LocalPlayer is determined based on three variables:
A LocalPlayer's focused movie player is the one with the highest priority that is focusable where focusable is (bAllowFocus
- bAllowFocus - Whether or not a movie player is focusable
- bOnlyOwnerFocusable - Whether or not a movie player is focusable by LocalPlayers other than the owner of the movie
- Priority - The priority of the movie player, higher priority takes precedence over lower priority in both render and focus.
= true && (Owner = Me || !bOnlyOwnerFocusable)). In the case of a tie on priority the most recently created movie player will win. Unless you can guarantee construction order, it is suggested to avoid colliding priority values.
When input is received, the system will attempt to give the input to the focused movie for the LocalPlayer that created the input. This all happens in:
If the focused movie is able to receive input (bAllowInput == true) and is not ignoring that input (pFocusIgnoreKeys does not contains the specific input) then the movie will be given the opportunity to handle the input and it will be passed to it.
If the movie did not receive the input, or if it did receive it and did not decide to capture the input (CaptureInput != true and IsKeyCaptured for the input returns false), or consume/reject the input for any reason (there are many... it could be that the focused movie is being GC'd, the movie consumes all input from non-owner controllers, or the unrealscript backing class for the movieplayer decided to eat the input, etc.) then it will also be passed to all texturemovies for potential usage.
Mouse input is handled slightly differently in that if it is not rejected for any reason on the focused movie (see UBOOL FGFxEngine::InputKey(INT ControllerId, FGFxMovie* pFocusMovie, FName ukey, EInputEvent uevent)) then every other movie should receive it.
UBOOL FGFxEngine::InputKey(INT ControllerId, FName ukey, EInputEvent uevent) which will call into
UBOOL FGFxEngine::InputKey(INT ControllerId, FGFxMovie* pFocusMovie, FName ukey, EInputEvent uevent) and
UBOOL FGFxEngine::InputAxis(INT ControllerId, FName key, Float delta, Float DeltaTime, UBOOL bGamepad) in GFxUIEngine.ccp.
UILoaders are CLIK widgets that provide an easy interface for loading textures from Unreal packages. The texture to load can be set either via ActionScript, or by UnrealScript at runtime. In both cases, the reference syntax is the same.
To load the texture Texture2D'MyPackage.MyGroup.MyTexture', for example, you would set the "source" variable of the UILoader to "img://MyPackage.MyGroup.MyTexture" for bilinear sample scaling, or "imgps://MyPackage.MyGroup.MyTexture" for point sampling.
At runtime, the SetString() method can be used to load in a new image, as in the following code:
NOTE: Since the texture is referenced by a string name instead of an asset reference, be careful when using this. The texture should be in an always cooked package, or a reference to the texture should be added to the GFxMovie's UserReferences to ensure that the texture is loaded on consoles.
Also, to ensure that the texture scales to the UILoader properly, be sure to set maintainAspectRatio to false in the Component Inspector. Otherwise, Scaleform GFx will try to maintain the aspect ratio of the texture being loaded in.
local GFxObject MyImageLoader;
MyImageLoader = GetObject("MyImageLoader_mc");
Swapping Images at Runtime
At runtime, to swap the image out, use the SetExternalTexture(string Resource, Texture TextureToUse) function in GFxMoviePlayer. This will swap any image that uses your exported reference as its texture. NOTE: This function can only be called after Advance() has been called on the GFxMoviePlayer for the first time!
UI Sound Themes
UI sound themes (see Engine's UISoundTheme class) provide a quick and easy method for associating sounds with events in your UI. The class itself provides a simple mapping of event names to SoundCues that should be played when those events occur in ActionScript.
Creating a new UISoundTheme:
- Open UnrealEd, and click on the Actor Classes browser (View -> Browsers -> Actor Classes)
- Uncheck both the "Use 'Actor' As Parent" and "Placeable Classes Only" boxes
- Locate UISoundTheme in the list, right-click, and select "Create Archetype..."
Now that you have a new sound theme, you need to associate events with SoundCues to play. A full listing of the events that are fired off by CLIK widgets is available in the EventTypes.as ActionScript file, located at //UnrealEngine3/Development/Flash/CLIK/gfx/events/EventTypes.as . To associate an event with a SoundCue, simply add an entry to the Sound Event Bindings array (click the + icon), and type the event name in the Sound Event Name field, and the SoundCue that you would like to play in the Sound To Play field, as seen below.
Now that you have a new sound theme, you need to bind it to your GFxMoviePlayer. To do this, in UnrealScript, you can either specify the sound theme binding in the default properties of the class as below, or simply add a new binding to the SoundThemes array.
- Select a location for your UISoundTheme to reside, and click OK in the dialog window that pops up
Advanced Using multiple sound themes in the same SWF
Up until this point, we haven't mentiond the "ThemeName" variable in the SoundThemeBinding struct. In the SoundMap inspectable property of CLIK widgets, there is an entry for "theme," which defaults to "default." However, this can be set to any name that you want. When the engine looks for a sound to play, it first tries to find the associated sound theme based on this "theme" identifier, and then it plays the SoundCue associated with the event in that theme. This allows you the flexibility to have any widget in your SWF to use any sound theme it wants.
For instance, if you had two buttons, and you wanted to have one make a beep noise when the "press" event is fired, and one to make a horn noise, you could make two different UISoundThemes: one which binds "press" to a beep sound cue, and one which binds it to a horn. Then, you can change the "theme" entry in the widgets' inspectable properties to be "beep" and "horn" respectively. When setting your default sound themes for the player, add a theme for both beep and horn, as below:
class MyGFxMoviePlayer extends GFxMoviePlayer;
Advanced Firing events off manually from ActionScript
Sound events are not only for CLIK widgets...sound events can be fired off arbitrarily from ActionScript using the following code:
class MyGFxMoviePlayer extends GFxMoviePlayer;
if( _global.gfxProcessSound )
_global.gfxProcessSound(this, "SoundThemeName", "SoundEventName");
Localization in Scaleform
For localization, our current best practice is to leverage the WidgetInitialized() callback system to replace string content with their localized equivalents. There are two major justifications for this:
To localize strings, simply have a string variable in your GFxMoviePlayer class, and include the keyword "localized". This will attempt to look up the string in the localization file corresponding to the script package that the class resides in.
- It allows the artist to put whatever placeholder text they would like in the actual movie, which helps them with formatting and style.
- It allows all strings to be searched for in code, which aids in tracking down potential bugs and problems.
In your localization file, you can then create a section for your class, and an entry for the localized string, like below:
class MyGFxMoviePlayer extends GFxMoviePlayer;
var localized string MyTitleString;
var transient GFxObject lbl_Title;
event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget)
if (WidgetName == 'TitleLabel')
lbl_Title = Widget;
MyTitleString=My title is awesome!
Testing and Debugging Scenes
Debugging with Scaleform can be a test of wills at times. Many errors such as misspellings and typos in ActionScript which don't throw compile time warnings, fail silently so bugs can be near impossible to track down. Other aspects, such as handling focus within scenes, are really hard to debug because there really are not any debugging tools for those aspects.
Testing in the GFxPlayer
In order to reduce iteration and testing time because of how long it takes to Publish -> Import -> Boot Game -> Test, often we set up dummy data in the ActionScript code to test. To prevent this from executing in game, in ActionScript you can use
This make sure the code only gets run in the external GFxPlayer, and not the game, so that you don't have to worry about fake data appearing in the game. One of the biggest lessons we learned is that where you can, you should always test your content in Flash and get it working there before bringing it into engine, because you'll save so much iteration time. That's part of the motivation for our newer scenes being set up with the gears.view.Foo class. That base location is where we put in the Flash-only debug data so we can test the scene before bringing it in engine. This allows for easy simulation of data being sent from the game, as that is the class we typically hook and send all the data to for processing.
This will also allow your UI Artist to be able to see what the scene will look like with dummy content in it.
if( _global.gfxPlayer )
// debugging code here.
Logging from Scaleform GFx is turned on by default in the PC library, but not the console libraries. Logging is sent through the DevGFxUI logging channel, which is suppressed by default. To enable, comment out (using a semi-colon) the line "Suppress=DevGFxUI" in your game's Engine.ini file (e.g. UDKEngine.ini).
Note that ActionScript Trace() logging is also routed through this channel, so it must be unsuppressed to see any ActionScript logging that you may be doing.