How to write more flexible game code

แชร์
ฝัง
  • เผยแพร่เมื่อ 5 มิ.ย. 2024
  • Writing code that is flexible and receptive to change can be a challenge, but these tips can help you improve your codebase, no matter the size of your game.
    The application hierarchy: 00:00
    Managing data and logic: 02:32
    Use components: 04:01
    Dependency injection: 04:38
    On design patterns: 05:59
    The final tip: 07:37
    Text writeup: shaggydev.com/2022/09/13/flex...

ความคิดเห็น • 56

  • @diegonorambuena4194
    @diegonorambuena4194 10 หลายเดือนก่อน +7

    My man, the last tip part hit me hard I have spent a lot of time trying to solve the perfect solution, but whenever I start coding, I freeze after a couple of lines and go back to the drawing board. It's endless rn. Thanks for your words :D

  • @krysidian
    @krysidian ปีที่แล้ว +15

    You don't know how glad I am that I found your channels. It's a godsend of useful and concise information and I really hope more people will find it.

  • @learningforever957
    @learningforever957 ปีที่แล้ว +8

    This video is a gem, is CRUCIALfor anyone who is working on any relatively "big" game. For me is the point where I struggle the most, to keep things organized in a way that Im able to look at past code and know exactly where I have to look if I want to check something.
    I enjoy your content a lot! Keep it up!

  • @pythonxz
    @pythonxz ปีที่แล้ว +1

    I love that you go over "Tell, Don't Ask.". Everyone should read The Pragmatic Programmer.

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว +1

      They really should! I also kind of mixed it with Martin Fowler's take on the topic for this video since he brings up some points, like co-locating data is good, but attempting to remove every getter function in your codebase is probably overkill

  • @circusmonkey28
    @circusmonkey28 4 หลายเดือนก่อน

    The Final Tip is the only tip that made sense to me which means it is too early for me to watch this video xD

  • @pedroehler
    @pedroehler 10 หลายเดือนก่อน

    The most important thing "it takes time"!

  • @learningforever957
    @learningforever957 ปีที่แล้ว +9

    3:45 you nailed it! Is what happens to me EVERYTIME; I know I have to keep things separated, but when I have to add a single thing is like "huh, but creating just a class/script for this.... I can handle this here". And thats how problems start D:

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว +2

      For sure! I've finally gotten myself in the habit of mostly just doing it right the first time and haven't regretted it.

  • @pabletoday9782
    @pabletoday9782 5 หลายเดือนก่อน

    These videos of yours are masterpieces

  • @kemeros3562
    @kemeros3562 ปีที่แล้ว +2

    Incredibly well made, useful and concise video that hit close to home. The final tip? Oh man, I hear you and i will need to play it in repeat in my head again and again because... i've been learning theory for a year and haven't done anything useful and mostly forget everything i learn because i don't actually use it.

  • @NicholasGrigoriev
    @NicholasGrigoriev ปีที่แล้ว +1

    I don't know why, but it's your code example for Dependency Injection, that finally made a click in my head, and I finally understood it. (or at least I hope so). Thank you Sir!

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว +1

      Glad it finally made sense! I always liked the saying that dependency injection is "a 25-dollar term for a 5-cent concept". Not too bad once you know it, but the name for it can make it sound like it's something more complex than it really is.

  • @bugimplementer
    @bugimplementer ปีที่แล้ว +1

    The final tip is so true!! Thanks for the reminder. Great video.

  • @realMenta
    @realMenta ปีที่แล้ว +1

    Thank you, really nice tips and easy to understand.

  • @panampace
    @panampace ปีที่แล้ว

    I put off making a state machine for my character controller for such a long time because I thought it was more complicated than it was worth - BOY was I wrong. Setting up a state machine ought to come first in a character controller - makes it SO much easier to add functionality without screwing up another part.

  • @queenofsquiggles
    @queenofsquiggles ปีที่แล้ว +9

    I absolutely love this! The details and the way you explained it was incredibly useful! I'm really interested in good design practices, and this will definitely be a video I reference!

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว +2

      Thank you! Glad you enjoyed it!

  • @juanloutech2864
    @juanloutech2864 ปีที่แล้ว +1

    Very well explained basics of programming (not just game programming)

  • @psyboyo
    @psyboyo ปีที่แล้ว

    Super useful video!

  • @mbg4681
    @mbg4681 10 หลายเดือนก่อน

    Well said, all of it but especially that final tip. Reasonable, approachable, fun, candid, considerate advice! You sound way too young and spry to be this wise already.

  • @spudrat
    @spudrat ปีที่แล้ว

    wowwww this channel is so underrated, just found you, really sick!

  • @Rin-qj7zt
    @Rin-qj7zt ปีที่แล้ว

    you should mention that assumption of structure is actually fine in cases where the architecture is specifically designed for that to be the case. for example, every major location could be a sibling of the same parent node by designe, so they can be switched in and out of memory like legos. assuming that the parent has the same resources it always does is fine, because we never intend on putting a major location anywhere else in the tree.
    it's okay for some structure to be designed not to change, and this grants some level of flexibility when coding because it allows an assumption to be made. it depends on what advantages you gain compared to the advantages of staying dynamic.

  • @ThunderThoster
    @ThunderThoster ปีที่แล้ว

    Love it! Many thanks for your enlightenment _/\_

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว

      Thanks! Glad you liked it!

  • @RobertoMaurizzi
    @RobertoMaurizzi ปีที่แล้ว +1

    Very good video (but yeah, I found your channel yesterday and all your videos are REALLY good :D).
    How would you manage things like driving a camera for a (sub)viewport?
    Situation: I'm scoping the effort required to make an old skool dungeon crawler game for a friend whose programmer ran away. I have a UI composed of different panels (main 3D window, health, inventory, character status and equipment, mini map, commands, direction, text area because the game has a visual novel component, time of the day, ...). The game will probably have multiple levels and I can't decide if I should put all the levels and the player under the viewport, or try to drive a remote camera from the player using a RemoteTransform3D or some bit of code that fetches the remote camera and drives it...
    Also: where to put the levels? 😵‍💫

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว +2

      Glad you're liking the content!!
      Hmm, that's a tricky one. For something like a dungeon crawler (which I'm also assuming will be entirely / largely UI driven and the camera is really just a lens into the level's content), I would probably have level contents within that viewport and pass camera and other commands to it from the UI. That would also let you swap levels on the fly while still keeping the main UI on screen if that's something you want to do.
      But it's really probably something to just try out and see what happens. Pick whichever one feels "most" right to you and try to minimally implement it and see how good / bad of an idea it was after all! That's what I always do when I can't decide.

    • @RobertoMaurizzi
      @RobertoMaurizzi ปีที่แล้ว +1

      @@TheShaggyDev Thanks! I'll try to experiment a bit 😄

  • @krzyszt0fus
    @krzyszt0fus ปีที่แล้ว

    Cool

  • @badunius_code
    @badunius_code ปีที่แล้ว

    5:40 that's where I'd argue in favor of using signals.
    Because what is presented relies on having a very specific owner.
    Signals otoh allows owner to listen to the ones that are relevant and interpret them in its own way

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว

      The tricky thing is that signals are for fire-and-forget purposes where you don't care if anyone is listening, but for something like a state machine you probably do want some amount of ownership over the object as you may need to call specific functions on it or receive values from it in order to operate properly.

    • @badunius_code
      @badunius_code ปีที่แล้ว

      @@TheShaggyDev true, but imagine the object under control is currently unable to move, say, left. Or the object is under "disorientation" effect or "paralysis". Either controller should be aware of all of this possible condition, or it might make the object behave against the rules and expectations.

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว

      @@badunius_code In those examples, I would let the states know about such conditions. May be a matter of taste, but I want my states to be the authority on things in their domain.

    • @badunius_code
      @badunius_code ปีที่แล้ว

      @@TheShaggyDev true, but here's another one: you can easily switch from a FSM controller to a Player Input controller without duplicating controller's owner related code as long as they implement the same interface

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว

      ​@@badunius_code These are all fair points but that's honestly just not something I would worry about. I think you run the risk of making more work for yourself by crippling the state machine and adding more layers of abstraction for something that probably won't be required most of the time.

  • @SLSat
    @SLSat ปีที่แล้ว

    Loved the video, one question though
    0:42 when you say "player code access" do you mean the player's children can have access to the parent's code, or the other way around (the player being able to access its children code?)

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว

      It's from the perspective of the Player object. So the Player code should generally only access the Sprite out of the items on that list, but not the Level or Bitmap Importer.

  • @snowman7514
    @snowman7514 ปีที่แล้ว

    in godot you can use export player:NodePath or in gd4 @ export player:KinematicBody2D, idk if this is the dependency injection you are talking about

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว

      Yep, that's definitely a form of dependency injection! In addition to exports, FuncRefs (or Callables in Godot 4) are another Godot-specific tool for dependency injection, though oftentimes just passing some data from the parent to the child can also serve this goal

  • @Portponky
    @Portponky ปีที่แล้ว +1

    What games are at 0:22 and 1:47? Also, really good video!

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว +1

      Thank you! First game is Tile Cities and the second is Mini Motorways - both are fun, puzzlely experiences

  • @JacobKinsley
    @JacobKinsley 10 หลายเดือนก่อน

    Does stuff like "@export var entity: Rigidbody" count as dependency injection?

    • @TheShaggyDev
      @TheShaggyDev  10 หลายเดือนก่อน

      I would say it does, as it lets you decouple your code from a specific instance of your dependency.

    • @JacobKinsley
      @JacobKinsley 10 หลายเดือนก่อน

      @@TheShaggyDev 2 days later and I forgot what dependency injection means, but I have used @export var in a practice game I made

  • @UhGoomba
    @UhGoomba 5 หลายเดือนก่อน

    How do you use components in practice? If I need to hit a character (from a bullet, for example), how should I get the character's health component? Do I check if the character has a health component child, wouldn't that violate the first principle? Or should I make an additional take damage function in the character that sends it to the health component? That seems over complicated. I would love to see how you have done this

    • @TheShaggyDev
      @TheShaggyDev  5 หลายเดือนก่อน

      There's a little bit of a variance to how I tend to use components, depending on if the component is for "internal" use or not. For an internal component, like an AI controller that shouldn't be accessed outside of the character, just exporting a node of the correct type can suffice to let me modify behavior as needed and keep code more compartmentalized.
      For something external, like with your example of a bullet hitting a character to deduct health, you could flip the approach and have a component in the character that can react to bullets rather than having the bullet check the character for specific functionality. Oftentimes, this may be what you want anyways as you may have instances where you need to modify whether or not damage is taken or how much is taken based on other properties of the character (defensive powerups, i-frames, etc). Of course, you now have to figure out how to let the bullet know it has hit something but it's probably simpler in its structure than a character and, depending on the game, even just a simple type check may be enough to know what you're working with.
      If you do find yourself needing to check for the presence of a component on another object, you may want to expose a pass-through function on the top-level node and check for that, since checking for component names or something like that could be finicky. Not my first choice of approach, but sometimes it can make sense.

  • @McHumaty
    @McHumaty 7 หลายเดือนก่อน

    Let's suppose I have a skill tree and they are released as the player progresses and chooses the path they want to follow when creating their character (build). Can each of these skills be a component? Can I put them in a database and add them to the player? I say this because there may be enemies in the game that have common skills that the player can choose.

    • @TheShaggyDev
      @TheShaggyDev  7 หลายเดือนก่อน

      Hmm, in cases like this the concept of separating data from presentation comes to mind, so my immediate thought is to store as much data about the actions externally to any one character as possible, since it needs to be shared, and then think of a way to let the player and enemies select and activate their actions. Could be a component that makes an action available, could be just a data structure that pulls in the data for their available actions to activate it, etc. Would need to think on it.

    • @McHumaty
      @McHumaty 7 หลายเดือนก่อน

      @@TheShaggyDev ...it gives me a good headache, but I'm still lost on the solution. rs

  • @Niohimself
    @Niohimself ปีที่แล้ว

    If two objects in a hierarchy can depend on their parent, but not on each other or their children - does that mean we are not allowed to take things that depend on each other, and make them into objects? I prefer to have lots of small, manageable objects rather than one big god object that knows about everything, but two cooperating parts of the same whole necessarily know about each other, which breaks encapsulation.

    • @TheShaggyDev
      @TheShaggyDev  ปีที่แล้ว

      An object can depend on its direct children, you just don't want to dig multiple levels down. And you can absolutely have two cooperating objects in one larger object, and certainly don't need a god object for everything, as that's also not great design imo. What you're describing is essentially what I'm talking about with making small, resusable components.
      The question to ask with that is how will the two side-by-side objects know about each other? A hardcoded path to the other isn't ideal, but the parent object that owns both of them CAN know they exist and can coordinate the appropriate dependency injection.
      Of course, no one's going to strike you down for telling the two siblings about each other directly, you'll just make it harder on yourself if you need to later break them up or reorganize things.

  • @trainzmarcel2074
    @trainzmarcel2074 10 หลายเดือนก่อน +1

    all my homies hate object oriented programming