UDN
Search public documentation:

GFxVolumeStatusBar
日本語訳
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 Home > User Interfaces & HUDs > Scaleform GFx > How to add a control volume with a status bar

How to add a control volume with a status bar


Overview


This tutorial will cover how to add a very basic control volume with working Status Bar. There are three versions to this tutorial. Version 1 must be completed before you can add the optional code of either version 2 or version 3.

  • The status bar will be drawn to the screen, centered on the HUD.
  • The status bar is displayed as a hovering bar at the center of the control volume. It is physically in the game world, and always faces the player.
  • The bar is again drawn on the HUD, this time at the relative position of the control volume, using Canvas.Project. The status bar moves around the HUD as the player moves, always positioned relative to the location of the control volume.

This tutorial does not provide code for another player or team capturing an already captured volume, nor combined capture efforts of more than one player. You'll need to work that out for yourself.

When finished, you will be able to add a volume to your level in the Editor via: Brush->Add Volume->SFControlVolume

Flash Setup


  • Create a new AS 2 document.
  • Set the Document dimensions to 512x256
  • ALERT! IMPORTANT: Set the Document background to black.
  • Create a blue rectangle in the center of the document.
  • Convert it to a Movie Clip.
  • Give it an instance name of BlueStatusBar.
  • Save and Publish the SWF to your UDK install under /UDKGame/Flash/UDKSFTutorial/CVStatusBar.SWF.
  • Import the SWF into UDK.

Scaleform_Tutorial_01.jpg

Status Bar Centered on HUD


Create the actual placeable control volume that you can place anywhere in your map.

SFControlVolume.uc
class SFControlVolume extends Volume
  placeable;

var bool bControlled;
var int ElapsedTime;
var int PercentCaptured;

// Editable Properties
var(TimeToControl) int TimeToControl;
var(TimeToControl) float UpdateRate;
var() SwfMovie Movie;

var Info ControlTimer;
var SFControlVolumeMovie CVMovie;

event PostBeginPlay()
{
  Super.PostBeginPlay();
  ControlTimer = Spawn(class'SFVolumeControlTimer', self);
}

event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal)
{
  Super.Touch(Other,OtherComp,HitLocation,HitNormal);
   
  if(!bControlled)
  {
    // Turn on the timer, and have it tick every X seconds, where X = UpdateRate.
    ControlTimer.SetTimer(UpdateRate,true);
  }

  // Only display the status bar when the player has touched/is in the volume.
  CreateControlVolumeMovie();
  UpdateStatusBar();
}

event UnTouch(Actor Other)
{
  Super.UnTouch(Other);

  // Reset capture timer.
  ControlTimer.ClearTimer();

  // Close the status bar when the player leaves the volume.
  CVMovie.Close(true);
  CVMovie = none;

  if(!bControlled)
  {
    ElapsedTime = 0;
  }
}

function StartControlling(SFVolumeControlTimer VC)
{
  local int PercentCaptured;

  if (VC == ControlTimer)
  {
    ElapsedTime++;
    `log("Time to capture: " @ TimeToControl - ElapsedTime);
      
    UpdateStatusBar();

    // Once we reach the total time to control, capture the volume and clear the timer.
    if(ElapsedTime >= TimeToControl)
    {
      `log("Captured!");
      bControlled = true;
      ControlTimer.ClearTimer();
    }
  }
}

function UpdateStatusBar()
{
  PercentCaptured = (100 * float(ElapsedTime)) / float(TimeToControl);
  CVMovie.UpdateStatusBar(self, PercentCaptured);
}

function CreateControlVolumeMovie()
{
  CVMovie = new class'SFControlVolumeMovie'; 
  CVMovie.MovieInfo = Movie; 
  CVMovie.SetTimingMode(TM_Real); 
  CVMovie.Start();
}

defaultproperties
{
  TimeToControl=100
  UpdateRate=0.1
}

Now create the timer class. This class simply executes the SFControlVolume::StartControlling() function every time it ticks.

SFVolumeControlTimer.uc
class SFVolumeControlTimer extends Info;

var SFControlVolume CV;

event PostBeginPlay()
{
  Super.PostBeginPlay();
  CV = SFControlVolume(Owner);
}

event Timer()
{
  CV.StartControlling(self);
}

defaultproperties
{
  TickGroup=TG_PreAsyncWork
  bStatic=false
  RemoteRole=ROLE_None
}

And finally, create the Movie Player class for our status bar.

SFControlVolumeMovie.uc
class SFControlVolumeMovie extends GFxMoviePlayer;

var GFxObject StatusBarMC;

function bool Start(optional bool StartPaused = false)
{   
  Super.Start();
  Advance(0);
   
  StatusBarMC = GetVariableObject("_root.BlueStatusBar");
   
  return true;   
}

function UpdateStatusBar(SFControlVolume CV, int PercentCaptured)
{
  local GFxObject.ASDisplayInfo DI;
      
  DI.hasXScale = true;
  DI.hasAlpha = true;

  DI.XScale = PercentCaptured;
  DI.Alpha = 100;
    
  if (DI.XScale >= 100)
  {
    DI.XScale = 101;
  }
  else if (DI.XScale == 0)
  {
    DI.XScale = 1;
    DI.Alpha = 0;
  }
   
  StatusBarMC.SetDisplayInfo(DI);
}

defaultproperties
{    
    bDisplayWithHudOff = false
}

  • Now add the control volume to your level, using a BSP builder brush.
  • Go into the properties of the volume (F4).
  • Set the Time To Control. (Maximum number of ticks to capture)
  • Set the Update Rate. (How often you want the status bar to 'tick' in seconds; Lower numbers = faster tick time)
  • Set the Movie field to the SWF you imported earlier.
  • Save the level and test!

Status Bar Floating in Game World


If you want the status bar physically in the game world, at the center of the volume, first modify the SFControlVolume class.

ALERT! NOTE: You will be able to add multiple control volumes to your level, but you must create a unique Material and TextureRenderTarget2D for each volume.

Modifications to SFControlVolume:

SFControlVolume.uc
// Add these global vars to the top of the class
var() StaticMesh Mesh;
var() vector TranslationOffset;
var() array<MaterialInterface> Materials;
var() TextureRenderTarget2D RenderTexture;
var SFControlVolumeMesh CVMesh;

// Add this code to the event Touch() function, just after Super.Touch();
CVMesh = Spawn(class'SFControlVolumeMesh');
CVMesh.Mesh.SetStaticMesh(Mesh);
CVMesh.Mesh.SetMaterial(0,Materials[0]);
CVMesh.SetLocation(self.Location + TranslationOffset);
CVMesh.PC = Other;

// Add this line of code to the event UnTouch() function
CVMesh.Destroy();

// Add this line of code to the CreateControlVolumeMovie() function, after CVMovie.MovieInfo = Movie;
CVMovie.RenderTexture = RenderTexture;

Now create a new class. This class will be used to display the status bar in the game world.

SFControlVolumeMesh.uc
class SFControlVolumeMesh extends StaticMeshActor
  placeable;

var vector PlayerLoc;
var actor PC;

var() editinline const StaticMeshComponent Mesh;

function Tick(float fDeltaTime)
{
  Super.Tick(fDeltaTime);

  // Always face the player
  PlayerLoc = PC.Location;
  SetRotation(Rotator(PlayerLoc - self.Location));
}

defaultproperties
{
  bStatic = false
  bMovable = true
   
  Begin Object class=StaticMeshComponent Name=StaticMeshComp1
    CollideActors = False
    BlockActors = false
    BlockRigidBody = False
  End Object
  Mesh = StaticMeshComp1
  Components.Add(StaticMeshComp1)
}

  • Now add the control volume to your level, using a BSP builder brush.
  • Go into the properties of the volume (F4).
  • Set the Time To Control. (Maximum number of ticks to capture)
  • Set the Update Rate. (How often you want the status bar to 'tick' in seconds; Lower numbers = faster tick time)
  • Set the Movie field to the SWF you imported earlier.
  • Open the Content Browser.
  • Create a new TextureRenderTarget2D, name it whatever you like, and set its resolution to 512x256 to match the SWF.
  • Create a new Material, and name it whatever you like.
  • In the Material, add a new TextureSample
  • Insert the TextureRenderTarget2D you created in the new TextureSample's Texture Field.
  • Connect the Diffuse Output (black square) of the TextureSample to the Diffuse input of the Material.
  • Set the Blend Mode of the Material to BLEND_AlphaComposite.
  • Add Color->New Desaturation.
  • Add Parameters->New ScalarParameter.
  • Set the Default Value of the Parameter to -20.
  • Wire the Diffuse Output (black square) of the TextureSample to A on Desaturation.
  • Wire the Param 'None' to B on Desaturation.
  • Wire the Output of Desaturation to Opacity and Emissive on the Material.
  • Close the Material window and save.
  • Save the package with your new material and render texture.
    Scaleform_Tutorial_02.jpg
  • Go into the properties of the volume (F4).
  • Set the Mesh field to a plane shaped mesh, such as found at: dwStaticMeshes.Plane
  • Set the Translation Offset. (This moves the status bar relative to the center of the control volume)
  • Add a new Material slot.
  • Assign the new material you created to Material slot 0.
  • Assign the TextureRenderTarget2D you created to the RenderTexture field.
  • Save the level and test!

Status Bar on HUD at Relative Position


This version draws the status bar on the HUD at the relative position of the volume using Canvas Project. As you look around, the status bar will move on the HUD so that it is still in the same relative position.

ALERT! NOTE: If you have done the modifications above to your code, undo them.

You must revise the PostRender() function of your HUD Wrapper:

YourHUD.uc
event PostRender()
{
  local SFControlVolume SFCV;

  super.PostRender();

  // Find all control volumes in the level
  foreach WorldInfo.AllActors(class'SFControlVolume',SFCV)
  {
    //Project 3D world position of control volume to 2D screen position
    if (SFCV.CVMovie != none)
    {
      SFCV.CVMovie.UpdatePosition(Canvas.Project(SFCV.Location));
    }
  }
   
  // Tick HUD
  if (HudMovie != none)
  {
    HudMovie.TickHud(0);
  }
}

SFControlVolumeMovie.uc Modifications: First, add two new global variables:

SFControlVolumeMovie.uc
var int Width, Height;

Next, add these three lines of code to the Start() function:

SFControlVolumeMovie.uc
function bool Start(optional bool StartPaused = false)
{   
    local float x0, y0, x1, y1;

    super.Start();
    Advance(0.0);
   
    StatusBarMC = GetVariableObject("_root.BlueStatusBar");

    GetVisibleFrameRect(x0, y0, x1, y1);
    Width = x1-x0;
    Height = y1-y0;
   
    return true;   
}

Then add this function, right after the UpdateStatusBar() function.

SFControlVolumeMovie.uc
function UpdatePosition(Vector ScreenPos)
{
    self.SetViewPort(ScreenPos.X,ScreenPos.Y,Width,Height);
}

Scaleform_Tutorial_03.jpg