Creating a static input manager



  • So I want to create my own input manager wrapper class that can handle input from a bunch of sources. My plan is to have my game only interface with this wrapper class and not use FlxG.

    First of all, is this even a good idea?

    And if it is, how do I go about creating a static helper class that updates on its own? Right now I am trying to extend a FlxBasic, but it doesn't seem to want to update. Do I have to call the static class's update function manually in my FlxState? Is it better to just instantiate an object of my input manager in every FlxState and not have it be static?



  • Experimenting more, I found that a FlxSprite doesn't run update unless you add() it to a FlxState. So how do you do the same with a FlxBasic? If I want to derive a class from FlxBasic so it can run update code, how do I ensure it actually runs that code? Instantiating an object doesn't seem to be enough. If I try to add() a FlxBasic, it seems to crash the game and say "Invalid Call".



  • Hello,

    Personally i'm using HaxeFlixel - and haxe for that matter - for about 2 months now. But like the majority of HaxeFlixel users, Haxe is not my first programming language to use, so I do speak out of some experience. I'm not totally sure about what you want, but i hope this message can help you out a bit.

    @Alkamist said in Creating a static input manager:

    So I want to create my own input manager wrapper class that can handle input from a bunch of sources. My plan is to have my game only interface with this wrapper class and not use FlxG.

    First of all, is this even a good idea?

    Personally i don't think that would be a good idea. Mainly because all HaxeFlixel API is using or depending on FlxG. To quote the documentation:

    Global helper class for audio, input, the camera system, the debugger and other global properties.

    Substituting FlxG for a different system is almost like writing a different framework. If you which to do that then just drop HaxeFlixel and write something on top of OpenFL. HaxeFlixel is probably just going to be in your way if you wish to write your own framework that is not based on HaxeFlixel. Also if you just want to write a wrapper that does noting more then just call FlxG.* then you probably just adding unnessersarey code/work to you project. I personally don't see a real benefit for it. But then again, if that's just your style! Go for it! (not going to argue with style :). I do always have a global class in all my projects. But i dont use it to substitue FlxG, it's just for storing global variables. If that's what you are looking for then you can do something like this;

    /* Source G.hx */
    class G {
        public static inline var kSomeConstantI:Int      = 1;
        public static inline var kSomeConstentS:String   = "Hello World!";
    }
    

    You can then include and use G in all your sources:

    import G;
    class PlayState extends FlxState
    {
        override public function create():Void {
    	super.create();
            trace(G.kSomeConstentS);
        }
    }
    

    @Alkamist said in Creating a static input manager:

    If I try to add() a FlxBasic, it seems to crash the game and say "Invalid Call".

    FlxState extends FlxTypedGroup<T> and should be able to add() any FlxBasic without problems. Do you perhaps have the code that gives you the error? Also, what kind of functionality does your extended FlxBasic have? Are you sure its not already coded in the usual suspects? (FlxSprite, FlxTilemap, etc)

    Common use for a framework like HaxeFlixel is probably to extends the primary basic classes

    you can then just use the update(elapsed:Float) from these and trigger you own code.

    override public function update(elapsed:Float):Void
    {
        /* Call your functions and do your stuff here */
        super.update(elapsed);
        /* Or here */
    }
    

    Still, if you just looking for a way to get the updates from HaxeFlixel into a global class like the example G. Then you can just call a function of the static class with elapsed from your main FlxState. Then in your global class you can delegate the update call to any foreign - non HaxeFlixel - class methods you want.

    Personally i have some classes that extend the basic FlxClasses. Any game/project that i'm writing in HaxeFlixel will be based on those instead of the plain HaxeFlixel ones. In that way I have some basic functionality in all my projects and still be 100% HaxeFlixel compatible. Perhaps this is a better way then a global static class? But then again, this just might be my style :D I've put these classes in a git submodule so I can update all my projects easily.

    I hope this message helps you out a bit!

    Good Luck!



  • I'm interested in a good way to do this too. I have a way that works for now, but I'm sure there's a better way to do this. This only covers keyboard and virtualPad buttons. I customized FlxVirtualPad for my needs, so some names are different here.

    This really needs improvement but might give you an idea.

    IC.hx (InputControl)

    package;
    	
    import flixel.FlxG;
    import flixel.ui.FlxButton;
    
    #if !FLX_NO_KEYBOARD
    import flixel.input.keyboard.FlxKey;
    #end
    
    class IC
    {
    	public static var  menu:IState = { justPressed:false, pressed:false, justReleased:false };
    	public static var  left:IState = { justPressed:false, pressed:false, justReleased:false };
    	public static var right:IState = { justPressed:false, pressed:false, justReleased:false };
    	public static var    up:IState = { justPressed:false, pressed:false, justReleased:false };
    	public static var  down:IState = { justPressed:false, pressed:false, justReleased:false };
    	public static var     o:IState = { justPressed:false, pressed:false, justReleased:false };
    	public static var     x:IState = { justPressed:false, pressed:false, justReleased:false };
    	public static var shoot:IState = { justPressed:false, pressed:false, justReleased:false };
    	public static var  jump:IState = { justPressed:false, pressed:false, justReleased:false };
    
    #if !FLX_NO_KEYBOARD	
    	public static var _menuKeys:Array<FlxKey>  = [M, P];
    	public static var _leftKeys:Array<FlxKey>  = [A, LEFT];
    	public static var _rightKeys:Array<FlxKey> = [D, RIGHT];
    	public static var _upKeys:Array<FlxKey>    = [W, UP];
    	public static var _downKeys:Array<FlxKey>  = [S, DOWN];
    	public static var _oKeys:Array<FlxKey>     = [O, Q];
    	public static var _xKeys:Array<FlxKey>     = [X, ENTER];
    	public static var _shootKeys:Array<FlxKey> = [J];
    	public static var _jumpKeys:Array<FlxKey>  = [SPACE, Z];
    #end
    
    	public static function checkInput():Void
    	{
    #if FLX_NO_KEYBOARD
    		if (Reg.hud.buttonMenu.exists)
    		{
    			if (Reg.hud.buttonMenu.down)  menu.justPressed  = Reg.hud.buttonMenu.justPressed;
    			                              menu.pressed      = Reg.hud.buttonMenu.pressed;
    			if (!Reg.hud.buttonMenu.down) menu.justReleased = Reg.hud.buttonMenu.justReleased;
    		}
    		
    		if (Reg.vPad.buttonLeft.exists)
    		{
    			if (Reg.vPad.buttonLeft.down)  left.justPressed  = Reg.vPad.buttonLeft.justPressed;
    			                               left.pressed      = Reg.vPad.buttonLeft.pressed;
    			if (!Reg.vPad.buttonLeft.down) left.justReleased = Reg.vPad.buttonLeft.justReleased;
    		}
    		
    		if (Reg.vPad.buttonRight.exists)
    		{
    			if (Reg.vPad.buttonRight.down)  right.justPressed  = Reg.vPad.buttonRight.justPressed;
    			                                right.pressed      = Reg.vPad.buttonRight.pressed;
    			if (!Reg.vPad.buttonRight.down) right.justReleased = Reg.vPad.buttonRight.justReleased;
    		}
    		
    		if (Reg.vPad.buttonUp.exists)
    		{
    			if (Reg.vPad.buttonUp.down)  up.justPressed  = Reg.vPad.buttonUp.justPressed;
    			                             up.pressed      = Reg.vPad.buttonUp.pressed;
    			if (!Reg.vPad.buttonUp.down) up.justReleased = Reg.vPad.buttonUp.justReleased;
    		}
    		
    		if (Reg.vPad.buttonDown.exists)
    		{
    			if (Reg.vPad.buttonDown.down)  down.justPressed  = Reg.vPad.buttonDown.justPressed;
    			                               down.pressed      = Reg.vPad.buttonDown.pressed;
    			if (!Reg.vPad.buttonDown.down) down.justReleased = Reg.vPad.buttonDown.justReleased;
    		}
    		
    		if (Reg.vPad.buttonO.exists)
    		{
    			if (Reg.vPad.buttonO.down)  o.justPressed  = Reg.vPad.buttonO.justPressed;
    			                            o.pressed      = Reg.vPad.buttonO.pressed;
    			if (!Reg.vPad.buttonO.down) o.justReleased = Reg.vPad.buttonO.justReleased;
    		}
    		
    		if (Reg.vPad.buttonX.exists)
    		{
    			if (Reg.vPad.buttonX.down)  x.justPressed  = Reg.vPad.buttonX.justPressed;
    			                            x.pressed      = Reg.vPad.buttonX.pressed;
    			if (!Reg.vPad.buttonX.down) x.justReleased = Reg.vPad.buttonX.justReleased;
    		}
    		
    		if (Reg.vPad.buttonShoot.exists)
    		{
    			if (Reg.vPad.buttonShoot.down)  shoot.justPressed  = Reg.vPad.buttonShoot.justPressed;
    			                                shoot.pressed      = Reg.vPad.buttonShoot.pressed;
    			if (!Reg.vPad.buttonShoot.down) shoot.justReleased = Reg.vPad.buttonShoot.justReleased;
    		}
    		
    		if (Reg.vPad.buttonJump.exists)
    		{
    			if (Reg.vPad.buttonJump.down)  jump.justPressed  = Reg.vPad.buttonJump.justPressed;
    			                               jump.pressed      = Reg.vPad.buttonJump.pressed;
    			if (!Reg.vPad.buttonJump.down) jump.justReleased = Reg.vPad.buttonJump.justReleased;
    		}
    #else
    		if (Reg.hud.buttonMenu.exists)
    		{
    			if (Reg.hud.buttonMenu.down)  menu.justPressed  = FlxG.keys.anyJustPressed(_menuKeys)  || Reg.hud.buttonMenu.justPressed;
    			                              menu.pressed      = FlxG.keys.anyPressed(_menuKeys)      || Reg.hud.buttonMenu.pressed;
    			if (!Reg.hud.buttonMenu.down) menu.justReleased = FlxG.keys.anyJustReleased(_menuKeys) || Reg.hud.buttonMenu.justReleased;
    		}
    		
    		left.justPressed   = FlxG.keys.anyJustPressed(_leftKeys);
    		left.pressed       = FlxG.keys.anyPressed(_leftKeys);
    		left.justReleased  = FlxG.keys.anyJustReleased(_leftKeys);
    		
    		right.justPressed  = FlxG.keys.anyJustPressed(_rightKeys);
    		right.pressed      = FlxG.keys.anyPressed(_rightKeys);
    		right.justReleased = FlxG.keys.anyJustReleased(_rightKeys);
    		
    		down.justPressed   = FlxG.keys.anyJustPressed(_downKeys);
    		down.pressed       = FlxG.keys.anyPressed(_downKeys);
    		down.justReleased  = FlxG.keys.anyJustReleased(_downKeys);
    		
    		up.justPressed     = FlxG.keys.anyJustPressed(_upKeys);
    		up.pressed         = FlxG.keys.anyPressed(_upKeys);
    		up.justReleased    = FlxG.keys.anyJustReleased(_upKeys);
    		
    		o.justPressed      = FlxG.keys.anyJustPressed(_oKeys);
    		o.pressed          = FlxG.keys.anyPressed(_oKeys);
    		o.justReleased     = FlxG.keys.anyJustReleased(_oKeys);
    		
    		x.justPressed      = FlxG.keys.anyJustPressed(_xKeys);
    		x.pressed          = FlxG.keys.anyPressed(_xKeys);
    		x.justReleased     = FlxG.keys.anyJustReleased(_xKeys);
    		
    		jump.justPressed   = FlxG.keys.anyJustPressed(_jumpKeys);
    		jump.pressed       = FlxG.keys.anyPressed(_jumpKeys);
    		jump.justReleased  = FlxG.keys.anyJustReleased(_jumpKeys);
    		
    		shoot.justPressed  = FlxG.keys.anyJustPressed(_shootKeys);
    		shoot.pressed      = FlxG.keys.anyPressed(_shootKeys);
    		shoot.justReleased = FlxG.keys.anyJustReleased(_shootKeys);
    #end
    	}
    }
    	
    typedef IState = { justPressed:Bool, pressed:Bool, justReleased:Bool }
    

    To use:

    I add this to PlayState#update()

    IC.checkInput();
    

    You can use in Player#update(), or elsewhere.

    if (IC.left.pressed)
    {
    	// move player left
    }
    
    if (IC.shoot.justPressed)
    {
    	// shoot
    }
    


  • Thank you for the help!

    I suppose I should explain my situation in a little more detail. I want to make a fighting game sort of like Super Smash Bros. Melee. The characters in this game have extremely complicated finite state machines controlling them. Essentially, in these state machines, I want to be able to call on a single input manager to influence the logic.

    Imagine I want a state for running; how would I get into that state? In Super Smash Bros., if you slam the analog stick really hard to the left or right you'll run. How do I know if the player slammed the stick really hard? I can't get that information from FlxG without running a long chain of logic and timers. Slamming the stick hard isn't only used for running either, so should I perform this long chain of logic manually every time I want to use that kind of input? It gets even worse if I want to allow for other types of controllers like a keyboard.

    Maybe I'm missing something, but it seems like my situation would be made a lot easier with a single universal wrapper that I could interface with to get these inputs. I could just sort out that logic in the wrapper. and if I wanted to change something later it would change everywhere.

    I guess the idea behind making it static is so you don't have to pass the input manager around everywhere. And also, it seems strange to have to instantiate the input manager for every playstate. I guess I'm also confused about if I should be called update functions manually and passing that update down the chain of children, or inheriting from FlxBasic and using that for updating.

    My input manager at the top level looks sort of like this so far (the preview shows this pasting in very strangely, not sure how to make it look nice, sorry). When I try to add() this object in my playstate, I get that error message:

    package;
    
    import flixel.FlxG;
    import flixel.FlxBasic;
    
    import universalInput.UniversalController;
    
    class InputManager extends FlxBasic
    {
        public var stickDeadZone(default, never):Float = 0.15;
        public var triggerDeadZone(default, never):Float = 0.15;
    
        public var universalController:UniversalController = new UniversalController();
    
        public function new()
        {
            super();
        }
    
        override public function update(elapsed:Float):Void
        {
            super.update(elapsed);
    
            if (FlxG.keys.justPressed.SPACE)
            {
                universalController.yButton.press();
            }
    
            if (FlxG.keys.justReleased.SPACE)
            {
                universalController.yButton.release();
            }
        }
    }
    


  • @Alkamist

    Sorry about that, I misunderstood.

    I see you are extending FlxBasic so you have an update() function for your controller. If you don't extend anything in your InputManager class, you can make a static function checkInput() to run the code you currently have in update(), and call checkInput() in PlayState#update(). This way works for me since I have PlayState as my only FlxState and all other states (menus, etc) are FlxSubState, and PlayState starts first.

    Maybe this won't work with your game, but thought I'd explain my usage anyway, in case it helps.

    To format the code use 3 back ticks ``` at start and end of your code block, on their own line



  • @dean Interesting, I didn't know of a FlxSubState. I'll have to check that out. I could maybe define one input manager in my main FlxState and just run all the menus and levels as FlxSubStates and pass in the manager or something. Thanks!


Log in to reply
 

Looks like your connection to HaxeFlixel was lost, please wait while we try to reconnect.