bw logo

Chapter 3. Scaleform

3.1. Introduction

Scaleform GFx is a third party Flash rendering library produced by Scaleform (http://www.scaleform.com/). Scaleform is not provided as part of the BigWorld Technology license and needs to be licensed seperately if required.

BigWorld Technology is integrated with Mozilla using llmozlib which allows showing web pages and Flash movies as part of the game but this solution is less efficient than Scaleform and therefore not suitable for game user interfaces.

Scaleform provides a hardware accelerated renderer for Flash movies suitable for use in games. BigWorld has implemented an integration with Scaleform allowing Flash movies to be displayed, and controlled by Python.

Scaleform IME is an optional add-on for Scaleform customers, allowing display of an in-game Scaleform IME movie to replace the windows IME language bar. BigWorld has integrated against Scaleform IME as well.

While Scaleform has tried its best to optimise rendering of Flash movies, there are still a number of guidelines that artists should follow in order to create Flash movies that behave well and don't bog down performance unnecessarily. It is therefore essential that you read the appropriate Scaleform documentation before launching into full-scale production of Flash movies for use with your BigWorld game.

This document describes how to display and use a Flash movie in BigWorld. It describes the changes required to the client to build in the Scaleform integration. It then describes how to use the Python API to display and manipulate a Flash movie. Finally it describes some BigWorld-specific features and limitations.

3.2. Building the Client with Scaleform

In order to build the client with support for Scaleform, you must download and install the SDK (version 3). The installer will create an environment variable called GFXSDK which is used by the BigWorld client solution to locate the SDK installation. You can check that the environment variable is correctly set by opening a command prompt and entering the command set GFXSDK. It should print out the root installation path of the Scaleform SDK.

3.2.1. Setting the BigWorld Build Configuration Macros

The Scaleform integration library is included with your BigWorld Technology package in the bigworld/src/lib/scaleform directory. Contained in this directory is a file called config.hpp, which should be changed to look like this:

#define SCALEFORM_SUPPORT 1
#define SCALEFORM_IME 1               //only necessary if also using Scaleform IME

Then rebuild the client. That's all you should have to do.

3.3. Displaying Flash Movies

This section aims to show you how to load up a Flash movie and display it in your game's user interface.

3.3.1. Flash GUI Component

Flash movies are integrated with the BigWorld GUI system. This allows you to, for example, embed a Scaleform movie within a moveable window, or to pop up a Scaleform movie like an Inventory display in the HUD.

In order to do this, you simply wrap the MovieView object in a Flash GUI Component. Once you do this, the GUI object takes control over the lifetime, display, size, position and input handling for your movie.

# movieDef, movieView attributes created in the above example

import GUI
g = GUI.Flash( movieView )
GUI.addRoot(g)

# movie is now displayed via the GUI tree, instead of displayed via a global Scaleform movie list.

# The movie can now be positioned just like other GUI components.
g.position = (1.0,-1.0,0.6)
g.verticalAnchor = "BOTTOM"
g.horizontalAnchor = "RIGHT"
g.widthMode = "PIXEL"
g.heightMode = "PIXEL"
g.size = (256, 256)

# The movie now responds to key / mouse events just like other GUI components
g.focus = True             # respond to key events
g.mouseButtonFocus = True  # respond to mouse button events
g.moveFocus = True         # respond to mouse events

Note

If you attach a script object to a FlashGUIComponent, you have to pass input events through to the MovieView yourself. This allows the script to filter events before they get passed through. For example, the following snippet passes through all input events except the escape key which is handled as a special case:

class MyFlashScript( object ):
    def __init__( self, component ):
        self.component = component
        component.script = self

    def handleKeyEvent( self, event ):
        if event.key == Keys.ESCAPE:
            self.visible = False
            return False
        
        return self.component.movie.handleKeyEvent( event )    
        
    def handleMouseButtonEvent( self, comp, event ):
        self.component.movie.handleMouseButtonEvent( event )
        return self.component.movie.hitTest( event.cursorPosition )

    def handleMouseEvent( self, comp, event ):
        self.component.movie.handleMouseEvent( event )
        return self.component.movie.hitTest( event.cursorPosition )

Notice the use of hitTest. This allows mouse events to fall through Flash components that have transparent parts.

3.3.2. Python controls the lifetime of Scaleform Objects

In the above example, two objects were created, a Movie Definition and a Movie View. Both of these are Python objects that internally maintain a reference to one or more Scaleform C++ objects. As a game programmer, all you need to know is that as long as you keep your Python objects alive, the underlying Scaleform objects are kept alive too. Here's a code snippet that demonstrates the lifetime of these two Python objects :

#movieDef, movieView attributes created in the above example

# First we will free the movie definition.
del movieDef

# The Movie View will still play, however in future if you require a new view of that movie, you'll have to reload the entire movie.

# Now free the movie view.
del movieView

# Notice that deleting the movieView object means it is no longer displaying

If you hold onto Scaleform objects after the Personality Script is fini'd(), then you will run into problems, as the Scaleform subsytem is shut down before Python. When Python is shut down, it frees all its outstanding objects, and the Python-controlled Scaleform objects will be released too late, causing an assert. It is up to you to make sure you release these objects before shutdown.

3.3.3. Configuring Font Libraries

The BigWorld Scaleform integration allows you to specify where fonts are loaded from by creating a font configuration file that sits next to the source .swf. This file allows you to specify any number of additional SWF filenames which will be searched by the Scaleform loader for fonts, and it allows you to define any number of font mappings. Internally, this corresponds to GFxFontLib and GFxFontMap respectively.

For example, if a movie is located at res/scaleform/mymovie.swf, the font configuration file would be named res/scaleform/mymovie.fontconfig. To avoid duplicating the same font configuration, it is possible to create a default.fontconfig and this will be used if a specific font configuration is not found (this is unique per directory).

The font configuration file is an XML file and has the following basic layout,

<root>
    <lib> fontlibA.swf </lib>
    <lib> fontlibB.swf </lib>
    ...
    <lib> fontlibX.swf </lib>

    <mapping> $FontNameA
        <family> Times New Roman</family>
        <flags>  BOLD </flags>
        <scaleFactor> 1.7 </scaleFactor>
    </mapping>

    <mapping> $FontNameB
        <family> Arial Unicode MS </family>
        <flags>  NORMAL </flags>
        <scaleFactor> 1.3 </scaleFactor>
    </mapping>
    ...
    <mapping> $FontNameX
        <family> Arial </family>
        <flags>  ITALIC </flags>
        <scaleFactor> 1.0 </scaleFactor>
    </mapping>
</root>

Note

Possible mapping flags are: ORIGINAL, NORMAL, BOLD, ITALIC, BOLDITALIC.

Note

Creating a font configuration file for a movie will disable the internal Scaleform support for gfxfontlib.swf (this includes default.config).

See the Fonts document in the Scaleform GFx SDK for details on how fonts work inside Scaleform.

3.4. Interacting with Flash Movies

No doubt you will want to build complex UI's in Flash that interact with your BigWorld game, sending and receiving events and swapping data back and forth. All method and data interactions are handled via the PyMovieView Python object. This chapter details a number of different ways to interact with your Flash movie and its action script.

3.4.1. Using fscommand in Action Script to call into Python

In earlier versions of Action Script, the fscommand() call was the way to call out from Action Script and into its container. fscommand() is supported by Scaleform and BigWorld. In order to handle fscommand() calls, you simply register a Python callback function to handle any such calls :

def myFSCommandHandler( cmd, arg ):
    # cmd, arg are both strings.  Dispatch to wherever is appropriate.

movieView.setFSCommandCallback( myFSCommandHandler )

3.4.2. Using External Interface calls from Action Script to Python

In later versions of Action Script, external interface is used instead to call out of Action Script and into the containing application. External Interface calls have the advantage of allowing different types and number of arguments to be passed around (instead of fscommand supporting only a single string argument). Additionally, the container can set return values for the external interface call. Usage is similar to handling fscommand, however the callback function has a different interface :

def myExternalInterfaceHandler( cmd, *args ):
    # cmd is still a string, but now we have a list of arguments, with arbitrary types.  Dispatch to wherever is appropriate

movieView.setExternalInterfaceCallback( myExternalInterfaceHandler )

3.4.3. Setting and Getting Action Script variables directly from Python

Action Script variables tend to be buried in a hierarchy, often looking like : "_root.levelN.someVariableName". Unfortunately this syntax is misinterpreted by Python and currently there is no elegant solution to work around this. However it is still easy to exchange data with Action Script. All that is required is to use Python's setattr and getattr syntax. For example:

setattr( movieView, "_root.level0.maxHealth", 10 )
currentHealth = getattr( movieView, "_root.level0.health" )

Using this syntax you can get and set any of the following types of data : boolean, int, floats, string and unicode string.

3.4.4. Calling Action Script methods from Python

The final interaction you will want to have with your Flash movie is to call Action Script methods directly from Python. In order to do this, simply use the invoke() method:

result = movieView.invoke( "_root.level0.flashHealthBar", 10.0 )