HUD Development

The following section is a high-level introduction to Scaleform’s recommended best practices when creating and iterating on a heads-up display (HUD). The implementations listed below are by no means required; however they should be reviewed for ideas and guidance that will help developers achieve better performance and optimal memory usage when creating a HUD with Scaleform.

Our recommendations are listed in an increasing order of complexity and length of time to implement. If creating multiple iterations of a HUD, we suggest starting with Multiple SWF Movie Views and moving deeper into the list as iterations toward a final version are created. This is a great way to rapidly prototype HUD elements that can operate in-game. As the HUD and resource requirements are refined, optimize using our recommended practices.

Please keep in mind that if developing an extremely complex, multilayered HUD with very high performance and very low memory requirements, development with C++ is still highly effective, and may be the best option.

Multiple SWF Movie Views

Overall, this type of HUD is very fast to both develop and iterate. It is purely artist driven and allows for more effects and better graphical representations in a very short period of time. This is optimal for prototyping and iterative design prior to optimization, but can increase memory use. Individual movie views create new player instances that add about 80K of memory overhead. The tradeoff is a faster HUD and individual movie control, but more memory usage. Take into consideration memory and performance issues vs. the flexibility of this dynamic, multilayered system.

Using multiple SWF movie views can offer benefits described below:

  1. Ability to Call Advance on different threads. A multithreaded HUD interface allows for the execution, or Advance, of each Flash movie on different threads (not the rendering but the processing, e.g., timeline, animation, AS execution, flash processing). This enables individual advance control; breaking up Flash into multiple movies allows the developer to stop and not Advance certain elements, or call Advance at different times for different threads. Some elements of the HUD can Advance at a higher rate, or the developer can actually stop calling Advance on a static HUD element until an event happens in the game requiring an action.

NOTE: Scaleform allows calling Advance on different threads for different GFx::Movie objects; however, since each movie instance is not thread-safe Display cannot be called on a different thread without explicit synchronization. Input and Invoke calls also need to be synchronized with Advance.

  1. Utilize render-to-texture caching. This involves rendering HUD elements to textures and keeping them cached, only updating when necessary. This will minimize draw primitives, but will take up more memory, because it requires having a texture memory buffer that is the same size as the HUD element. It could have a positive performance impact, but a negative memory impact. Consider this option only if the element is rarely updated and fairly uncomplicated.

Single Movie View Containing Multiple SWFs

This method involves multiple Flash files loaded into a single full screen movie view via the AS SWF loading APIs. Benefits of this approach include more efficient memory use and slightly better Display performance.

We recommend the following guidelines when loading multiple Flash files into a single movie view.

  1. Carefully group objects that can be hidden in batches and mark them with _visible = false (AS2) or visible = false (AS3) while having _global.noInvisibleAdvance (AS2) or scaleform.gfx.Extensions.noInvisibleAdvance (AS3) set to true to minimize advance processing overhead. This is probably one of the most important things that can be done when creating a HUD: grouping objects that can be hidden and adding a parent to manage them. Use _visible/visible = false to stop processing after the objects are invisible (NOTE: This is NOT to control visibility but to stop calling Advance on an object that is already not visible). By hiding certain groups of objects, execution logic will not be called on those elements inside of the Flash file. This is particularly useful where certain parts of the HUD are hidden and shown (e.g., pause menu, map, health bar). We also recommend that when hiding/showing the entire HUD, the developer should stop calling Advance altogether. Don’t forget to turn on Scaleform extensions by setting _global.gfxExtensions (AS2) or scaleform.gfx.Extensions.enabled (AS3) to true.
  2. Group multiple variable updates from the application through SetVariableArray and a single Invoke. This is useful if there is an element that has several different components, a high degree of complexity (e.g., a map with moving elements), and involves grouping updates into a single call vs. calling an individual invoke for every icon on the map. Call SetVariableArray to pass an array of data (e.g., new positions of each map element) and then call a single Invoke after that to process and use the array of data that is set to move the items all under a single function execution. However, if there is only a small amount of data to be updated, do not use this method as it could negatively impact performance.
  3. Be judicious with the use of onEnterFrame (AS2) or Event.ENTER_FRAME (AS3) when creating HUD elements. If there are multiple elements inside the HUD that have onEnterFrame/Event.ENTER_FRAME, every time the developer calls Advance they will be executed, even if that particular element does not change.
  4. Keep the animation frame rate of movies at half the game’s frame rate and only call AS when needed. A common game programming paradigm is to call tick every frame in the game engine. This is definitely not optimal for using Flash. It is essential that the developer avoid calling AS Invokes every frame to reduce memory usage and increase performance. For example, if the game is running at 30–40 FPS, only update the animations every 15–20 FPS. However, consider that HUD animation maybe laggy, jittery and not as smooth if a very high framerate is used and there are animations that are called too slowly.
  5. Shorten timeline animations as much as possible, as lengthy timeline animations tend to use more memory. However, this must be carefully managed, as too much shortening can cause jittery animations.

