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
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!
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
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!
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.
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.
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!
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:
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.
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.
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.
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? 😵💫
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.
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
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.
@@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.
@@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.
@@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
@@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.
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
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.
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?)
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.
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
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.
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.
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.
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.
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
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.
Thank you! Glad you find it useful!
4:32 - or use ECS ;D
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!
I love that you go over "Tell, Don't Ask.". Everyone should read The Pragmatic Programmer.
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
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!
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.
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.
The final tip is so true!! Thanks for the reminder. Great video.
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!
Thank you! Glad you enjoyed it!
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:
For sure! I've finally gotten myself in the habit of mostly just doing it right the first time and haven't regretted it.
The most important thing "it takes time"!
These videos of yours are masterpieces
Very well explained basics of programming (not just game programming)
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.
wowwww this channel is so underrated, just found you, really sick!
Thank you!
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.
Thank you, really nice tips and easy to understand.
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
i will watch all your videos
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.
Super useful video!
Love it! Many thanks for your enlightenment _/\_
Thanks! Glad you liked it!
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? 😵💫
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.
@@TheShaggyDev Thanks! I'll try to experiment a bit 😄
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
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.
@@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.
@@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.
@@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
@@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.
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
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.
Does stuff like "@export var entity: Rigidbody" count as dependency injection?
I would say it does, as it lets you decouple your code from a specific instance of your dependency.
@@TheShaggyDev 2 days later and I forgot what dependency injection means, but I have used @export var in a practice game I made
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?)
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.
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
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
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.
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.
@@TheShaggyDev ...it gives me a good headache, but I'm still lost on the solution. rs
What games are at 0:22 and 1:47? Also, really good video!
Thank you! First game is Tile Cities and the second is Mini Motorways - both are fun, puzzlely experiences
Cool
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.
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.
all my homies hate object oriented programming