Reputation: 3111
I'm developing a videogame with .NET, yet I'm struggling on how to properly implement a queueing of commands and then execute them at once.
My videogame is simple, it's an aircraft that moves. My implementation of the Command Pattern is the following, then I implement the management of these commands on the Player class:
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]));
}
}
}
How can I implement properly the Command Pattern and instantiate these commands with all their parameters properly when they are required?
Thank you
Upvotes: 1
Views: 1262
Reputation: 43264
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]();
Upvotes: 2