Announcement

Collapse
No announcement yet.

Control Volume With Status Bar

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Control Volume With Status Bar

    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.
    • Version 1 [REQUIRED] The status bar will be drawn to the screen, centered on the HUD.
    • Version 2 [Optional] 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.
    • Version 3 [Optional] 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
    • 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.




    Uploaded with ImageShack.us




    Version 1 - Status Bar Centered on HUD

    New UnrealScript Class - SFControlVolume.uc


    Next, we create the actual placeable control volume that you can add anywhere in your map.

    Code:
    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
    }
    New UnrealScript Class - SFVolumeControlTimer.uc

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

    Code:
    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
    }
    New UnrealScript Class - SFControlVolumeMovie.uc

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

    Code:
    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
    }


    Version 2 - 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.

    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.

    UnrealScript - SFControlVolume.uc Modifications

    Code:
    // 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;
    New UnrealScript Class - SFControlVolumeMesh.uc

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

    Code:
    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)
    }

    Version 3 - 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.

    NOTE: If you have done the Version 2 modifications to your code, undo them.

    UnrealScript - Hud Wrapper Modifications

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

    Code:
    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);
        }
    }

    UnrealScript - SFControlVolumeMovie.uc Modifications

    First, add two new global variables:

    Code:
    var int Width, Height;
    Next, add these three (red) lines of code to the Start() function:

    Code:
    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.

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


    UnrealEd Setup
    • Ok, 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!

    Version 2 Only
    • Do the steps above, then...
    • 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.




    Uploaded with ImageShack.us
    • 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!




    Final Result



    Uploaded with ImageShack.us

    #2
    Wow thanks a bunch, love how you mix little extra gameplay features with scaleform Art. Thanks again for these tutorials.

    Comment


      #3
      This was a really helpful tutorial, now we can place Flash elements in the real world as well!

      Comment


        #4
        I have succeeded in applying the texture to a plane (imported form Maya) and putting it in the world. The Material is configured like you did in the tutorial, with the exception of not binding desaturation to emissive. I have a problem though: The black background doesn't show up as transparent, but fully opaque, any idea what can be wrong?



        Uploaded with ImageShack.us

        The Class:

        Code:
        class LSGFxControlPoint extends LSGFxBaseGUI;
        
        var GFxObject Root, Window;
        
        var float    Scale;
        var float    Width, Height;
        var float    rotval;
        
        var float    rightThreshold, leftThreshold;
        var bool     bInitialized;
        
        var rotator StartRotation;
        
        function SetBarPC(int pc){Root.ActionScriptVoid("SetMaskPC");}
        function SetBonusPC(int pc){Root.ActionScriptVoid("SetBonusPC");}
        function SetCapturing(bool isit){Root.ActionScriptVoid("SetCapture");}
        function SetActive(bool isit){Root.ActionScriptVoid("SetActive");}
        
        
        function SetRenderTexture(TextureRenderTarget2D target)
        {
        	RenderTexture = target;
        }
        
        function onPressedE()
        {
        	//Handle E input here
        }
        
        function Init(optional LocalPlayer PC) 
        {
        super.Init(PC);
        
         //Start and load the SWF Movie
         	Start();
        	 Advance(0);	
          
        	RenderTextureMode = GFxRenderTextureMode.RTM_AlphaComposite;
        }
        
        
        defaultproperties
        {
        
            bEnableGammaCorrection = false
         
          bDisplayWithHudOff = false
        
        MovieInfo=SwfMovie'HUD.controlpoint'
        
        }

        Comment


          #5
          Set the Blend Mode of the Material to BLEND_AlphaComposite?

          Comment


            #6
            Yeah, as I said, i've followed the tutorial and It is set to Blend_AlphaComposite
            It's pretty funny because if i choose Blend_Additive, I do kind of get the desired result.

            Comment


              #7
              Figuring out material settings is really beyond the scope of Scaleform.

              You'll just have to play with your material until it renders how you want it. The setup I used in the tutorial worked perfectly for me. Try doing that.

              Also, I see you are setting the blend mode in code, but I"m not sure if that actually works:

              RenderTextureMode = GFxRenderTextureMode.RTM_AlphaComposite;

              Did you set the blend mode in the material editor?

              Also, i noticed you use the alpha output of the texture (white box) instead of the diffuse output (black box) to go to the A input of Desaturation. Try using the diffuse instead.

              Comment


                #8
                Sigh.. you know when you stare yourself blind at a problem? I didn't realise it went from the opacity output, everything works fine now, I even got the logic of it: The brighter the color, the more opaque it gets, the darker the color, the more transparent it gets, but it works fine for my GUI

                Comment


                  #9
                  Is there a way to use this method to create healthbars that are floating above other pawns? Like stick this volume to a pawn?

                  EDIT: Nevermind, after some adjustments I already did it.

                  Comment

                  Working...
                  X