Single Movie View

Use this method to have very efficient Flash with a complex multi-element interface (e.g., radar screen). It is important to carefully examine how Flash vs. C++ is used to render elements. Multiple Flash layers cause separate draw primitives, reducing performance. Be sure to utilize all of the guidelines from Single Movie View Containing Multiple SWFs section in addition to the following:

  1. Use the game engine to draw elements inside of an interface, but use Flash to draw a border and frame and Scaleform for text. When there are several rapidly changing elements within a HUD, Flash can be used to draw the static elements and C++ for rendering the items that are changing often.
  2. Use C++ for positioning elements while still having them drawn by Flash. A good example of this would be a radar screen; create a single Flash set of dots, but manage their position inside the Render::Renderer2D, tagging the element using the RenderString identifier. Use the C++ engine to reposition the element correctly before it is rendered. This can avoid some of the AS update overhead, but it is fairly complex and requires extra programming.

Single Movie View (Advanced)

Using this method is significantly more advanced and time consuming, but can provide some additional memory savings. We do not recommend using this method until HUD iteration and creation is nearly complete. In addition to utilizing the methods detailed in Single Movie View section, consider the following techniques:

  1. Only call Advance on graphic changes in the HUD, or try to do all updates through a single Invoke. This could be used on a single movie that has a background layer of HUD animation updating, with more complicated HUD interfaces (e.g., those containing text and progress bars) that sit on top of it that are not animating and not getting an Advance called. A good example of this would be a Health bar that typically isn’t animated; there is no reason to call an Advance on it until there’s a change. (e.g., play the first frame, freeze, and call the display and only Advance/invoke when there is a change to the character’s health).This is more efficient to render, with less CPU overhead, but it is a lot more complex to manage.
  2. Utilize custom static buffer management for vertex data, and override the Render::Renderer. This method is C++ intensive, and would require heavy programmer intervention on the part of highly skilled C++ graphics programmers. In overriding the Render::Renderer, use different (custom) video memory vector data storage, and override other parts of the Scaleform system to use static buffers instead of dynamic buffers to manage HUD elements.
  3. Use threaded rendering and override the Render::Renderer. This is probably the most complicated method; it requires rewriting the renderers and calling a separate Advance on a HUD that is rendered by the game engine. The tradeoff in this level of complexity is a potentially large performance gain.

Note: Threaded rendering exists in the Scaleform–Unreal® Engine 3 integration, but it still would require a great deal of programming effort to implement this method.

Custom HUD Creation without Flash

This process is by far the most complex and time consuming. Ultimately, this HUD would be purely C++ and bitmap based. Scaleform and Flash could be utilized throughout the process of creating and iterating on your HUD, but then removed for the final version by converting Flash interfaces to bitmaps. At this point, Scaleform would not be doing any advance or taking memory for HUD elements, except for one occurrence of the Scaleform Player in memory.

Depending on what you can afford, you can combine custom-tuned C++ rendering for performance-critical HUD items with Scaleform rendering for everything else. Areas that can most benefit from external custom rendering are mini-maps and inventory/stat screens with many items; these are areas that can be optimized the most from DP batching and efficient multi-item updates, something that is difficult for Scaleform to do automatically. Other HUD elements such as borders, panels, stats and animated pop-ups can be left using Scaleform and replaced only if they become a bottleneck.

Regardless, we recommend that the developer continues to use the Scaleform font/text engine, particularly because Scaleform includes a DrawText API that allows the developer to programmatically draw text from C++ using the same Flash font and text systems used in a Scaleform-created user interface. This may save memory by not having to have two separate font systems: one for the HUD and a separate one for the rest of the menu system. For more information on our font/text engine, as well best practices for font and text usage please refer to the "Font & Text" section of our FAQ and Font and Text Configuration Overview or DrawText API.

Overall, please keep in mind the following considerations when creating and iterating on a HUD: