Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm currently working on creating an input-action system for my game. I simply wanted an easy way to map an ingame action to a certain key.

I've managed to do so with the following method: Create a class that holds both the action and the key for the action:

 public static class InputManager
{
    //states
    public static KeyboardState previousKeyboardState;
    private static KeyboardState keyboardState;
    private static MouseState mouseState;

    public static MouseState previousMouseState;
    public static InputState currentInputState = InputState.Gameplay;

    //different types of input layers
    public enum InputState
    {
        Interface,
        Gameplay
    }

    public enum GAction
    {
        Confirm,
        Cancel, 
        MoveLeft, 
        MoveRight, 
        MoveUp, 
        MoveDown, 
        Inspect
    }

    public enum InputButtons
    {
        W,
        S,
        A,
        D,
        Enter,
        Space
    }

    private static List<ActionMap> actionmaps;

    /// <summary>
    /// Map an action to a key, attaching conditions for the action in the progress
    /// </summary>
    /// <param name="gaction">the action enum item</param>
    /// <param name="key">key for the action</param>
    /// <param name="action">the function we want to run</param>
    /// <param name="conditionList">allowed condition states</param>

    public static void MapAction(GAction gaction, Keys key, List<InputManager.InputState> conditionList)
    {
        actionmaps[(int)gaction] = new ActionMap();
        actionmaps[(int)gaction].conditions = conditionList;
        actionmaps[(int)gaction].keyboardKeys.Add(key);
    }


    public static KeyboardState currentKeyboardState
    {
        get { return keyboardState; }
        set { keyboardState = value; }
    }

     /// <summary>
    /// Check if a key is pressed.
    /// </summary>
    public static bool IsKeyPressed(Keys key)
    {
        return currentKeyboardState.IsKeyDown(key);
    }

    public static MouseState currentMouseState
    {
        get { return mouseState; }
        set { mouseState = value; }


    }

    // / / / / / / / / / / / / /
    //   f u n c t i o n s   / /
    // / / / / / / / / / / / / /

    public static void init()
    {
        int count = Enum.GetValues( typeof( GAction ) ).Length;
        actionmaps = new List<ActionMap>(count);
        for (int i = 0; i < count; i++)
        {
            actionmaps.Add(new ActionMap());
        }


        InputManager.MapAction(InputManager.GAction.Confirm, Keys.Space, null,
                    new List<InputManager.InputState>() { InputManager.InputState.Interface});
    }

    public static void update()
    {
        previousMouseState = currentMouseState;
        previousKeyboardState = keyboardState;

        currentKeyboardState = Keyboard.GetState();
        currentMouseState = Mouse.GetState();  
    }

    /// <summary>
    /// checks if action is both used and allowed to be used [conditions]
    /// </summary>
    /// <param name="action"></param>
    /// <returns></returns>
    public static bool IsActionUsedNAllowed(GAction action){

        if(IsActionPressed(action) && isActionAllowed(action)){
            return true;
        }

        return false;
    }

    public static bool isActionAllowed(GAction action){

        return CheckAllowedActionStates(actionmaps[(int)action]);
    }

    private static bool CheckAllowedActionStates(ActionMap actionmap){

         for (int i = 0; i < actionmap.conditions.Count; i++)
        {
            if (currentInputState != actionmap.conditions[i])
            {
                return false;
            }
        }

        return true;
    }



    public static bool IsActionPressed(GAction action)
    {
        return IsActionMapPressed(actionmaps[(int)action]);
    }
    private static bool IsActionMapPressed(ActionMap actionMap)
    {
        for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
        {
            if (IsKeyPressed(actionMap.keyboardKeys[i]))
            {
                return true;
            }
        }

        return false;
    }

}
}

