Question

My game has a property of type IInput:

private IInput Input;

Before the game loop, I add some key bindings like this:

protected override void Initialize()
{
    input.BindKey( Keys.Escape, Exit );
    // etc...
}

The BindKey method adds an item to the key bindings dictionary of the Input class:

internal class Input : IInput
{
    private readonly IDictionary<Keys, Action> keyBindings;

    public void BindKey( Keys key, Action action )
    {
        keyBindings.Add( key, action );
    }

    public void Process()
    {
        // update keyboard state...            

        foreach ( var binding in keyBindings )
        {
            if ( keyboard.IsKeyDown( binding.Key )
            {
                binding.Value();
            }
        }
    }
}

The Process method is called during the game loop. This works well, but I would like to bind a key to a method that accepts a parameter, ala:

input.BindKey( Keys.W, Move( Directions.Up ) );

How can I do this? I tried changing the keyBindings property of the Input class:

private readonly IDictionary<Keys, Delegate> keyBindings;

This requires me to change my original bindings to:

input.BindKey( Keys.Escape, new Action( Exit ) );

But a similar approach doesn't work for binding methods with parameters:

// compiler error: "Method name is expected"
input.BindKey( Keys.W, new Func<Directions>( Move( Directions.Up ) ) );

// compiler error: "Delegate constructor is invoked with 2 argument(s)"
input.BindKey( Keys.W, new Func<Directions>( Move, Directions.Up ) );

This doesn't work either:

// compiler error: "Argument type 'lambda expression' is not assignable to parameter type 'System.Delegate'"
input.BindKey( Keys.W, x => Move( Vector2.Zero ) );

EDIT

I was using Func incorrectly. I want to supply the direction to a method, but I don't care what it returns. I'm not getting very far with this either:

// 1. Cannot use "void" as a type argument
// 2. Method name is expected
input.BindKey( Keys.W, new Func<Vector2, void>( Move( Directions.Up ) );

// Cannot apply operator ">" to operands of type "null" and "void"
input.BindKey( Keys.W, new Func<Vector2, null>( Move( Directions.Up ) );

Even if I change the Move method to return true:

// Method name is expected
input.BindKey( Keys.W, new Func<Vector2, bool>( Move( Directions.Up ) );    
Was it helpful?

Solution

The key concept here is, using the delegation, you'e just pointing to a method which accepts an input parameter, so it won't work like what you've written in your code, the parameter should be passed during invocation! So as a workaround you can write st. like this:

internal class Input : IInput
{
  private readonly IDictionary<Keys, Tuple<Action<Directions>, Directions>> keyBindings;
  public void BindKey( Keys key, Tuple<Action<Directions>, Directions> action)
  {
    keyBindings.Add( key, action );
  }

  public void Process()
  {
    // update keyboard state...            

    foreach ( var binding in keyBindings )
    {
        if ( keyboard.IsKeyDown( binding.Key )
        {
            binding.Value.Item1(binding.Value.Item2);
        }
    }
  }
}

Use input.BindKey like this:

input.BindKey(Keys.W, Tuple.Create<Action<Directions>, Directions>(Move, Directions.Up));

EDIT: You can even do that with you original Input class and instead of pointing to the Move method, you can create a delegate to do it for you (s.t like @Magus said in the comments) like this:

input.BindKey(Keys.W, () => Move(Directions.Up));

this way, during the invocation, this delate will be invoked and in turn as you can see it invokes the Move method with required parameter.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top