USE COMPOSITION trust me.

29,331
0
Published 2024-07-14
I'm not saying you should never use Inheritance, just be careful and use Composition where necessary.

I mean State Machine when I say "Behaviour Tree".
These are actually two different terms I didn't realise.

Composition, Message Bus, State Machine Patterns.
Consider this a code overview of my current project, It's a break down of the Behavior Tree in my game.

Tired of spaghetti code? Join me as I refactor my 2-year-old game project using State Machine, Message Bus & Composition! This video is your guide to cleaner, more maintainable code.

#gamedev #refactoring #codingpatterns

All Comments (21)
  • @betatester03
    I'm not pointing this out to be pedantic, I promise, but what you've called a behavior tree is a hierarchical state machine. A behavior tree is a fundamentally very different approach to a logic control structure. The ONLY reason I point this out is because you have an audience of new developers and this could cause a lot of confusion over time as this misunderstanding propagates outward and with state machines and behavior trees both being fundamental approaches to game AI. Similar to the way Roblox kids call incremental/idle games "simulators", which are something else entirely, or how so many younger people say "literally" when they mean "figuratively". It creates a communication barrier between two generations that makes it more difficult for lessons to be passed down. New devs have to fight through a lot of confusion and endure a lot of frustration in their journey to becoming intermediate devs and these kinds of misunderstandings can make the harder parts of the learning curve even harder. The misnomer aside, you did an excellent job of communicating some useful concepts that are usually hard earned through experience and that's very difficult to do well. I sincerely hope you keep making content like this because you're an excellent communicator and we really don't have enough of those in the world.
  • @JustSteve85
    I really appreciate your explanation, Nesi. This is exactly why I dropped modding ArmA 3 and decided to just make my own damn game with Godot. Everything custom in A3's config scripting inherits from base classes at the top of the game asset inheritance tree, which results in having to redefine or add unnecessary lines to turn off certain features to each custom class, while the original class is still technically the overriding class, meaning to add new stuff you have to inherit from the base class all of its code, and if you want to change anything in your custom class at the bottom you usually have to hunt down and modify lines that pertain to what you want to change. In this example of a state machine, you can not only pick and choose what to inherit from but you're not stuck with pre-packaged components you might not even use. It's just smarter and more efficient to work with.
  • @erasercs
    Whatching that video feels like sitting at some jazz cafe in a noir detective movie
  • @Hysorix
    This coding style is one I've gradually moved towards over time. I didn't realize it until watching this. I always thought I was using inheritance, but it turns out I was employing a hybrid approach. I use this method when writing full-stack programs, and it has saved me so much trouble because it's easier to swap out modules and functions without breaking everything.
  • As an OOP pilled dev, the randomize function seems like an oversight in the first place. That sort of manipulation of variables upon creation of the object would be better done in the constructor method. You can have as many constructors with different method signatures as you want. Usually, for this sort of operation, you would have a blank constructor, say, titan(). This would initialize the object, and you could use it to set values randomly. Then you would have another constructor, say, titan(String name, int hp, int speed) and set the in scope variables using those values. There would be no need for creating subclasses.
  • @joemama-j8b
    The problem with inheritance is not necessarily itself, but the fact that people use it in places that they are not supposed to. It is a principle like any other in software development/computer science, and like any other principle, you need to think about if, and how you should implement it. Its common use probably stems from the fact that it gets taught the earliest in university/courses, and alternatives are taught either later or not at all. A different way to mitigate the problems that you referenced is the decorator pattern, but I would not recommend it, since it can get overly complicated quick, especially if there is a lot of divergence between the classes that exist. I am currently not involved in any projects that would need OOP principles, but if I find myself in a situation like that in the future, I will give it a try. I enjoyed the video, good job. The editing is really good.
  • @pokefreak2112
    Inheritance makes sense if you actually have shared logic, but it's easy to overuse because we have a tendency to want to create "logical" inheritance hierarchies. Having a ParticleEmitter base class the efficiently batch renders particles makes sense to me, the only reason you'd ever want to inherit from it is if you want to emit particles while for your titan example you could imagine a friendly titan that just caries you from place to place instead of attacking, in that case the inuitive thing to do is inherit from Titan, but the practical thing to do is inherit from something like Transporter or MovingPlatform
  • @DDeuces
    Good video structure. I can tell you that you're a lot easier to understand than other channels I've watched on YouTube and the reason why that is, is because when you are about to explain something you take short breaks before explaining everything and even when you do explain everything you also are taking short breaks in-between each chunk of information to let the viewers mind catch up with the information you're saying. I would say that in the future try to think of doing this more often because it really helps the viewer (me) and also, I can say that it makes you stand out as a content creator as well. Keep up the hard work Nesi. 🙏
  • @divy1211
    Some time ago, I read a reddit post which explained this extremely well (sadly I can't find the link, so I have to paraphrase) and it said that inheritance is basically composition but with extra features: 1. compose the supertype in the subtype, 2. automatically delegate properties and methods to the composed instance (syntax sugar: instead of having to do x.y.z you get to do x.z), 3. Make all subtype instances valid instances of the supertype from the perspective of the type checker. Interestingly, not only does inheritance often mislead new OOP programmers into the problem you described, the 3rd feature, formally called subtype polymorphism, makes a program harder to type check too in certain cases.
  • This is probably the best explanation and comparison of the two I've seen yet. All of only one thing is never good, but instead you mix and match them. And sometimes, as you show here, neither inheritance nor composition are the best choice and thus you go to a state machine, or in other cases some different construct, where that is most appropriate.
  • @Nik-dz1yc
    Very good video that got recommended to me exactly when I needed it since im about to run into the same issues
  • @nkacey2000
    love these educational / devlog style of a vids
  • @origanami
    I clicked the video and came straight here to say THIS MAN HAS SEEN THE LIGHT AND YOU WILL TOO
  • @felfar197
    such a great video! beautiful animations and pretty helpful
  • My biggest issue with inheritance is that it always makes sense when you're doing it. Like, if i start writing a program from literal scratch, and i make a bunch of simple struct-like things, then eventually I'll see what they all have in common and make a base class. Then you build more and you need base classes for your base classes. Then again, and again, and again, and by the end of it i have an impenetrable layer cake a mile high of class hierarchy. AND to top it all off, i usually don't remember which base classes did what specifically, which bloats debugging time
  • @erumabo
    Your video gave me some good ideas. For some reason I was trying to implement those behaviors/actions/states using events, and that let to some weird phenomenon like a character who can negate dead, dying before triggering the skill. Using states is much more sensible. Also, OOP is the paradigm, Inheritance is a property of OOP, and Composition is a pattern of OOP. You have some concepts mixed up there, but the general idea is well explained.
  • @VideoGamesAreBad
    This was great! Both from a video and information perspective.
  • @Rejuker
    The free fall mechanic is pretty cool, not sure I’ve seen that before. Seems like you have an interesting expandable core with this approach so best of luck building on that to a more fleshed out version.
  • @TuberTugger
    Composition isn't just having a list of things on an object. Composition is inheriting multiple interfaces instead of one baseClass. So instead of Enemy : Entity. You have Enemy : ICanJump, ICanWalk, IDamagable, IHasInventory And each of those interfaces handles the default logic. Then later when you want to damage an object, you don't check if it's an Enemy, you check if it's an IsDamagable and call the object's TakeDamage method. Now anything can be IDamagable. Destructible environments, the ground, the player. It doesn't matter what, because all it's promising is that it takes damage. The code's intention is super transparent and easy to maintain. When people say use Composition over Inheritance, they mean direct inheritance. Not inheriting in general. Composition is still a type of inheritance. You just inherit components instead of one root class. If you're not using interfaces, you're not really using composition. Just Lists. Every baseClass you consider creating, instead break it down into many interfaces each responsible for a singular thought.