The Command Pattern: Coding Undo/Redo | Game Engine Concepts #3

Поділитися
Вставка
  • Опубліковано 2 лип 2024
  • Join the Discord: / discord
    In this episode I explain what the Command Pattern is and how you can use it. Before I explain what the Command Pattern is I explain what a design pattern is and reference the Gang of Four's original book, Design Patterns. After that, I explain that the Command Pattern is simply a wrapper around executing commands. I then illustrate how this can be used in conjunction with a Command History object to create an undo/redo system, record gameplay sessions, and create instant replays. I describe in detail how you might code this, then I show an actual example that I use in my code to implement undo/redo system.
    The Command Pattern: gameprogrammingpatterns.com/c...
    Gang of Four Book: www.amazon.com/Design-Pattern...
    Command Code Example: github.com/ambrosiogabe/Cocoa...
    Command History Code Example: github.com/ambrosiogabe/Cocoa...
    0:00 Intro
    0:31 What is a Programming Pattern?
    1:39 What is the Command Pattern?
    13:39 What does the Code Look Like?
    ---------------------------------------------------------------------
    Website: ambrosiogabe.github.io/
    Github: github.com/ambrosiogabe
    Here are some books I recommend if you want to learn about game engine development more thoroughly. I do not profit off any of these sales, these are just some books that have helped me out :)
    My Recommended Game Engine Books:
    Game Engine Architecture: www.gameenginebook.com/
    Game Physics Cookbook (Read this before the next physics book): www.amazon.com/Game-Physics-C...
    Game Physics (Ian Millington): www.amazon.com/Game-Physics-E...
    Game Programming Patterns (Free): gameprogrammingpatterns.com/
    My Recommended Beginning Game Programming Books:
    JavaScript Game Design: www.apress.com/gp/book/978143...
    My Recommended Java Books:
    Data Structures/Algorithms: www.amazon.com/Data-Structure...
    LWJGL (Free, but I haven't read this thoroughly): lwjglgamedev.gitbooks.io/3d-g...
    Outro Music: www.bensound.com/royalty-free...

КОМЕНТАРІ • 17

  • @inxomnyaa
    @inxomnyaa 2 роки тому

    Underrated channel & video, learned a lot

  • @20gg99
    @20gg99 3 роки тому +3

    This is so useful! tysm!!!!!

    • @GamesWithGabe
      @GamesWithGabe  3 роки тому +1

      No problem @20GG. I'm glad it could help you out :)

  • @vfrz3862
    @vfrz3862 3 роки тому

    Good quality content, thank you for your work :)

  • @vixellar7933
    @vixellar7933 3 роки тому +4

    Loved seeing this in cpp!!

    • @voxelrifts
      @voxelrifts 3 роки тому +3

      Yeah me too

    • @GamesWithGabe
      @GamesWithGabe  3 роки тому +2

      Cool thanks @Vixellar and @Nikhil! I want to eventually transition all the tutorials to cpp so hopefully other people enjoy it as well haha

    • @vixellar7933
      @vixellar7933 3 роки тому

      @@GamesWithGabe Hey, i am curious, did you ever try godot?

    • @GamesWithGabe
      @GamesWithGabe  3 роки тому +2

      @@vixellar7933 I hesitate to say I tried it, because it's more like I "tried" it haha. I downloaded it, and loved the fact that it was a single small exe, and then I messed around with all the demos they had. Then I went to code something, and realized it only let you code in GDScript, and I really didn't feel like learning a new language at the time, so I just sort of put it off indefinitely. I would love to take another look at it though :)

    • @vixellar7933
      @vixellar7933 3 роки тому

      @@GamesWithGabe I am pretty sure you can use C# for scripting and its great for engine developers because the source code is available on github and it is very easy to understand for everyone who knows a little bit of c++.

  • @andriibilych4983
    @andriibilych4983 3 роки тому +1

    That is just the best tutorial on command pattern, thank you. Also, the repository with your engine no longer contains code for this implementation.

    • @GamesWithGabe
      @GamesWithGabe  3 роки тому

      Hey Andrii thanks for the comment! I recently changed some naming and stuff and broke the links, so I've patched those and they should work again. Thanks for bringing it to my attention :)

  • @KennyTutorials
    @KennyTutorials 2 роки тому

    When i first try to implement undo/redo system in my game engine, i do that way, basically create two lists for undo and redo commands. And from perspective undo for example i add function to add/push undo command in my list, with struct that holds recovery data, its can be anything, TransformComponent or maybe single variable. And when i need undo this command i simple call process/execute function, that checks witch command need to be process and get the all recovery data to that specific command and execute it, and after this just decrement pointer/remove command from undo list and push it to redo list, for that case if we need to redo it. In sinple words my commands just changes between lists. And that works perfectly even with ECS that hold every time different ids, cuz im using UUIDs for my game objects.

  • @ianlaird8519
    @ianlaird8519 2 роки тому +1

    Hey Gabe, great video. Might I suggest that rather than delete every command after you add a new one, you instead keep a second pointer for the end of the list? Also, you don't appear to be handling the case when you hit the limit of your undo buffer which leaves you in danger of an overflow issue. If you continue to use an array, you would need to move every element back by one. I potentially better option would be to use a linked list as your undo buffer. In that way, you can just cut the head and append it to the tail. Similarly, you would not need to loop ahead when adding a new command, simply replace the next pointer to the new command and dereference the old tail.

    • @GamesWithGabe
      @GamesWithGabe  2 роки тому +1

      Hey Ian, thanks for the comment! In the code it doesn't look like I'm deleting every command every time I add a new one. I'm just checking to see if there are any commands that need to be deleted then deleting those. For example, say you have:
      AddCommand->AddCommand->AddCommand->Undo->Undo
      What happens is that we're back at the first command, but I save the next commands just in case a user does Redo. But if they do something different and a new command is added, then I delete the two excess commands since the history is now being rewritten.
      You're right in saying that I have an overflow issue though, and I could avoid that by moving everything back by one like you said, or I could use a std::vector haha. I don't plan on updating any of this code anytime soon, I've moved onto other projects for now, but you're suggestions sound like they should work :)