Announcement

Collapse
No announcement yet.

Creating a Mouse Cursor in the HUD

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

    Creating a Mouse Cursor in the HUD

    UPDATED 9/2/2011

    This intermediate level tutorial will explain how to add a toggle-able mouse cursor to an already existing HUD. The mouse cursor will not exist on the stage of the Flash file, but only in the library. It will be instantiated and attached to the _root Flash timeline at runtime by UnrealScript when the user presses and holds the Left Shift key on the keyboard.

    NOTE: Using this method, it is best to place all your UI content such as buttons, dropdowns, checkboxes, etc. (with the exception of the mouse cursor) inside a container movie clip that lives at _root. The mouse cursor will also live at _root. This will ensure the mouse is always on top, even when a popup UI element opens (such as that found in a drop down menu widget). These popup elements normally set themselves above everything else when they are created dynamically at runtime. Without being inside a container movie clip, the popup widget would appear above the mouse cursor.

    Step 1 - Flash
    • Open your HUD FLA file, and create a new mouse cursor graphic on the stage, either with a bitmap image or using Flash drawing tools. Be sure the tip is pointing to the top-left corner, like most cursors.
    • Now Select the cursor graphic, right click, and choose 'Convert to Symbol'.
    • In the Name field, enter MouseContainer.
    • Type should be Movie Clip.
    • Registration should be set to top-left.
    • Enable Export for ActionScript and Export in frame 1.
    • In the Identifier field, enter MouseContainer.
    • Press OK.
    • Now, double click the cursor on stage to enter the MouseContainer movie clip.
    • Select the graphic again, right click, and choose 'Convert to Symbol' again.
    • Name it MouseImage
    • Type should again be Movie Clip.
    • Registration should again be top-left.
    • Press OK.
    • Select the cursor movie clip on stage, and give it an instance name of mouseCursor_mc.

      You should now have the following hierarchy:

      MouseContainer movieclip (no instance name)
      -MouseImage movieclip (instance name: mouseCursor_mc)
      --mouse cursor bitmap image

    • Add any filters you like to this movie clip (mouseCursor_mc), such as a drop shadow filter.
    • Now, unselect mouseCursor_mc, add a new layer to the timeline (the timeline inside MouseContainer), and call it actions.
    • On frame 1 of the actions layer enter this code:


    ActionScript 2.0 Code Sample

    Code:
    import flash.external.ExternalInterface;
    
    Mouse.hide();
    
    var mouseListener:Object = new Object();
    
    mouseListener.onMouseMove = function ()
    {
        mouseCursor_mc._x = _root._xmouse;
        mouseCursor_mc._y = _root._ymouse;
    	
        ExternalInterface.call("UpdateMousePosition", _root._xmouse, _root._ymouse);
       
        updateAfterEvent();
    };
    
    Mouse.addListener(mouseListener);
    • Now return to the _root timeline of your Flash file, and delete the cursor movie clip (MouseContainer) form the stage. Don't worry, it still exists in the library. NOTE: You should have two movie clips in the library: MouseContainer & MouseImage, plus the mouse cursor bitmap image.
    • Save, Publish, and Import your modified HUD file into UDK.


    Step 2 - UnrealScript - HUD Class Changes (extends GFxMoviePlayer)

    First, add these vars to your HUD class (which extend GFxMoviePlayer):

    Code:
    // Standard Flash Objects
    var GFxObject RootMC, MouseContainer, MouseCursor;
    
    var array<ASValue> args;
    Add this code to your Start() or Init() function.

    Code:
    RootMC = GetVariableObject("_root");
    AddFocusIgnoreKey('LeftShift'); // Tells HUD to ignore Left Shift keyboard presses
    Add these functions to your class:

    Code:
    event UpdateMousePosition(float X, float Y)
    {
        local MouseInterfacePlayerInput MouseInterfacePlayerInput;
    
        MouseInterfacePlayerInput = MouseInterfacePlayerInput(GetPC().PlayerInput);
    
        if (MouseInterfacePlayerInput != None)
        {
            MouseInterfacePlayerInput.SetMousePosition(X, Y);
        }
    }
    Code:
    /** Toggles mouse cursor on/off */
    function ToggleCursor(bool showCursor, float mx, float my)
    {	
        if (showCursor)
        {
    	MouseContainer = CreateMouseCursor();
    	MouseCursor = MouseContainer.GetObject("my_cursor");
    	MouseCursor.SetPosition(mx,my);
    	MouseContainer.SetBool("topmostLevel", true);
        }
        else
        {
    	MouseContainer.Invoke("removeMovieClip", args);
    	MouseContainer = none;	
        }
    	
        self.bIgnoreMouseInput = !showCursor;		
    }
    Code:
    function GFxObject CreateMouseCursor()
    {
        return RootMC.AttachMovie("MouseContainer", "MouseCursor");
    }
    The CreateMouseCursor function attaches the Movie Clip in Flash that has the Identifier of MouseContainer to the _root timeline (the Stage), and gives it an instance name of MouseCursor.

    In the defaultproperties, be sure to include:

    Code:
    defaultproperties
    {
        bIgnoreMouseInput = true
    }

    Step 3 - UnrealScript - HUD Wrapper Class Changes

    First, add these variables to your HUD Wrapper class (the class that instantiates the HUD class above):

    Code:
    var GFxObject 			HudMovieSize;
    var MouseInterfacePlayerInput MouseInterfacePlayerInput;
    var float MouseX, MouseY;
    Next, add this line of code (in red) to your PostBeginPlay() function:

    Code:
    simulated function PostBeginPlay()
    {
        super.PostBeginPlay();
        
        // Your HUD instantiation code here...
    
        // Stage.originalRect contains the original width and height of the SWF file.
        // Replace HudMovie with the name of your instantiated HUD.
        HudMovieSize = HudMovie.GetVariableObject("Stage.originalRect");
    
        MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);
    }
    Next, add this function, which will be fired off (executed) when the user presses the Left Shift key. This function calls the ToggleCursor() function in the HUD class, and passes it true if the left shift key is pressed down, or false if it is released. It also passes the X & Y position of the mouse.

    Code:
    exec function SetShowCursor(bool showCursor)
    {
        // Replace HudMovie with the name of your instantiated HUD.
        HudMovie.ToggleCursor(showCursor, MouseX, MouseY);
    }
    Now, add these lines of code to the PostRender() event function, just above the HudMovie.TickHud(0) call. These two lines of code are used to get the current X & Y position of the mouse cursor.:

    Code:
    event PostRender()
    {	
        super.PostRender();
    
        MouseX = MouseInterfacePlayerInput.MousePosition.X;
        MouseY = MouseInterfacePlayerInput.MousePosition.Y;
    
        // Tick HUD
        if (HudMovie != none)
        {
    	HudMovie.TickHud(0);
        }
    }

    Step 4 - UnrealScript - MouseInterfacePlayerInput Class

    Create this new class and save it as MouseInterfacePlayerInput.uc. Be sure to replace all instances of SFHudWrapper (marked in red in the code) with the name of your HUD Wrapper class:

    Code:
    class MouseInterfacePlayerInput extends PlayerInput;
    
    // Stored mouse position. Set to private write as we don't want other classes to modify it, but still allow other classes to access it.
    var PrivateWrite IntPoint MousePosition; 
    var SFHudWrapper SFHudWrapper;
    var float HudX, HudY;
    
    event PlayerInput(float DeltaTime)
    {
        GetHudSize();
    	
        if (myHUD != None) 
        {
    	// Add the aMouseX to the mouse position and clamp it within the viewport width
    	MousePosition.X = Clamp(MousePosition.X + aMouseX, 0, HudX); 
    	// Add the aMouseY to the mouse position and clamp it within the viewport height
    	MousePosition.Y = Clamp(MousePosition.Y - aMouseY, 0, HudY); 
        }
        Super.PlayerInput(DeltaTime);
    }
    
    // This function gets the original width and height of the HUD SWF and stores those values in HudX and HudY.
    function GetHudSize()
    {
        // First store a reference to our HUD Wrapper and get the resolution of the HUD
        SFHudWrapper = SFHudWrapper(myHUD);
        HudX = SFHudWrapper.HudMovieSize.GetFloat("width");
        HudY = SFHudWrapper.HudMovieSize.GetFloat("height");
    }
    
    function SetMousePosition(int X, int Y)
    {
        GetHudSize();
    	
        if (MyHUD != None)
        {
    	MousePosition.X = Clamp(X, 0, HudX);
    	MousePosition.Y = Clamp(Y, 0, HudY);
        }
    }
    
    defaultproperties
    {
    }
    Step 5 - UnrealScript - PlayerController Class Changes

    Add this line to the defaultproperties of your PlayerController class:

    Code:
    defaultproperties
    {
        InputClass=class'MouseInterfacePlayerInput'
    }


    Step 6 - Config Files

    Then, add this line to your DefaultInput.ini file:
    Code:
    .Bindings=(Name="LeftShift",Command="SetShowCursor true | Onrelease SetShowCursor false")

    • Save and compile scripts.
    • Test your new cursor by running the game!



    Alternative: A good custom mouse cursor class written by UDK developer Marc Rogerson, can be found here: http://www.f00n.com/unreal/2010/06/3...r-class-as2-0/

    #2
    mmm one question Lets say i have a flash movie im rendering to a BSP wall

    is it possible to hide the mouse in this HUD and then set it to true once i interact with the BSP wall?

    sorry if my question wasnt clear enough spanish is my native lenguage so its a bit hard for me talk on english

    Comment


      #3
      Sure.

      Set your mouse cursor's _visible flag to false by default when it is created. Place a trigger object in the game world next to your BSP object that tells your HUD to toggle the _visible flag of the mouse true or false by executing an UnrealScript function in the HUD class.

      Comment


        #4
        got it thanks

        Comment


          #5
          I'm not sure how to trigger it in Kismet. I mean, I've got function like this inside my actionscript:
          Code:
          import gfx.controls.Cursor;
          
          var myCursor = new Cursor("cursorMC");
          Mouse.hide();
          myCursor.disable();
          function showCursor(MyBool:Number)
          {
          	if(MyBool == 1)
          	{
          		myCursor.enable();
          	}
          	else
          	{	
          		myCursor.disable()
          	}
          }
          It uses Deadlyzer0's mouse class. Enable simply turns visibility to true, and disable to false. Then I try to invoke actionscript in Kismet, by using showCursor, and I've got nothing.

          Comment


            #6
            I can't speak to a user created mouse class, but all you have to do to enable a movie clip's visibility in AS is:

            myCursor._visible = false;
            myCursor._visible = true;

            And, in UnrealScript it is:

            myCursor.SetBool("_visible", false); // or true

            Comment


              #7
              I've made a function showCursor, which gets a bool, and if it's true, sets myCursor._visible to true, if not, to false. But when I call it from kismet invoke, nothing really happens.

              Comment


                #8
                This should be your setup.

                1. Open GFx Movie in Kismet opens the SWF in question.
                2. Open GFx Movie's Player Owner is linked to a Player Variable.
                3. Open GFx Movie's Movie Player is linked to an Object Variable.
                4. Invoke's Movie Player is linked to the same Object Variable.
                5. Invoke's Method (Function) Name has the fully qualified path to your AS method. (e.g. my method is in a movie clip called 'myMovieClip' on the _root timeline so I would put _root.myMovieClip.MyMethodName in the Method Name field.)
                6. Add an Argument.
                7. Set it to AS_Boolean type.
                8. check or uncheck the B.

                If the Invoke AS node is being called by Open GFx Movie, try adding a Delay (Action->Misc->Delay) between Open GFx Movie and Invoke, and put at least a 0.1 second delay. The Invoke might be happening before the movie is actually finished loading. That shouldn't be the case, but I've seen it happen.



                Uploaded with ImageShack.us

                Comment


                  #9
                  Well, that's almost exactly what I had in my Kismet, except for delay, and yet, it won't work. I guess I'll try Your way of showing mouse cursor, maybe it will work better.
                  EDIT: Nope, no help either. Right now, using your way, my cursor won't update it's position, but I can still click blindly on the movie. I guess I'll have to find different approach. Can I enable input only on trigger event? And only mouse input, too. Using ignore keys in Kismet crashes my UDK.
                  EDIT2: Ok, I got it working, it finally shows and hides my cursor - but there is still one more problem. Even though cursor is not visible, it still intercepts LMB clicks - and as I said, focus ignore keys in Kismet won't work for me. Any ideas?
                  EDIT3: I've got it working in AS, using simple if (myCursor._disabled==false) [which means basically the same, as myCursor._visible==true]), and then throwing every button.onRelease function. But when I import it to UDK, it won't intercept clicks, no matter if the cursor is enabled or not.
                  EDIT4: Ok, got it off at the beginning, turning on after stepping on trigger, via using two invoke actionscripts. One turning bool in function setting mouse cursor visible or not, and one running function that listen for button clicks. But, it still won't work to turn off the mouse clicks after I step off the trigger. Mouse cursor dissappears as it should, but the clicks are still intercepted. Any ideas? ;P

                  Comment


                    #10
                    Ok, first of all - focus ignore keys are for keyboard and gamepad input, not mouse.

                    If you're doing your scripting with ActionScript, you can remove the cursor movie clip using:

                    ActionScript 2.0 Code Sample

                    Code:
                    function RemoveCursor()
                    {
                        _root.MouseCursor.removeMovieClip();
                        ExternalInterface.call("IgnoreMouse", true);
                    }
                    This completely removes the cursor movie clip, not just hiding it. Then just use Invoke in Kismet to call RemoveCursor(). The RemoveCursor() function above assumes you have attached the mouse to the _root timline, and that it has an instance name of MouseCursor.

                    Then, in your UnrealScript HUD class, you'll want a function like this, to intercept the ExternalInterface call:

                    UnrealScript

                    Code:
                    function IgnoreMouse(bool toggle)
                    {
                        // Use me in UDK versions starting with Sept 2010
                        self.bIgnoreMouseInput = toggle; 
                    
                        // Or use me in UDK versions May 2010 through Oct 2010
                        SetFocus (toggle, toggle)
                    }
                    Keep in mind there are two aspets to mouse input - the cursor graphic, which is just an image, and really has nothing to do with input, and the mouse input, which is the part that actually listens for and intercepts mouse clicks, etc.

                    And this won't keep your mouse from left clicking. The game itself intercepts mouse clicks.

                    So, if you want to stop those mouse clicks from being interecepted by the Flash file, you can do this:

                    Add a boolean variable to your HUD class:

                    Code:
                    var bool bNoMouse;
                    Then, modify your IgnoreMouse() function:

                    Code:
                    function IgnoreMouse(bool toggle)
                    {
                        // Use me in UDK versions starting with Sept 2010
                        self.bIgnoreMouseInput = toggle; 
                    
                        // Or use me in UDK versions May 2010 through Oct 2010
                        SetFocus (toggle, toggle)
                    }
                    Finally, Create this function:

                    Code:
                    event bool FilterButtonInput(int ControllerId, name ButtonName, EInputEvent InputEvent)
                    {
                    	/* Test if a button has been moused over and the left mouse button has been pressed. */
                    	if (self.bNoMouse == true && ButtonName == 'LeftMouseButton')
                    	{
                    		return true; // Do not intercept LeftMouseButton clicks.
                    	}
                    	return false; // Intercept LeftMouseButton clicks.
                    }

                    Comment


                      #11
                      Is the whole intercepting of keys and mouse movement something that is by default set up to propagate only to the SWF or to the GFxHUD? Or is such functionality also available in the base HUD class without using Flash?

                      Comment


                        #12
                        Unreal intercepts all input. Handling it is usually done with binding in DefaultInput.ini

                        When you have a GFx Movie Player being shown though, you can tell it to intercept input.

                        Comment


                          #13
                          I've got an error: 'Unrecognized member 'bIgnoreMouseInput' in class <GFxUI_Keycode>'

                          Comment


                            #14
                            Where did you put bIgnoreMousInput? What class? What does that class extend?

                            Comment


                              #15
                              GFxUI_keycode class, extending GFxMoviePlayer. It's right at the end of the file, just before defaultproperties.

                              Comment

                              Working...
                              X