Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

My Composite Pattern Design - C&C + Help

Discussion in 'Scripting' started by Nigey, Nov 23, 2015.

  1. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Hi Guys,

    I'm trying to create some systems and have them relating, using the composite design pattern. I want to keep a decent level of abstraction so my classes don't become rigid and brittle, and I can extend on this project. However I'm struggling with dependencies, and trying to make objects not aware of each other too much. I want to throw you guys some thoughts on how I am setting it up so far and hopefully get some feedback/help.

    At the beginning of the main game scene, a weapon is created. It has two classes Weapon(containing an inherited firing state machine from abstract class WeaponFramework), and WeaponConfig(containing weapon data and bullet types ect, from abstract WeaponConfigFramework). They have a [RequiresComponent] relationship and Weapon always knows it's config data. There is also a singleton, called WeaponManager. This isn't attached to or part of the weapon at all. It's passed the reference of the weapon at runtime, and calls the Weapon class's inherited methods. The WeaponManager is used at runtime by the UI, to do things like aim, ready, fire, and disable the weapon. Any unique UI parts of a weapon type are added to the UI directly at runtime by the weapon's instantiation method. All 3 are in the WeaponFramework namespace.

    The Weapon class originally just instantiated it's bullet type according to it's config data to fire on the Fire delegate event being called by UI. However now I'm making a pooling system, with a PoolManager script. It creates a list on Awake(), of GameObject's implementing an interface called IPoolableObjects, which handles activation and deactivation.

    Now the Weapon script, whenever it wants to fire, grabs the PoolManager's reference, and asks for a pooled object of type "WeaponConfig's Bullet Type". The issue being that the PoolManager will need to know what object to pool at the beginning of the game, creating ANOTHER required reference at start up. I want to minimise system dependencies. How would you go about setting up the pool manager with the bullet type required? Just delay it's Initialisation method to be called by the WeaponManager, who passes it's reference as it gets it?

    Also, I have no one around to do peer review on my setup. Any comments / criticism about my setup would be really useful :).

    Thanks
     
    Last edited: Nov 23, 2015
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,200
    The pool seems like it should be created when needed, rather than always existing. I'd hide the creation behind a factory, and initialize each bullet type's pool as needed.

    Assuming BulletType is an enum:

    Code (csharp):
    1. //Weapon:
    2. private void Awake() {
    3.     myBulletPool = BulletPool.GetPool(config.bulletType);
    4. }
    5.  
    6. //BulletPool:
    7. public static Dictionary<BulletType, BulletPool> pool = new Dictionary<BulletType, BulletPool>();
    8.  
    9. public static BulletPool CreatePool(BulletType bulletType) {
    10.     if (!pool.ContainsKey(bulletType)) {
    11.         pool[bulletType] = new BulletPool(bulletType);
    12.     }
    13.  
    14.     return pool[bulletType];
    15. }
    16.  
    17. private BulletPool(BulletType type) {
    18.        //Create the thing
    19. }
    There's some issues to look out for here; in particular, you'll have to make sure that your pools handles scene changes, either by setting the pooled bullets to not get destroyed, or by ditching the pools on a scene load.
     
    Nigey likes this.
  3. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Awesome, thanks a lot Baste :).
     
  4. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    Code (csharp):
    1. //BulletPool:
    2. public static Dictionary<BulletType, BulletPool> pool = new Dictionary<BulletType, BulletPool>();
    3.  
    4. public static BulletPool CreatePool(BulletType bulletType) {
    5.     if (!pool.ContainsKey(bulletType)) {
    6.         pool[bulletType] = new BulletPool(bulletType);
    7.     }
    8.  
    9.     return pool[bulletType];
    10. }
    This is a very common pattern in my own programming. I call it "lazy loading", though that term has many meanings.. Anyways, lazy loading has benefits, like not needing to know what kind of objects you need in advance and reducing overall memory use. However, it comes with costs like, as Baste said, dynamic unloading when they are no longer needed and initialization costs at undetermined moments. For instance, the first time your weapon is fired, projectiles of the correct type will need to be created in memory. If that is an expensive operation for the computer, there may be some noticeable lag.

    A few options to mitigate those costs are: a) Define a regular point in time when pools can be cleared, such as scene change, alternatively, have weapons that are discarded send a message to the pool indicating they no longer need projectiles b) Have weapons that are sure to appear in a given level send a message to the pool during scene load so the pool can pre-allocate some projectiles if it so chooses.

    This stood out to me in your OP
    Seems like it might make sense for Weapon Config to simply be part of Weapon. Unless you have weapons that change their configuration dynamically, it seems like the WeaponConfig class may end up anemic.

    I don't actually see anything wrong with this, so long as the configuration is organized and uses interfaces to communicate. Remember, the only way to eliminate dependencies entirely is to have classes which cannot send messages to each other. Good luck making any kind of software with that requirement.
     
    Nigey likes this.
  5. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Thanks for the response :). Yeah I've gone with that pool kind and found the same issues. Though mine isn't static. A shame the function can't instantiate and pass the required instance, then instantiate the rest in another thread.

    Code (CSharp):
    1.         public GameObject GetPooledObject(GameObject poolType, Vector3 _objPosition, Vector3 _objRotation)
    2.         {
    3.             string name = poolType.name;
    4.  
    5.             if (!pools.ContainsKey(name))
    6.             {
    7.                 pools[name] = NewPool(poolType);
    8.             }
    9.  
    10.             foreach(GameObject obj in pools[name])
    11.             {
    12.                 if(!obj.activeSelf)
    13.                 {
    14.                     obj.transform.position = _objPosition;
    15.                     obj.transform.rotation = Quaternion.Euler(_objRotation);
    16.                     obj.GetComponent<IPoolableObject>().ActivatePoolableObject();
    17.                     return obj;
    18.                 }
    19.                 else
    20.                 {
    21.                     continue;
    22.                 }
    23.             }
    24.  
    25.             return null;
    26.         }
    27.  
    28.         public List<GameObject> NewPool(GameObject _poolType)
    29.         {
    30.             int listIndex = 0;
    31.  
    32.             for(int i=0; i < pool.Length; i++)
    33.             {
    34.                 if(pool[i] != null)
    35.                 {
    36.                     listIndex = i;
    37.                     break;
    38.                 }
    39.             }
    40.  
    41.             if(!poolContainers[listIndex])
    42.             {
    43.                 poolContainers[listIndex] = new GameObject(_poolType.name + "Container");
    44.                 poolContainers[listIndex].transform.SetParent(transform);
    45.             }
    46.  
    47.             pool[listIndex] = new List<GameObject>();
    48.  
    49.             for (int i = 0; i < poolCacheQuantity; i++)
    50.             {
    51.                 GameObject obj = Instantiate(_poolType, Vector3.zero, Quaternion.identity) as GameObject;
    52.                 obj.transform.parent = poolContainers[listIndex].transform;
    53.                 obj.SetActive(false);
    54.                 pool[0].Add(obj);
    55.             }
    56.  
    57.             return pool[listIndex];
    58.         }
    I had the same thought toying in my mind. My original thinking was that there is a lot (a lot lol), of variables for each weapon. One weapon can have different scopes attached, with different unique fittings, and different ammo, which I wanted to be dynamically loaded. Plus each weapon has a fair number of unavoidable floats. It could well become anemic thinking... :S I'll have a look, thanks for the heads up. It could be a thought to have those as different components, which have base classes for the weapon to deal with abstraction.

    I suppose I could get the PoolManager to subscribe to the WeaponManager's equip/unequip events, and find the ammo type to pool from there. Might be two birds in one stone. So when the weapon's equipped the poolmanager makes any necessary pools, and on unequipped it'll remove them (they won't see that gun again lol).

    When it comes to peer review and making sure you don't form any bad habits, how do you deal with that? If you don't work in an office I mean lol.
     
    Last edited: Nov 24, 2015
  6. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    Interesting idea. You could do something similar with a time-splitting coroutine. Anyways, this seems like an optimization best left until you see a lag problem.

    Rather than data in a class, each of these things could be a class hierarchy: AScope, AFitting, AAmmo, etc... Customize a weapon by instantiating one class of each type and including it as a reference in the weapon. These classes don't even need to be MonoBehaviours.

    You should balance the unloading against the extra work the GC will need to do once the objects are released. You should see a decrease in memory use but will pay with processor cycles as the GC finds more work to do.

    You might be surprised how hard that can be even in an office...

    From my experience:
    1. Read a lot. Pick up some high level stuff like Design Patterns, Refactoring, Clean Code, etc... and some more technical stuff too like C# In Depth, Effective Java, etc.. Read it once and then read it again in a year or two.
    2. Seek out peer reviews. Sometimes you will know someone willing to look at your code, sometimes you need to get on a forum and beg. I enjoy these kinds of questions so I'll try my best to make a few comments if I see questions about design here.
    3. Try submitting to an open source project. It can be hard to find something you a) are interested in, and b) know the language, and it can be really intimidating the first time, but there's probably nothing better in terms of getting critique. The guys who maintain open source code are pretty zealous, so they'll probably rip a strip out of you when you first start but, if you can take it, you'll learn a lot.
     
  7. MattRix

    MattRix

    Joined:
    Aug 23, 2011
    Posts:
    121
    Maybe I'm missing something, but it seems like you're overcomplicating things a ton and trying to do this in a very java-esque OOP way rather than just doing it more of a "Unity way". There are very, very few times you need abstract classes in Unity. Make things component driven! Why not have your weapon configs and bullet types be ScriptableObjects? Don't create managers unless they are really adding functionality.

    "I want to keep a decent level of abstraction so my classes don't become rigid and brittle, and I can extend on this project."

    In my experience, developing things this OOP-y way makes things more brittle, harder to maintain, and harder to extend. Make a game, not a framework :)
     
    Kiwasi, Fajlworks and eisenpony like this.
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,200
    Component-based architecture and OOP are not mutually exclusive in any way.

    You can build a shmup game where each ship has a Movement component and a Weapon component and a Health component where both the Movement, Weapon and Health classes are abstract:

    abstract class Movement : MonoBehaviour
    class WaypointMovement : Movement
    class SplineMovement : Movement

    abstract class Weapon : MonoBehaviour
    class ProjectileWeapon : Weapon
    class CrashWeapon : Weapon

    etc. etc.
     
    eisenpony likes this.
  9. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    @MattRix very good points. It is easy to slip into the class hierarchy trap, particularly when you are new to OOP -- which a lot of the Unity community is. As @Baste pointed out though, Component based and OO based design are not mutually exclusive.

    Most often, I see this trap when programmers try to model real life within their code. i.e., the classic "a square is a rectangle". Or perhaps more practical to this group, "a gun is a weapon". I think the trouble stems from the fact that natural languages, like English, allow for a certain level of ambiguity out of necessity. Translating concepts that suffer from polysemy into a structured language is easy to mess up. Still, throwing away OOP because it "isn't the Unity way" is like throwing the baby out with the bath water. Polymorphism is central to a number of very good design patterns, the trick is applying it with just the right discretion.

    I like the way you put this. A heuristic for me to decide whether polymorphism is appropriate is to determine if the code I am writing is more domain or more framework. If it's framework-ish, polymorphism is safer. If it's domain-ish, I try to stay away from inheritance entirely.

    However, I'd say a good code base usually has bits of both in it; framework code helps keep your code base maintainable, domain code makes you code base actually do something interesting. As a programmer, one of our jobs is finding the right ratio of domain to framework.
     
  10. MattRix

    MattRix

    Joined:
    Aug 23, 2011
    Posts:
    121
    Hah wow guys, I know you can use inheritance in Unity :p I was not talking about OOP as in "never use inheritance". I specifically called it the "java-esque" style, and I think you guys know exactly the stuff I mean: worrying about IOC, dependency injection, factories, abstract classes, etc.

    And yep eisenpony, you're right, it comes from how we're used to describing objects with spoken languages. The other place it comes from is that most of us programmers love thinking about systems and ways they can slot together. Creating complex systems and structures is just very satisfying, but unfortunately it's rarely the right approach for game dev. I think the key is to learn to think about programming in terms of behaviour: think "what does it actually do" rather than "what kind of thing is it".
     
  11. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    Then I detect a bit of polysemy around the term "java-esque". I write mostly in C#, Small Talk and JavaScript. I routinely use IOC, DI, factories, and abstract classes in all of these (OO in JavaScript is possible, same as it is possible in C). Honestly, I didn't even see the word in your first post..

    I think I missed your meaning in this too. Maybe you meant something more specific?
    I've assumed you are referring to industry software since you refer to Java. Game dev and industry software design are certainly different but I don't believe they are different in that one is complex and one is not.

    Breaking systems into behaviours definitely makes sense for Unity, since the framework encourages this explicitly -- it's called a MonoBehaviour after all. Not every framework thinks this way though, and it shouldn't stop you from also breaking systems into "things".

    I have to assume this was meant for my suggestion to break the "mods" into components using abstract classes.
    Actually, that's precisely what I was suggesting -- componentize. Rather than keeping floats in a configuration script and ending up with an Anemic Domain Class, move the modifying behavior to a class hierarchy and reference it from the weapon using the Strategy Pattern. If it makes you feel better, you can rename AScope to AZoomingBehaviour.
     
  12. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Hi MattRix, thanks for the feedback! I was following the advice of the Unity Tutorials when it comes to abstraction - https://unity3d.com/learn/tutorials/modules/intermediate/scripting/coding-practices?playlist=17117. Whether I'm using direct dependency, an interface, or inheritance would normally base off of whether the system/c;ass in question would be a one time, unique dependence. One that is not likely to change. So I'd use a direct dependence there and just accept that. If it's a class/system which will be used on a lot of different object types, but be recognised as the same 'type' in some common functionality. So like a 'PickUp'. There I'd choose an interface. Lastly if it was a object that shared very similar functionality between a collection of other objects, which all had some unique code, but were all inherently very similar; objects that something else will be depending on to call. There I'd use abstraction through inheritance. It could be that I'm just not using single responsibility correctly, or just going overboard with framework like you guys are saying. I need to re-evaluate how I develop in Unity I think.

    Before Unity I hailed from Unreal Development Kit. The weapon class hierarchy there was literally:

    Object -> Actor -> Weapon -> UDKWeapon -> UTWeapon -> UTSomeUniqueWeaponType

    And from UDKWeapon forwards it had dependencies on the player's InventoryManager, ThirdPersonMeshSetup, FirstPersonMeshSetup, and all sorts. Each class knocking up aboutt 2k of code totaling about 15,000 lines of code just in UnrealScript on the weapon. That's ignoring behind the scenes C++ setup of the weapon. So it was a pretty deep set hierarchy. I think this setup is still wracking around my brain somewhere.

    Originally I was actually just 'making a game' with Unity, without really thinking about framework. Each game worked too. It's just as it got larger and larger, they became more unwieldy, and changes to gameplay from design would mean a hack and slash through dependency. So after looking through a load of Unity design pattern blogs I went to the IoC frameworks on Unity Asset Store, trying to find the best architectural designs for big projects. Asked advice on here about them. Generally people suggested against IoC. So I read through a plethora of tutorials about the 'Unity Way'. Finding the most popular way was the composite design pattern. Where each mini system had it's own tree hierarchy, and each system would relate to each other as a single entity (as I understand it). Now after following that for about 6 months, I still find myself slightly confused about what is the 'Unity Way'. As there seems to be no set form. I get that Unity is a blank white sheet of paper where you can put anything you want on, - which is brilliant - however after about 3yrs on it, my design patterns for it still feel confused. I'm used to a fixed clean hierarchy. That where these questions are truly coming from.

    So any advice on or any suggested articles or tutorials on how cleanly architecture games in Unity. I know each game will be different, however there must be some pattern to it. Even subjective mini patterns.

    Because I've been looking at things like this mainly for advice:
    http://wiki.unity3d.com/index.php/Dependency_injection
    http://blogs.unity3d.com/2014/05/07/dependency-injection-and-abstractions/
    http://www.gamasutra.com/blogs/SebastianoMandala/20121019/179765/IoC_Container_for_Unity3D.php
    https://unity3d.com/learn/tutorials/modules/intermediate/scripting/coding-practices?playlist=17117

    and I'm not sure whether they're subjective opinions or correct, and I'm still not totally sure on how to set things up. Unless I just do it 'however it fits'.
     
  13. MattRix

    MattRix

    Joined:
    Aug 23, 2011
    Posts:
    121
    Yeah all of this is subjective at the end of the day, but IMHO the solutions in all of those articles you linked are crazy. This stuff can get confusing, especially after reading a bunch of articles like that!

    Keep in mind that just because something is in a tutorial on the Unity site, it doesn't mean it's the "right" or only way to do things. When I said "Unity Way", that was definitely a bit misleading... Unity has used conflicting ways of developing things, even in their own examples and tutorials. All I really meant was that you should try to use the stuff that's already built in (components and ScriptableObjects), rather than reinventing the wheel.

    I can't help but think those articles are all trying to pre-solve problems that they don't have yet. In other words, they're trying to do things "just in case". All the IOC and DI stuff seems insane. Your project isn't going to explode if you let thing reference each other directly. You don't need interfaces or abstract classes! (most of the time...)

    Now it's easy for me to complain about things without giving any solutions, so here are a few tips that work for me... There are no hard absolute rules, and every situation is different, but the key rule here is *keep it simple*.
    • Use ScriptableObjects! These are handy for any kind of configurable data, especially for data that you want to use in multiple locations (ex. how Unity uses them for physics materials).
    • Don't worry about dependencies! Start everything tightly coupled and only decouple if you really, really need to.
    • Singletons/global managers are great! Don't worry about single responsibility or decoupling with these, it's way easier to make sweeping changes when whole bunch of tightly related things **specific to your game** are actually all kept together.
    • Don't do test driven game dev! That is insane. The only place where tests make sense is in stuff like integration with backend services. (I know you weren't specifically mentioning that, but some of the articles you linked did)
    • It's often good to have single responsibility components, BUT don't feel like you have to create them that way from the start. It's fine to have a component that does a bunch of different things, you can always split it into other components later *IF NECESSARY* (often it isn't necessary).
    • For example, with weapon types, maybe they don't need to all be in different classes (it depends on your game). You could just have a WeaponType ScriptableObject, and the Weapon component combines that with a few different functions that different weapons types use do to their different actions. Think of it like a state machine where each state is actually a different weapon behaviour. Again, depending on the game this might be too simple... but this is where I would start, and then only decouple it later *if needed*.

    Reading these tips, I realize it probably reads like a list of "what not to do". This is just what works for me, there are no truly "right answers". All I know is that I used to do things the "proper way", and these days I'm doing things this way and I'm getting things done faster, finding my code easier to maintain, and enjoying game dev way more... because now I'm making games instead of making frameworks :)
     
    Last edited: Nov 25, 2015
  14. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    I actually really like your "to don't" list. It's very easy to get lost in the amount of advice out there so it's great to hear from someone with a fair bit of experience sharing how to focus on what's working.
     
  15. Palimon

    Palimon

    Joined:
    Apr 18, 2013
    Posts:
    225
    I'm building a game with a very component-centric architecture, as I'm building a spaceship with piles of equipment, and I wanted it to be very modular. This could be very similar to what you want as you clip on scopes and such. I have an abstract Equipment class, and engines, shields, guns, etc all derive from Equipment. Now, I also split the control portion of these entities into an abstract EquipmentController and derived them into GunController, etc. Whether it was necessary to split that, I don't know yet :). BUT, the useful part for you, I feel, is that EquipmentController has this method sig:
    Code (CSharp):
    1. public abstract void Operate(List<KeyCode> allKeys, List<KeyCode> allKeyDowns, List<KeyCode> allKeyUps);
    With that, my spaceship...and your weapon...all loop through their list of equipment and pass their input through that method to each equipment, and do so recursively. This way your gun doesn't need to know about a scope's zoom functionality, it just knows it has a scope and tells it to do it's thing...if it has a thing. Or, alternatively, you could put the scope on a hamburger and BAM, it's a zoomburger :p.

    Now, I probably have a lot more ways to mix and match equipment in my game, which is why I had to go down this path, but it might be useful for you too. Good luck!
     
  16. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    With all the random conflicting advice out there it's brilliant to read this. Should be a sticky :p. It sounds very much like what I was doing for about 10 games. Then started to read all these sites about the 'proper way' to dev in Unity. Including things like unit testing and test driven development :eek:. My mind ran the thought that it seemed silly, but you know, 'the blogs' said so lol.

    @Palimon yeah it's a neat idea, but I'm actually hoping to hack the architecture down after the advice from these guys :).

    Thanks for all the advice guys, it's cleared up my head tremendously!
     
  17. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    How did I miss this thread? It's relatively interesting.

    I'm nervous about some of this advice. Most of it (but not all) runs against most of my experience thus far.

    ScriptableObjects are great for configuration details. But close coupling is a pain, I always end up needing to break up close coupling and dependencies. Singletons are another one that I have almost always regretted implementing. And single responsibility is kind of essential to getting component based design to work.
     
    Nigey likes this.
  18. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    @BoredMormon I'm trying to understand how to design systems in Unity. I've heard of the practice of making each little system like a single entity, and I remember you tend to break them down in namespaces, but I don't know the ins and outs of how to make them. Would you mind chipping in your 5 cents and maybe showing an example of a system? If it's not too much of a bother?

    Anyone who could show an example system and how they've put it together would be SOOOOOOOO helpful!
     
  19. MattRix

    MattRix

    Joined:
    Aug 23, 2011
    Posts:
    121
    It's anecdote vs anecdote at this point hah ;) Sometimes you do need to break up close coupling, but lots of the time you don't! That's why I (almost) never decouple at first. I do it later when the need actually arises. And to be clear, sometimes single responsibility components are what you need, but other times, a big monolithic component gets the job done.

    For example, if you're making Mario, maybe you have a gameobject called Player that has a KeyboardMovement component, a PlatformPhysics component, a JumpOnEnemiesHeadsWeapon component, a ShootFireballWeapon component (disabled until you pick up a flower), a SpriteRenderer component, a ShrinkMarioWhenHeGitsHit component etc.

    OR, as I'm suggesting, you can just start with just a SpriteRenderer and a component called Mario, and you program all the mario gameplay logic into that one component. Later on you might choose to break out the PlatformPhysics and some of the other components, but there's a good chance you won't ever need to.
     
  20. MattRix

    MattRix

    Joined:
    Aug 23, 2011
    Posts:
    121
    Oh and I'm very curious to hear about your specific issues with Singletons. I've found them to be the perfect choice for all kinds of systems in my games, and never once had an issue with them.
     
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Which often means there is no right answer. Just differences in style.

    On singletons I normally find they tangle the code up. They tend to break encapsulation. And they tend to encourage dependencies. (But if you don't mind heavy dependencies then it's not a big issue.)

    I occasionally use them for very low level systems, like game state managers. But that's about it.


    The way I build games I would quickly find I would need
    • A Mario entity that did not respond to player input and instead could be run by an AI. Hence input would be a seperate script.
    • Other entities that would also move and jump and land on platforms. Hence a seperate motor script.
    • Other entities that shoot fireballs, hence a fireball shooting script.
    And so on. At this point I've just about broken things down to single responsibility anyway. So I might as well finish it off.

    System based coding is one of my particular favorites. I don't know if it's a real thing outside of my head. If it is it's probably called something different.

    The general idea is that rather then building entities you build systems. That might include a damage system, a movement system, a score system and so forth.

    Each system is composed of a bunch of components that are relatively close coupled. A damage system might include a weapon component, a health component, a projectile component and a death component.

    Together this system can be relatively isolated from the rest of your game code. No matter what else you build, the weapon will still create a projectile. That projectile will communicate with a health component. When health runs out death is invoked.

    On key to make this work is that the entry and exit points for your systems must be clearly defined and adhered too. For the damage system the entry point would be a Fire method on the weapon component. The exit points would be an OnDeath event called by the death component. There may be a few viewable parts along the chain, like current health.

    Entities like Mario or a mushroom are built by combining a bunch of components from different systems on a single GameObject. Add in a little configuration data and you are done.

    For me the biggest advantage of this set up is that it matches up reasonably well to how chemical plants are built. And I'm still very much a chemical engineer at heart. ;) In more useful terms it allows parts to be pulled out and replaced without requiring any change to the rest of the code base. And that's useful in making games.
     
  22. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    I tried pretty hard to find some literature on why you should use tight coupling. I failed.

    I don't think anyone would argue that loose coupling is "bad", just "expensive" in terms of code complexity and time to write it. So if building in abstraction makes your design more flexible we want more of it but since it is expensive, we want less of it.

    But let's break this down another step.

    Flexibility: We really only need our design to be flexible if we plan on making a lot of changes, if we plan to reuse our code many times, or we plan to share our code with others. So if we're out to make small games without a team, we probably don't need any abstraction at all.

    Expensive: Abstraction is difficult to wrap our heads around -- we are better at considering concrete examples. However, the more we practice at it, the more easily we can detect common patterns and write good abstractions. So if we practice using abstraction, it becomes less expensive.

    This is where your judgement and your goals need to come in. Are you out to quickly make some simple games? Then you can probably skip the abstraction and get down to the, potentially, more "important" aspects like making a game that is actually fun. Are you out to make a gigantic game with a team of developers? Then you should probably take your time and build up a framework to maximize code reuse and retain consistency across all developers. Are you out to learn about making games fun? Don't worry about programming "correctly", just make it work and focus on your mechanics. Are you out to lean about making big projects and working with others? Spend the time to read about good design principles and practice some of the more "insane" processes.
     
    Kiwasi likes this.
  23. MattRix

    MattRix

    Joined:
    Aug 23, 2011
    Posts:
    121
    I think we're mostly on the same page here. If you **know** you're gonna need those capabilities, then I'm all for splitting things up. My point is that way too often I (and lots of other devs) tend to do a lot of decoupling "just in case".

    The main unconventional argument I'm making is that heavily decoupled code often becomes *harder* to manage rather than easier. Every part of the system is in a million different places, and you have to follow a complex control flow to figure out what happens, and when. You start running into race conditions where it's hard to ensure things happen in the right order with the right data. When your code is tightly coupled, it's much easier figure out what is going on, and to make changes without serious side effects.
     
    Last edited: Nov 26, 2015
    Kiwasi and eisenpony like this.
  24. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    I don't believe singletons will lead to tangled code or dependencies on their own. You should be able to abstract your references to a singleton just as easily as you can to any other component. The trouble with Singleton is that there can only ever be one. If you decide later that there should be two of something, you are out of luck without refactoring that code.

    For instance, if you made your input system a singleton and later decided you wanted to support local multiplayer, you might have worked yourself into a corner. That doesn't mean there is no way out, you'll probably just wish you had thought of multiplayer earlier on. Again, this comes with experience and having a clear image of the desired end project. Predicting how things will change, and proactively building in abstractions that make this change easier to implement are part of what makes a great programmer. (It's also really hard and I certainly don't consider myself any good at it)
     
  25. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    Definitely guilty of that.
    This I think can be managed using other techniques like aspect oriented programming or inversion of control, but it's still a good point. Tightly coupled code has more physical closeness by definition. Doesn't mean you are immune to race or timing conditions though. If the code is too tightly coupled, it may actually make it worse.
     
  26. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This I have encountered. Its one of the genuine downsides of going with massive amounts of tiny components. A badly designed system ends up with logic flowing through a bunch of scripts. You can manage this with good design. And moderation, any good principle taken to an extreme can be bad.

    And here we are disagreeing again. I've always found close coupled stuff is less robust against changes and side effects.
     
    eisenpony likes this.
  27. MattRix

    MattRix

    Joined:
    Aug 23, 2011
    Posts:
    121
    There isn't a ton of info out there on it, though I guess YAGNI is kind of what I'm advocating.

    Jon Blow did a great talk a few years ago on how he programs games, and I think his ideas are in the same ballpark as where I'm coming from. If you don't have a lot of time, there's a good summary of the talk here: http://www.magicaltimebean.com/2011...athan-blows-how-to-program-independent-games/

    The full talk is fantastic and well worth watching, but if you just want to see the "specific vs generalized systems" section, skip to 24 minutes in:

     
    Nigey and Kiwasi like this.
  28. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    Video was great. Thanks! It put into words very nicely the tension between "write some very carefully crafted software" vs "write a lot of working software." I agree it's easy to focus on what he called the optimization of program speed and space and lose sight of how much of your life you spend on a program.

    Anyways, I still believe there is value in discussing complex and flexible system designs. Not because they are the best way to create software but because they help you to enumerate ways to make your system more reusable in the future. This is especially valuable to do in community because different people will bring different view points and options. Probably, the discussion will reveal dozens of paths to introduce flexibility and it is then up to the programmer to decide which of those are actually necessary, taking their goals and resources (e.g., lifespan) into account.

    Like he said in the video, there are a lot of people sharing all kinds of ideas on the internet, so it can be a difficult to filter through. Still, I'm glad for the voices of experience who take the time to share their knowledge.
     
    Nigey and Kiwasi like this.
  29. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I watched the video a few months back. It's a pretty good one. I tried coding like he advocates breifly. It didn't really work for me.

    My experience with YAGNI so far is I always do need it. This may just be my style or personality.