4
\$\begingroup\$

I am trying to implement a replay/undo system for a turn based strategy game I am currently working on. A sample move could go as follows:

1. A players select a pawn and gives it an attack command.
2. The attacked unit dies.
3. The dead unit is removed from the map and the controlling player.
4. The controlling player takes some damage.
5. The attacking unit gains a survivor strength power up for the remainder of the game.

The rest of the game plays similarly to a Fire Emblem type game, but the point is a single command like attacking yields multiple effects to various game entities.

Eventually I want to make the game networked, but for now everything is local play.

After doing some research, I came across several resources discussing the command pattern and feel it is the solution to my replay and undo system. I ended up using an interface similar to https://gamedev.stackexchange.com/a/37684/41499 but I am confused on some of the implementation details.

  1. Do I make commands to update each component of my game separately (i.e. AtkUnitCmd, RmvUnitFromMapCmd, RmvUnitFromPlayerCmd, InflictPlayerDmgCmd, etc.) or should my AtkUnitCmd handle all updates of entity state internally?

  2. How do I handle dead units? I can store a reference to the dead unit, but this would likely need to change when networking is implemented as the object itself would not be transmitted over the network correct?

  3. Each command has some animation involved for example when a unit moves an animation needs to play. I may choose to skip the animations while other times leave them on. Do I bake the animations into the commands themselves or should I separate the animation code and call it somehow when the command is played?

Any thoughts?

\$\endgroup\$
3
\$\begingroup\$

Broadly speaking there are three approaches to rewinding game state (with various flavours in between): reversing/undoing actions, replaying actions from an earlier, fixed state, or storing all states and just picking the right one on demand. The first is more flexible but in the worst case you need to double your work, creating reverse versions of all your commands, plus you need to keep around "dead" state so you can undo into them (reviving dead entities, keeping all their properties around so you can resurrect them, v.s never really removing them, just toggling an "alive" flag). The latter two are much easier, but if you have complex, real-time, long-running states it can be very expensive to replay or store everything.

Judging from your genre though (TBS), I'm inclined to guess that the replaying or even full storage approach will be fine. After all, chess is a turn-based game but entire games can be stored in a few dozen characters, describing the moves.

Given that the replay and store approaches don't require reverse commands, your first two questions are irrelevant for them, which may save you a ton of trouble. Note that you should consider hybrid approaches too, which offer excellent compromises. Braid, IIRC, used a hybrid of stored states every short while plus replaying commands forward, and could store almost an hour of real-time gameplay.

  1. Do I make commands to update each component of my game separately (i.e. AtkUnitCmd, RmvUnitFromMapCmd, RmvUnitFromPlayerCmd, InflictPlayerDmgCmd, etc.) or should my AtkUnitCmd handle all updates of entity state internally?

Whatever's most convenient, but you'll find that more atomic commands are better simply because of code reuse - surely AtkUnitCmd isn't the only command capable of inflicting damage?

  1. How do I handle dead units? I can store a reference to the dead unit, but this would likely need to change when networking is implemented as the object itself would not be transmitted over the network correct?

It would be easiest to keep around the dead units and flip an alive flag to revive them. But you raise an interesting question: how to maintain references over the network? Most network-ready game engines should have this solved for you and offer some kind of "universal entity reference", but if you are rolling your own, my suggestion is to use a portable, container-agnostic unique identifier. Yes, if you assume your clients are running the same binary then you can get away with more specific references like memory offsets or indices, but using unique identifiers will save you trouble down the road, when switching containers or dealing with non-determinism. For most people, all it takes is one hours-long debugging session over invalid references for this investment to pay off.

  1. Each command has some animation involved for example when a unit moves an animation needs to play. I may choose to skip the animations while other times leave them on. Do I bake the animations into the commands themselves or should I separate the animation code and call it somehow when the command is played?

Separating rendering from game logic is the best thing to do. Consider MVC and related patterns.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ This definitely gives me some things to think about. I had thought about doing the hybrid approach and saving state at intervals, but thought it might consume too much space by having to save copies of all in-game units, map conditions, player stats, etc. I will play with this to see what ends up being best for the project. Thanks again for the answer. \$\endgroup\$ – MrJman006 Jun 11 '15 at 2:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.