【问题标题】:How to implement Command Pattern and Key Binding?如何实现命令模式和键绑定?
【发布时间】:2013-10-29 11:41:11
【问题描述】:

我正在使用 .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]));
                }
            }
        }

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

谢谢

【问题讨论】:

    标签: c# design-patterns command instantiation


    【解决方案1】:

    这里的关键是要认识到呈现命令模式的“标准”方式是一种指导方针,而不是规则。 C# 内置了委托和 lambda,因此无需定义 ICommand 等。通过摆脱命令类,您可以大大简化 Player 类。以下内容远未完成,但希望能说明我的意思:

    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);
        }
    
        ...
    }
    

    关键更改是将MoveAircraft 方法移到类中,然后通过_actionBinding 字典中的闭包lambda 调用它。 Dictionary&lt;ActionMove,Action&gt; 定义了一个以 ActionMove 为键的字典和一个不带参数的 void 方法作为值。然后例如() =&gt; MoveAircraft(_playerSpeed, 0f, _aircraft) 表达式指定了一个不带参数的匿名void 方法,它将_playerSpeed_aircraft 的当前值传递给MoveAircraft

    要调用这些方法,您只需执行例如_actionBinding[ActionMove.MoveRight]();

    【讨论】:

    • 感谢您的回答。但是,如果我希望将来添加更多命令怎么办?会不会让 Player 类太大而与其他组件耦合?
    • @DavidJiménezMartínez 最简单的解决方案是通过其构造函数为玩家类提供Dictionary&lt;ActionMove,ICommand&gt; 的实例。这样,命令和绑定可以在其他地方设置,而玩家类不需要耦合到它们。这也将使单元测试更容易,因为您可以提供模拟命令。
    • @DavidJiménezMartínez 话虽如此,您将失去关闭行为的好处(即,将飞机的当前值和玩家位置传递给命令。因此,它们例如可以通过以下方式访问公共吸气剂。
    • 好的,我会尝试吸收您的答案,并在家里与代表一起测试。非常感谢:)
    猜你喜欢
    • 2011-01-13
    • 2013-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多