endlesstravel / Love2dCS

C# Wrapper for LÖVE, a 2d game engine
MIT License
164 stars 14 forks source link

Question on how to go about creating a input manager #42

Open Shadowblitz16 opened 5 years ago

Shadowblitz16 commented 5 years ago

I have a question on how to create a input manager that supports multiple players along with supporting remapping.

I currently thinking about doing something like this..

public class InputMap
{
        public bool Enabled    { get; set; } = false;

        public bool[] A      { get; set; } = { false, false };
        public bool[] B      { get; set; } = { false, false };
        public bool[] X      { get; set; } = { false, false };
        public bool[] Y      { get; set; } = { false, false };
        public bool[] L      { get; set; } = { false, false };
        public bool[] R      { get; set; } = { false, false };
        public bool[] DPadU  { get; set; } = { false, false };
        public bool[] DPadD  { get; set; } = { false, false };
        public bool[] DPadL  { get; set; } = { false, false };
        public bool[] DPadR  { get; set; } = { false, false };
        public bool[] Start  { get; set; } = { false, false };
        public bool[] Select { get; set; } = { false, false };

        public GamepadButton AMap { get; set; } = GamepadButton.A;
        public GamepadButton BMap { get; set; } = GamepadButton.B;
        public GamepadButton XMap { get; set; } = GamepadButton.X;
        public GamepadButton YMap { get; set; } = GamepadButton.Y;
        public GamepadButton LMap { get; set; } = GamepadButton.LeftShoulder;
        public GamepadButton RMap { get; set; } = GamepadButton.RightShoulder;
        ...

}

however since Keyboards, Controller Buttons and Controller axises all use different enums and functions I really don't know how to go about doing this

endlesstravel commented 5 years ago

Oh, There are several different solutions. Let me describe the one of them.

you need create two class. First represent a player input mapping. The second represent which action corresponding to which type of device and which type of keycode or button


    abstract class InputMappingAction
    {
        public abstract bool IsDown();
    }

    class InputMappingKeyboard : InputMappingAction
    {
        KeyConstant key;

        public InputMappingKeyboard(KeyConstant key)
        {
            this.key = key;
        }

        public override bool IsDown() => Keyboard.IsDown(KeyConstant.Space);
    }

    class InputMappingJoystick : InputMappingAction
    {
        Joystick j;
        int actionKey;

        public InputMappingJoystick(Joystick j, int actionKey)
        {
            this.j = j;
            this.actionKey = actionKey;
        }

        public override bool IsDown() => j.IsConnected() ? j.IsDown(actionKey) : false;
    }

    class PlayerInputMapping
    {
        const int JumpMapping = 0;
        const int LeftMoveMapping = 1;
        InputMappingAction[] container = new InputMappingAction[]{
            new InputMappingKeyboard(KeyConstant.Space),
            new InputMappingKeyboard(KeyConstant.Left),
        };

        public bool IsJump()
        {
            var jump = container[JumpMapping];
            return jump != null ? jump.IsDown() : false;
        }

        public bool IsLeftMove()
        {
            var leftMove = container[LeftMoveMapping];
            return leftMove != null ? leftMove.IsDown() : false;
        }

        public bool UpdateCaptureKeyMapping(int mappingIndex)
        {
            // test keyboard
            KeyConstant[] keyConstants = (KeyConstant[])System.Enum.GetValues(typeof(KeyConstant));
            foreach (var key in keyConstants)
            {
                if (Keyboard.IsDown(key))
                {
                    container[mappingIndex] = new InputMappingKeyboard(key);
                    return true;
                }
            }

            // test joystick
            foreach (var joy in Joystick.GetJoysticks())
            {
                for (int i = 0; i < joy.GetButtonCount(); i++)
                {
                    if (joy.IsDown(i))
                    {
                        container[mappingIndex] = new InputMappingJoystick(joy, i);
                        return true;
                    }
                }
            }

            return false;
        }
    }
Shadowblitz16 commented 5 years ago

well the way I did it in gamemaker was I basically had a static object or script that would take a state up, down, held a button a, b, up, down, left, right, start, select and a player index

it would use two bools one for previous input and one for current input and would use those to check against the state. but this helps alot thankyou

Shadowblitz16 commented 5 years ago

I have one more question. is there a way to get a list or array of all the keyconstents and loop through them and check for the first pressed key?

public abstract class InputMappingGroup
{
        int Player = 0; 
        InputMappingKeyboard[] Keyboards = new InputMappingKeyboard[12];
        InputMappingJoyStick[] JoySticks = new InputMappingJoyStick[12];

        public bool GetButtonInput(ButtonType type, ButtonState state)
        {
                return Player == 0 && Keyboards[(int)type].GetInput(state) || JoySticks[(int)type].GetInput(state);
        }
        public void SetButtonInput(ButtonType type, ButtonState state)
        {
                if (Player == 0) //should only execute first line
                Keyboards[(int)type].SetInput(state); 
                JoySticks[(int)type].SetInput(state);
        }
        public void MapButtonInput(ButtonType type)
        {
                if (Player == 0)
                {
                        foreach (KeyConstant key in Keyboard.???)
                        {
                                if (Keyboard.IsDown(key)) Keyboards[(int)type].MapInput(key);
                        }
                }

                foreach (var joyStick in JoySticks)
                {
                        for (var i=0; i< joyStick.Joy.GetButtonCount(); i++)
                        {
                                if (joyStick.Joy.IsDown(i)) Keyboards[(int)type].MapInput(i);
                        }
                }
        }
        public void Update()
        {
                if (Player == 0)
                {
                        foreach (var Keyboard in Keyboards)
                        {
                                Keyboard.Update();
                        }
                }

                foreach (var JoyStick in JoySticks)
                {
                        JoyStick.Update();
                }
        }
}
endlesstravel commented 5 years ago

I have one more question. is there a way to get a list or array of all the keyconstents and loop through them and check for the first pressed key?

we can do it by :

            // test keyboard
            KeyConstant[] keyConstants = (KeyConstant[])System.Enum.GetValues(typeof(KeyConstant));
            foreach (var key in keyConstants)
            {
                if (Keyboard.IsDown(key))
                {
                    // this is the first key you pressed
                    break;
                }
            }
Shadowblitz16 commented 5 years ago

ok thankyou I will try it