To map and check for an action you use the following statements:

 InputManager.MapAction(InputManager.GAction.MoveDown, Keys.S, ControlActor,
                    new List<InputManager.InputState>() { `InputManager.InputState.Gameplay });`

InputManager.IsActionUsedNAllowed(InputManager.GAction.MoveDown);

I'm not liking the fact that I have to use an if statement to check if any actions have been performed though. I would much rather use events. But create a delegate and event for each action seems sluggish.

Is there any way to generate an event for every field in the enum? I know I could write my own event system, but I want to avoid mixing up c#'s build in events and my own events.

share|improve this question
    
Accepting questions is a good thing (hint hint) –  bummzack Sep 29 '11 at 19:45

3 Answers 3

I was working on a tool to provide event based input for XNA games and I came across this issue as well - do I have a single key pressed event which passes in the key, or 70 separate events. I wrote some code to generate all the event delegates for all the Keys and then I just stuck them in to an input manager. I never really got round to doing any stress testing... I don't know exactly what optimisation the C# compiler does, but I would like to think that if you don't use an event, it doesn't get included, but this is probably wishful thinking.

Anyway, if you are interested I used the following to generate the events:

for (int i = (int)Keys.A; i <= (int)Keys.Zoom; i++)
{
    string line = "public static event EventHandler " + ((Keys)i).ToString() + "KeyPressed = delegate { };";
    lines.Add(line);
}

for (int i = (int)Keys.A; i <= (int)Keys.Zoom; i++)
{
    lines.Add("case \""+((Keys)i).ToString()+"\":");
    lines.Add(((Keys)i).ToString() + "KeyPressed(null, new KeyPressedEventArgs(keyPressed));");

    lines.Add("break;");
    lines.Add("");

}

The output it here (https://github.com/thk123/XGT/commit/475b31ecd8262efd214e397bf7689e94da1196bd), but like I said, it is pretty monstrous.

share|improve this answer

What if you passed an Action (if needed)? When the input manager detects the key is pressed it can call the given action. That action in turn can do whatever you want.

Maybe a Func will work better for you, but the basic idea is instead of having the IM throw an event, just have it call the action/func directly

Action
Func

share|improve this answer

Here is what I used for my input handling in a XNA project I was working on. Not sure if this is a good system or not, but it seemed to work for my purposes. It sort of evolved to this, so it can probably be refactored to be a bit better. I am adding some comments, but its been a while since I looked at it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;

namespace ClientLayer
{
    public class KeyController
    {
        //delegate to represent the action that is to be preformed on key press
        public delegate void KeyAction(List<Object> args);

        //probably should have created a class to hold all this information
        private Dictionary<Keys, int> keySensitivity;//regulates how often the action is invoked
        private Dictionary<Keys, int> keyPresses;//keeps track of how many times the key has been pressed since it last invoked the action
        private Dictionary<Keys, KeyAction> keyMapping;
        private Dictionary<Keys, List<Object>> keyArgs;
        private List<Keys> ToBeEnacted;
        private Dictionary<Keys, bool> ActOnPressed;

        public KeyController()
        {
            keyMapping = new Dictionary<Keys, KeyAction>();
            keySensitivity = new Dictionary<Keys, int>();
            keyPresses = new Dictionary<Keys, int>();
            keyArgs = new Dictionary<Keys, List<object>>();
            ToBeEnacted = new List<Keys>(); //list of keys who's action should be invoked
            ActOnPressed = new Dictionary<Keys, bool>();
        }

        public void AddKeyMapping(Keys key, KeyAction action, int sensitivity, List<object> args)
        {
            keyMapping.Add(key, action);
            keySensitivity.Add(key, sensitivity);
            keyArgs.Add(key, args);
        }

        public void AddKeyMapping(Keys key, KeyAction action, List<object> args)
        {
            keyMapping.Add(key, action);
            keyArgs.Add(key, args);
            ActOnPressed.Add(key, true);
        }

        public void AddKeyMapping(Keys key, KeyAction action, List<object> args, bool actOnPressed)
        {
            keyMapping.Add(key, action);
            keyArgs.Add(key, args);
            ActOnPressed.Add(key, actOnPressed);
        }

        public void AddKeyMapping(Keys key, KeyAction action, int sensitivity)
        {
            keyMapping.Add(key, action);
            keySensitivity.Add(key, sensitivity);
            keyArgs.Add(key, new List<object>());
        }

        public void AddKeyMapping(Keys key, KeyAction action)
        {
            keyMapping.Add(key, action);
            keyArgs.Add(key, new List<object>());
            ActOnPressed.Add(key, true);
        }

        public void AddKeyMapping(Keys key, KeyAction action, bool actOnPressed)
        {
            keyMapping.Add(key, action);
            keyArgs.Add(key, new List<object>());
            ActOnPressed.Add(key, actOnPressed);
        }

        public void handleKeyPress(KeyboardState state)
        {
            Keys[] pressed = state.GetPressedKeys();
            foreach (Keys key in pressed)
            {
                if (keyMapping.ContainsKey(key))
                {
                    List<Object> args = keyArgs[key];
                    if (keySensitivity.ContainsKey(key))
                    {
                        if (keyPresses.ContainsKey(key))
                        {
                            if (keyPresses[key] <= 0)
                            {
                                keyPresses.Remove(key);
                                keyMapping[key](args);
                            }
                            else
                            {
                                keyPresses[key] -= 1;
                            }
                        }
                        else
                        {
                            keyPresses[key] = keySensitivity[key];
                            keyMapping[key](args);
                        }
                    }
                    else
                    {
                        if (ActOnPressed.ContainsKey(key) && ActOnPressed[key])
                        {
                            keyMapping[key](args);
                        }
                        else
                        {
                            if (!ToBeEnacted.Contains(key))
                            {
                                ToBeEnacted.Add(key);
                            }
                        }
                    }
                }
            }

            for (int x = ToBeEnacted.Count - 1; x >= 0; x--)
            {
                Keys key = ToBeEnacted[x];
                if (!pressed.Contains(key))
                {
                    List<Object> args = keyArgs[key];
                    keyMapping[key](args);
                    ToBeEnacted.RemoveAt(x);
                }
            }


            IEnumerable<KeyValuePair<Keys, int>> tbr = keyPresses.Where(x => !pressed.Contains(x.Key));
            List<KeyValuePair<Keys, int>> tbrList = tbr.ToList();
            foreach (KeyValuePair<Keys, int> pair in tbrList)
            {
                keyPresses.Remove(pair.Key);
            }
        }
    }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.