1

我正在使用 .NET 开发视频游戏,但我正在努力解决如何正确实现命令队列然后立即执行它们。

我的电子游戏很简单,它是一架会移动的飞机。我对Command Pattern的实现如下,然后我在Player类上实现对这些命令的管理:

public abstract class ICommand {
        Category CategoryProperty { get; set; }

        public abstract void Execute();
    }

public class MoveAircraftCommand : ICommand
    {
        private Vector2f _velocity;
        Aircraft aircraft;

        public Category CategoryProperty {
            get {
                return Category.PlayerAircraft;
            }
        }

        public MoveAircraftCommand (float vx, float vy, Aircraft v_aircraft) {
            _velocity = new Vector2f(vx, vy);
            aircraft = v_aircraft;
        }

        public override void Execute()
        {
            aircraft.Accelerate(_velocity);
        }
    }

//Then, There is the Player class that binds keys to actions, and actions to Commands.

public class Player
    {
public enum ActionMove {
            MoveLeft,
            MoveRight,
            MoveUp,
            MoveDown,
            ActionCount
        }

private IDictionary<Keyboard.Key, ActionMove> _keyBinding;
private IDictionary<ActionMove,ICommand> _actionBinding;

public Player()
        {
            _keyBinding = new Dictionary<Keyboard.Key, ActionMove>();
            _keyBinding.Add(Keyboard.Key.Left,ActionMove.MoveLeft);
            _keyBinding.Add(Keyboard.Key.Right,ActionMove.MoveRight);
            _keyBinding.Add(Keyboard.Key.Up,ActionMove.MoveUp);
            _keyBinding.Add(Keyboard.Key.Down,ActionMove.MoveDown);

/** Dunno how to bind the actions to commands without instantiating the command, Hard-Coding the parameters at start. Also Yet I don't have instantiated the aircraft object**/
float playerSpeed = 200f;
            _actionBinding.Add(ActionMove.MoveRight,new MoveAircraftCommand(+playerSpeed,0f,aircraft));
            _actionBinding.Add(ActionMove.MoveUp,new MoveAircraftCommand(0f,-playerSpeed, aircraft));
            _actionBinding.Add(ActionMove.MoveDown,new MoveAircraftCommand(0f,+playerSpeed,aircraft));
/** **/

/**This function pushes the Commands to a queue, in order to process them in order at once**/
public void HandleRealTimeInput(CommandQueue commands) {
            foreach (KeyValuePair<Keyboard.Key,ActionMove> entry in _keyBinding) {
                if (Keyboard.IsKeyPressed(entry.Key) && isRealTimeAction(entry.Value)) {
                    commands.Push(_factory.GetCommand(_keyBinding[entry.Key]));
                }
            }
        }

如何正确实现命令模式并在需要时正确实例化这些命令及其所有参数?

谢谢

4

1 回答 1

2

The key here is to realise that the "standard" way of presenting the Command pattern is a guideline, not a rule. C# has delegates and lambdas built-in, so there is no need to define ICommand etc. By getting rid of the command class, you can greatly simplify your Player class. The following is far from complete, but it hopefully shows what I means:

public class Player
{
    private Aircraft _aircraft;
    private float _playerSpeed = 200f;

    private readonly IDictionary<Keyboard.Key, ActionMove> _keyBinding =
        new Dictionary<Keyboard.Key, ActionMove>
        {
            { Keyboard.Key.Left,ActionMove.MoveLeft },
            { Keyboard.Key.Right,ActionMove.MoveRight },
            { Keyboard.Key.Up,ActionMove.MoveUp },
            { Keyboard.Key.Down,ActionMove.MoveDown }
        };

    private readonly IDictionary<ActionMove,ICommand> _actionBinding =
        new Dictionary<ActionMove,Action>
        {
            { ActionMove.MoveRight, () => MoveAircraft(_playerSpeed, 0f, _aircraft) },
            { ActionMove.MoveUp, () => MoveAircraft(0f, -_playerSpeed, _aircraft) },
            ...
        };

    public MoveAircraft(float vx, float vy, Aircraft v_aircraft) 
    {
        var velocity = new Vector2f(vx, vy);
        aircraft.Accelerate(_velocity);
    }

    ...
}

The key changes are to move the MoveAircraft method into the class and to then invoke it via closure lambdas in the _actionBinding dictionary. The Dictionary<ActionMove,Action> defines a dictionary with ActionMove as the key and a void method with no parameters as the value. Then the eg () => MoveAircraft(_playerSpeed, 0f, _aircraft) expression specifies an anonymous void method with no parameters which passes the current value of _playerSpeed and _aircraft to MoveAircraft.

To call these methods, you'd simply do eg _actionBinding[ActionMove.MoveRight]();

于 2013-10-29T12:23:56.683 回答