Skip to content

Commit 5b6b6b4

Browse files
committed
Added command pattern
1 parent f88f729 commit 5b6b6b4

File tree

3 files changed

+51
-37
lines changed

3 files changed

+51
-37
lines changed

Assets/Patterns/1. Command/Rebind keys/Scripts/GameController.cs

+39-33
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,30 @@ public class GameController : MonoBehaviour
1010
{
1111
public MoveObject objectThatMoves;
1212

13-
//The keys we have
13+
//The keys we have that are also connected to commands
1414
private Command buttonW;
1515
private Command buttonA;
1616
private Command buttonS;
1717
private Command buttonD;
1818

1919
//Store the commands here to make undo, redo, replay easier
20-
private List<Command> oldCommands = new List<Command>();
20+
//The book is using one list and an index
21+
//private List<Command> oldCommands = new List<Command>();
2122
//Start at -1 because in the beginning we haven't added any commands
22-
private int currentCommandIndex = -1;
23+
//private int currentCommandIndex = -1;
24+
//But I think its easier to use two Stacks
25+
//When replay, we convert the undo stack to an array
26+
private Stack<Command> undoCommands = new Stack<Command>();
27+
private Stack<Command> redoCommands = new Stack<Command>();
2328

2429
private bool isReplaying = false;
2530

2631
//To make replay work we need to know where the object started
2732
private Vector3 startPos;
2833

34+
//The time between each command execution when we replay so we can see what's going on
35+
private const float REPLAY_PAUSE_TIMER = 0.5f;
36+
2937

3038

3139
void Start()
@@ -43,6 +51,7 @@ void Start()
4351

4452
void Update()
4553
{
54+
//We can check for input while we are replaying
4655
if (isReplaying)
4756
{
4857
return;
@@ -54,50 +63,52 @@ void Update()
5463
//You could solve this by saving the Time.deltaTime somewhere
5564
if (Input.GetKeyDown(KeyCode.W))
5665
{
57-
ExecuteCommand(buttonW);
66+
ExecuteNewCommand(buttonW);
5867
}
5968
else if (Input.GetKeyDown(KeyCode.A))
6069
{
61-
ExecuteCommand(buttonA);
70+
ExecuteNewCommand(buttonA);
6271
}
6372
else if (Input.GetKeyDown(KeyCode.S))
6473
{
65-
ExecuteCommand(buttonS);
74+
ExecuteNewCommand(buttonS);
6675
}
6776
else if (Input.GetKeyDown(KeyCode.D))
6877
{
69-
ExecuteCommand(buttonD);
78+
ExecuteNewCommand(buttonD);
7079
}
7180
//Undo with u (ctrl + z is sometimes interfering with the editor's undo system)
7281
else if (Input.GetKeyDown(KeyCode.U))
7382
{
74-
if (currentCommandIndex == -1)
83+
if (undoCommands.Count == 0)
7584
{
7685
Debug.Log("Can't undo because we are back where we started");
7786
}
7887
else
7988
{
80-
Command lastCommand = oldCommands[currentCommandIndex];
89+
Command lastCommand = undoCommands.Pop();
8190

8291
lastCommand.Undo();
8392

84-
currentCommandIndex -= 1;
93+
//Add this to redo if we want to redo the undo
94+
redoCommands.Push(lastCommand);
8595
}
8696
}
8797
//Redo with r
8898
else if (Input.GetKeyDown(KeyCode.R))
8999
{
90-
if (currentCommandIndex == oldCommands.Count - 1)
100+
if (redoCommands.Count == 0)
91101
{
92102
Debug.Log("Can't redo because we are at the end");
93103
}
94104
else
95105
{
96-
Command nextCommand = oldCommands[currentCommandIndex + 1];
106+
Command nextCommand = redoCommands.Pop();
97107

98108
nextCommand.Execute();
99109

100-
currentCommandIndex += 1;
110+
//Add to undo if we want to undo the redo
111+
undoCommands.Push(nextCommand);
101112
}
102113
}
103114

@@ -124,45 +135,40 @@ void Update()
124135
//Replay
125136
private IEnumerator Replay()
126137
{
138+
//Move the object back to where it started
127139
objectThatMoves.transform.position = startPos;
128-
129-
for (int i = 0; i < oldCommands.Count; i++)
140+
141+
//Pause so we can see that it has started at the start position
142+
yield return new WaitForSeconds(REPLAY_PAUSE_TIMER);
143+
144+
//Convert the undo stack to an array
145+
Command[] oldCommands = undoCommands.ToArray();
146+
147+
//This array is inverted so we iterate from the back
148+
for (int i = oldCommands.Length - 1; i >= 0; i--)
130149
{
131150
Command currentCommand = oldCommands[i];
132151

133152
currentCommand.Execute();
134153

135-
yield return new WaitForSeconds(0.5f);
154+
yield return new WaitForSeconds(REPLAY_PAUSE_TIMER);
136155
}
137156

138-
//Is used if oldCommands length is 0, then we never reach isReplaying = false;
139-
yield return new WaitForSeconds(0.01f);
140-
141157
isReplaying = false;
142-
143-
//Now we are always at the end of the list, so make sure the currentCommandIndex is there as well
144-
currentCommandIndex = oldCommands.Count - 1;
145158
}
146159

147160

148161

149162
//Will execute the command and do stuff to the list to make the replay, undo, redo system work
150-
private void ExecuteCommand(Command commandButton)
163+
private void ExecuteNewCommand(Command commandButton)
151164
{
152165
commandButton.Execute();
153166

154-
//First we have to remove all commands that may be in the list after the current command
155-
if (oldCommands.Count > 0)
156-
{
157-
//We earlier stored them from redo, but that shouldn't be possible now because we added a new command and should just be able to undo
158-
oldCommands.RemoveRange(currentCommandIndex + 1, oldCommands.Count - (currentCommandIndex + 1));
159-
}
160-
161167
//Add the new command to the last position in the list
162-
oldCommands.Add(commandButton);
168+
undoCommands.Push(commandButton);
163169

164-
//The current command index is now the last position in the list
165-
currentCommandIndex = oldCommands.Count - 1;
170+
//Remove all redo commands because redo is not defined when we have add a new command
171+
redoCommands.Clear();
166172
}
167173

168174

Assets/Patterns/1. Command/Rebind keys/Scripts/MoveObject.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace CommandPattern.RebindKeys
88
public class MoveObject : MonoBehaviour
99
{
1010
//Speed of the object
11-
private const float DISTANCE = 1f;
11+
private const float MOVE_STEP_DISTANCE = 1f;
1212

1313

1414
//These methods will be executed by their own command
@@ -36,7 +36,7 @@ public void TurnRight()
3636
//Help method to make it more general
3737
private void Move(Vector3 dir)
3838
{
39-
transform.Translate(dir * DISTANCE);
39+
transform.Translate(dir * MOVE_STEP_DISTANCE);
4040
}
4141
}
4242
}

README.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ In you game you have many commands, such as play sound, throw cake, etc. It can
4343

4444
**How to implement?**
4545

46-
* You have a base class called Command which has a method that a child can implement called Execute. In each child class, you put in the Execute method what will actually happen when you run (execute) that Command.
46+
* You have a base class called Command which has a method that a child can implement called Execute. In each child class, you put in the Execute method what will actually happen when you run (execute) that command.
4747

4848
**When is it useful?**
4949

5050
* To make it easier to rebind keys. Example of this is available in the code section.
5151

52-
* To make it easier to make a replay system. When you play the game, you store in some list which button you pressed each update. When you want to replay what has happened, you just iterate through the list while running the game. Example of this is available in the code section.
52+
* To make it easier to make a replay system. When you play the game, you store in some data strcuture which button you pressed each update. When you want to replay what has happened, you just iterate through each command while running the game. Example of this is available in the code section.
5353

5454
* To make it easier to make an undo and redo system. Is similar to the replay system, but in each command you also have a method called Undo() where you do the opposite of what the command is doing. Example of this is available in the code section.
5555

@@ -132,6 +132,8 @@ In your game you have a game object. Now you want to duplicate that object to cr
132132

133133
## 5. Singleton
134134

135+
-
136+
135137

136138

137139
## 6. State
@@ -220,6 +222,9 @@ The update method will process one frame of behavior. Each object that needs it
220222

221223
## 10. Bytecode
222224

225+
-
226+
227+
223228

224229
## 11. Subclass Sandbox
225230

@@ -325,6 +330,9 @@ When making your game you use many standardized methods to for example generate
325330

326331
## 16. Data Locality
327332

333+
-
334+
335+
328336

329337
## 17. Dirty Flag
330338

0 commit comments

Comments
 (0)