Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Architecture & Tight Coupling #1

Discussion in 'General Discussion' started by GarBenjamin, Mar 23, 2017.

  1. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    I'd like to get some input from you on a common practice in Unity.

    This being wiring things up in the Editor. When I first started using Unity I thought it was quite empowering the way we can easily drag n drop references from one component to another.

    However, in the back of my mind I had this nagging thought that I was doing something I really shouldn't be doing... basically hard-wiring dependencies. The only difference is I was doing it visually instead of programmatically.

    I think we all know that too much of this kind of thing causes problems down the road.

    I think the visual nature of the process makes it so many people don't consider it the same as if they were hard-wiring inside their code. Kind of the same way many people use the common pattern of setting up a Delegate / EventHandler in code and then use the .SomeEventSubscriptionMethod += SendEventNotificationsToThisMethod pattern thinking they are actually making their code more loosely coupled when in reality this is not loosely coupled. How can it be? This pattern has one class explicitly registering itself to an Event on another class and to do that it must first have a reference to the other class. There is some flexibility in the pattern in that multiple classes can register and receive the notifications but that is another story. The proper way to decouple it IMO is to just create a completely separate EventManager class and have all publishers and subscribers use that one class. Now there is a single explicit connection used by all other classes. Of course, in Unity I suppose the SendMessage may be ideal for decoupling.

    Anyway, I digress...

    Let's use a basic example of setting a camera to follow a player. Making the camera a child of the player works. But that is too simple so let's say the camera is following a pinball. Can't simply be a child because the camera would roll as the ball rolls.

    There are always a number of different ways to do things and there are alternatives to wiring things up in the Editor.

    One way would be to simply use GameObject.Find("Player").GetComponent<Transform> in a Camera script to automatically find and set the reference of the player's transform.

    Another (kind of insane) way would be for the Camera script to have a public method that accepts a transform and in a script on the player have it locate the camera, get a reference to the attached camera script and then call the method to pass over the player's transform. The camera script receives the transform and stores it.

    The Unity messaging system could be used in the same way to allow a player script to send its transform over to the Camera.

    A custom Notification Class could be used to do the same thing.

    And so forth. Many different ways. With pros and cons to each. For Unity I think a good way is probably the first approach described above... simply make use of the Find() method in a camera script. However, it is obviously not completely decoupled. There is a dependency. A requirement. Simply changing the name of the GameObject from Player to Hero would cause the code to fail and the reference would not be set. Same for passing to a camera where the camera name has changed.

    So, in this case relying on some kind of Messaging / Notification system OR simply wiring it up in the Editor actually are the more robust solutions. The issue I see with hard-wiring up references in the Editor is it can turn a project into a mess of spaghetti where you end up looking around... "where in hell is this set up?!" Is it in the code? Is it in the Editor? This is particularly a pain point when dealing with projects someone else created such as Assets from the store.

    Reason for all of this is that I plan on very slowly getting back into Unity and focusing on each thing like this one at a time to find a best pattern before going forward. I already did that a lot previously but I will revisit and evaluate the options just to be sure.

    So this all leads me to my question...

    How do the rest of you feel about such things... do you just wire up references in the Editor not giving it a second thought or do you strive to decouple things more by using code to set up these references such as using some kind of Messaging system or possibly even a single class containing all of the Editor hard-wired references?
     
  2. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,462
    I think the concerns you have are valid, but some of them are dependent on how the rest of the code is structured and where you decide an event subscription or hard reference is a good idea. It's a really broad topic because at some point you must have references between objects or there is only giant multipurpose scripts that never communicate. The tricky part is structuring those classes and interfaces so that things communicate safely. I like observer style patterns.

    For instance I try not to have to drag-n-drop references on anything that is not in the same hierarchy. Eg, Prefabs only. This basically keeps them entirely self sustaining. If I need scene references, I try to obtain them either through static variables when possible, or via some scene event which makes sense. If two things should be communicating then there is very likely some kind of interaction where you can pass the references between them safely and guarantee non-null communication. It's really about control.

    Usually I design this stuff backwards. I write the simple code that I want to use to talk to some object then I go to that object and figure out how to make that call possible. This makes communication interfaces simple and forces the target scripts to deal with validation and stuff instead of the observers/remote methods.
     
    GarBenjamin likes this.
  3. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    I've done both.. for things that I "believe" may be relatively static I'll use the reference, perhaps as a list of elements that are just drag-drop in editor.

    In some use cases, to ease reference handling, I have used a design pattern like Observer to do it as the objects may change frequently, and I want them to just register/de-register themselves.

    Anything to avoid the "Find" object stuff... we all know that is lazy and slow.
     
    Ryiah and GarBenjamin like this.
  4. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,554
    I'm using a dumbest, simplest approach that will still work without potentially biting me in the future. See:
    https://en.wikipedia.org/wiki/KISS_principle
    https://en.wikipedia.org/wiki/You_aren't_gonna_need_it

    In case of your example - "Camera + player", the idea of using .Find() is wrong, because you rely on magic hardcoded string "Player", and because .Find is not guaranteed to be really fast.

    What I'd do would be making a simple CameraTarget component, and have Camera component accept the reference it (via a CameraTarget typed field that is visible in editor) and orbit around it if it is specified and not null. This way you can attach empty "Target" game object to any thing and this way you do not depend on player being named "Player".

    That's all there is to it.
     
  5. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Thanks for the replies. Just one tidbit with the use of the Find I'm not concerned about performance. I would use that only one time to get the reference and then store it. Not do some madness like use Find every frame. lol

    So if I understand correctly... two of you are leaning toward use of an Observer pattern with subscribers and publishers. Which is basically my preference as well only further decoupling it to a stand alone NotificationsManager (that I already wrote about 3 years ago for Unity lol) so the subscriber and publisher don't know anything about each other period no interfaces etc both sides just deal directly with the NM.

    And one person favors wiring up the references in the Editor with the addition of a separate GameObject named CameraTarget. But still wired up in the Editor.

    Which of course also provides a solution for the case of using Find on a Player that has been renamed. By using Find to get ref of CameraTarget child it doesn't matter what the parent Player has been renamed to.

    Still this gets back to the heart of the question... to wire up in the Editor or not to wire up in the Editor.

    I think my struggle with it is I do believe in the same philosophies as @neginfinity yet at the same time I also believe in the value of decoupling at least to a degree.

    I think at this point I would favor adding a CameraTarget "empty" child GO and using Find. It is very simple and yet decoupled. Sure if CameraTarget name is changed it will break but the chances of that name changing are far less than Player being renamed to Hero or maybe Bob. The observer pattern can be used but seems a tiny bit overkill for something so simple. Decoupling in the Editor!!! I guess I still have a hard time wrapping my head around the Editor and realizing these GameObjects are for anything & everything we want to use them for. lol

    I think I'd consider this dedicated reference GO as a best practice. Thanks.
     
    Last edited: Mar 23, 2017
  6. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,554
    Magic numbers are evil. Magic strings are also evil.

    No. Observer pattern notifies subscribers. My example does not notify anyone of anything.

    In "Camera" component there's a serializable field "CameraTarget cameraTarget = null". If CameraTarget is set, Camera orbits around it. CameraTarget component is completely blank and have no functions or data in it, although I suppose if you wanna be fancy, you can add OnDrawGizmos/OnDrawGizmosSelected to it.

    Basically, I create typed empty class, use its type as a filter to restrict number of objects that can be dragged into camera, and this way maintain a "weak link". By the way, testing for null is overloaded in unity, meaning you don't even need to check if the target object is dead.

    Now, if you had a problem with grabbing player, then I'd probably go with something like UserController having a static method or a property that return "currentInstance". Basically a singleton. The player object would register itself upon creation and unregister itself from distruction. This one is less universal, and closer to observer pattern, except there really isn't an observer to notify anything.

    Whichever floats your goat. I mean boat.

    Basically... unity events, for example, allow one to add seven different notification receivers to a button click. Doing that is (IMO) bad and should be avoided, because it falls into category "it is going to bite you later". However, if there's one or maybe two methods added to notifier, this isn't wrong.

    I think I definitely would not use Find because it relies on string names.
    If your method relies on an object name, then Murphy's law says that the object is not present in the scene, named incorrectly, or will be renamed by another string (because you can change names on the fly at runtime) and you'll only discover it two months after release. Unity object names also are not even guarnateed to be unique. You can have two players in the scene.

    I'd either search for components, use a signleton method "UserController::getInstance()" and one more option would be actually make CameraTarget more active and register/deregister itself in global camera as a follow target. That's mainly because there's Camera.main and Camera.allCamera which do not rely on string names.

    Basically, in all situation - types, classes, components and never object names. Because magic strings are evil and objects can be renamed. Class types cannot change at runtime and are compiled into the game.
     
  7. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    @neginfinity Good points. I meant the other two people for Observer pattern. :)

    I considered using something similar to one thing you are describing. Defining an Interface Player and sticking it on the Player class.

    A lot of this always comes down to personal preference. And there are always so many ways to do basically everything. Even this which is a very simple case and look at the number of strategies mentioned so far.

    I think I will just try several of the "better" ways and see which I like the best. The rest of the game dev I think is easy. With Unity it is these most fundamental things that I wrestle with. I don't want to spend ages on such simple things but at same time want a good reliable pattern for the future.
     
    Last edited: Mar 24, 2017
  8. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    I actually agree with this. All you're doing is inverting the call, not decoupling. That said, it's a really handy way to invert the call. The alternative is:
    Code (csharp):
    1.  
    2. public interface IHandleThing{ void DoThing(); }
    3. public class Button{
    4.  public List<IHandleThing> OnClickListeners = new List<>....
    5.  protected void Notify(){ foreach( var i in OnClickListeners ){ i.DoThing(); }
    6. }
    7.  
    The event/delegate approach is really just shorthand for all this and helps you not need a billion interfaces for everything.

    I have used pub/sub systems in unity for a couple years. They're ok. I tend to use the pub/sub stuff to move a message to horizontally to an unrelated subsystem, so I tend to use them for more state changes. Pub/Sub can be just as annoying as anything else and I keep my implementation very simple. I strongly type all messages and instead of just namespacing, I use nested types, so you must include the message 'namespace' when using the message.

    Code (csharp):
    1.  
    2. public static class On{
    3.   public class CharacterKilled{ ... }
    4. }
    5.  
    6. public class Thingie : ISubscriber<On.CharacterKilled> { ... }
    7.  
    It works alright.

    Problems are generally the same as any other decoupling strategy, makes it a bit harder to find stuff than tight coupling. I don't always immediately notify (I guarantee notification is complete before sending more messages, which is not 'out of box' with events), and if you aren't notifying immediately, you can lose the stack which can make debugging harder.
     
    GarBenjamin likes this.
  9. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    I routinely use the various approaches @neginfinity just described and they work well for me.

    No... one object is registering itself to an Event on another object, and to do that it must have a reference to that object. That object's reference does not have to involve knowledge of a specific class. It could be a parent class, an Interface, or looked up by reflection off the top of my head.

    In one game I made a couple of years ago I've got a "board" that has all of the pieces in it and then a bunch of other components that represent and implement rules. One that controls how moving works, one that controls how spawning works, one that controls how score works, etc. They all communicate via events, they are all aware of each other only via Interfaces, and no component cares what specific types of other components it's working with. For instance, score is provided by an IScoreHandler, of which there are different implementations for different game modes. The UI only cares that there is an IScoreHandler in the scene to listen to score changes from, it doesn't care what class is implementing IScoreHandler or how it works internally.

    I can implement a different game mode just by writing new implementations for an interface and swapping one component out for another. No other component cares when I make such a change, and in many cases I can straight up delete things and the rest of the system just goes on doing its thing with the game remaining entirely playable. For instance, the "chillout" mode is exactly the same as one of my other modes, with the end game and score components deleted.
     
  10. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Yeah I get that. I am using the term loosely class. Ultimately it is an an instance of a class whether we get there through an interface or otherwise.

    This is the kind of thing I used to love working on before I decided to throw it all out and simplify down to straightforward procedural programming. The kind of system you are talking about. Programming to interfaces instead of implementations. There is value in it.
     
  11. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Yep this is why I decided a year ago to throw out all of this stuff and simplify for game dev. And how I ended up where I was for that Unity Space Invaders again. The stuff is interesting & fun to work with but it is easy to spend way too much time messing around with all this overhead instead of actually working on the actual game.

    I spent some time earlier throwing together 4 examples of getting a reference to the Camera from the Player. And while I was doing that I couldn't help but to think how much simpler all of this would be if it was a more straightforward approach.

    GameManager
    player= CreatePlayer();
    camera = CreateCamera(player);

    and so forth. Dead simple. I am tempted to approach my Unity Game Dev this time around from this angle.

    Anyway... I hate to see long discussions like this without any ready to go examples... so if anyone wants to see a test of 4 different ways to get the reference the link is below.

    Unity 5.4 Project With 4 Examples Of Getting Player Reference to Camera Script

    I have no idea why it is 3 MB zip file but that is what it ended up as.

    Basically just drag the player around in each scene and watch the Game Window for testing.

    Scripts are named to make it easy to recognize which scripts go with each scenes. I didn't make folders specifically for each scene. Could have done that. But didn't.

    Also... I know some of the comments in there are not correct like I remember one I put this is called by the so-and-so script and the name I wrote was the current script. lol It is called by the PlayerControl script. But the point was just to illustrate various ways of doing the same thing. If nothing else it might be useful to someone just learning Unity. Maybe.

    I did waste time trying to make an example of using SendMessage but for whatever reason it never worked. Kept complaining about no listeners despite me having the method in a script. Maybe it doesn't do what I thought it did and you need a reference to the other object and use that to send the message. I have no idea but don't need it for anything so won't waste any more time on it.
     
    Last edited: Mar 24, 2017
  12. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    I went pretty extreme with that project to see how it went. I was expecting it to be an unfollowable mess, because there's no single place where the system flow is visible. To my surprise it was actually one of the smoothest, easiest to work in code bases I've used.

    To be fair a part of that was because the game was fairly simple. But another part was because each component was small, did a simple job, had clear input and output expectations, and could be worked on almost in isolation. If there was a bug with something I knew exactly where to look for it and I knew exactly the scope of change for any modification I made.

    I also never needed to write any game-specific configuration code, including not needing a "GameManager". I put things in a scene and they looked after themselves. You say "dead simple", but you're hiding the details behind function calls. What goes on behind "CreateCamera(player);"? Is writing that code really any simpler than using Unity's Editor to do what Unity's Editor is meant to do?
     
  13. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    I also really feel I should say... I've worked on projects that took the "simple" approach above and while it does work well enough on small things, as projects grow the maintenance costs just get huge.
     
    GarBenjamin likes this.
  14. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,850
    The Inspector is your friend. Do not fight it. Make use of drag and drop references. These can be altered via code to reference another of the same type at runtime. Don't get anal. This is not Python nor MS C#. This is Unity components. Carry on.
     
    Ryeath, Ryiah and GarBenjamin like this.
  15. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Ha ha. I'm not bashing Unity here. I am saying for me it is simpler to just use other approaches. But the whole point of this (me revisiting Unity) is to try to connect with it. So while it would be much simpler IMO to use an approach where a manager knows all of the top level, sees all of the top level and delegates responsibilities to other managers. That is something that scales well IMO. I won't use that approach and will try to focus on a more Unity style of game dev.

    Unity is powerful there is no doubt about that. Mainly I'm trying to find how much I should embrace the Unity specific things. That is why I asked the question originally. If most experienced people here have just went with the approach of wiring everything up in the editor and found that it does work well for them even as the game scale increases then I'd give it a shot even though originally (when I first started using Unity) it was an area of concern for me.

    Basically I am wondering how much of software engineering principles are people using in general and how much are they relying on the ways Unity allows us to do things.

    I think I will have to get into Unity again so I can remember exactly what I was running into originally that turned me off to using the way a lot of folks seem to be. To be sure, I am not the only person who thinks these things or approaches it from this angle. Over these past few years I did a lot of searching just to see what other people were doing and they are using everything from a purely procedural approach (although different setup than I used) to an MVC model to basically just doing exactly what the Learn section shows for the beginning examples and various combinations. Just a huge amount of variety really. And that says a lot for Unity that it is so flexible.

    Anyway, I am going to work on a little project just a simple one. Just a little 3D RPG in a small area. Of course, it will be primitives mainly. But this will be enough of a scale to get into it and really see how well I can adapt to it. And compare it to using a more procedural or standard OOP approach. Basically I am just going to have fun with it and try to spend more time in the Editor and taking advantage of what it has to offer. Who knows in the end I might just end up really enjoying it.
     
  16. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    How many engineers does it take to decide on a strategy for script referencing?
     
    Martin_H likes this.
  17. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    Exactly one.
     
  18. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    Sure, but that's not the distinction I'm asking about. You're saying it's simpler to put things in code explicitly. (At least I think that's what you're saying.) I'm saying that at some point it becomes simpler to use other tools for those things. ("I put things in a scene and they looked after themselves.")
     
    GarBenjamin likes this.
  19. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    Yes. And every engineer thinks that they are that one.
     
  20. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    And we all are. There doesn't have to be One Correct Way for everything.
     
    Martin_H and Master-Frog like this.
  21. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    And so begins the epic debate of the neck beards . . . for whom time has a different meaning.

    Honestly, I think software engineers are very similar to these guys:



    "Anything worth saying is worth taking a long time . . . to say."
     
    Jingle-Fett likes this.
  22. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Oh I see. Well yeah... it is always easier to do things in code. You can structure your architecture exactly the way you want to. You can handle the details exactly the way you want to. You can abstract things exactly the way you want to. And while sure you can still do these things the point is it won't be nearly as straightforward. Because the underlying architecture has already been chosen. And here it is composition with a strong focus on using both code and the Editor.

    BUT... I don't want to think about this because my goal here is to not view it this way and try to keep an open mind and give it another shot. At the same time I don't really want to have a bunch of tiny scripts all doing their own thing either. I think I will either use classes and interfaces or classes with inheritance. I used to love inheritance at one time. Long ago now like 17 or more years ago. I do think that is a great approach for game development.

    Then I favored composition working with the mindset of classes having behaviors. And the past many years I have favored working with interfaces over concrete implementations and found interfaces worked excellent for supporting the behaviors approach.

    So at this point I don't know what in hell I will do exactly. I might try the class hiearchy approach I might try behaviors. In either case I am sure I will use interfaces.

    At the same time... I think there is value in using the Editor. So I want to get into using this Editor as much as I can.

    Before I can do anything I need to take the time to design the game. Right now I just know I would like to make a little rpg. A small village. A hamlet. Maybe a forest nearby. A few buildings. Some NPCs. A few quests. A few monsters to battle. A few items to collect. But what exactly I have no idea other than people will be cubes and enemies will be pyramids and buildings will be cubes and so forth. It's just about screwing around and having fun more than anything.
     
  23. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    Why is this an "or" thing?
     
    Dustin-Horne and GarBenjamin like this.
  24. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Because in my head at the moment I was writing that I was thinking that and I wrote exactly what I was thinking. Because I was coming from the angle of seeing it as Composition or Inheritance. "Has a" or "is a". Of course, we know it is not limited black & white like that. lol You seem to be focusing on each word like I am writing a software engineering white paper. I am just rambling on writing random thoughts as they come into my mind.
     
  25. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    These are some examples of how I usually approach scene organization and communication. This has got like two little scenes and a couple classes.

    They're both pretty minimal examples that do nothing/little.

    In all honesty I prefer the 'QuickReference' approach, but I do use the pub/sub as well to move data around or notify things.

    I included a bunch of notes in the scenes themselves, so check if interested. The pub/sub example is set up to notify subscribers of scene "init" as an example.
     

    Attached Files:

  26. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    I really really want to use the editor for lots of things, but then there are times where I am changing code or renaming things, and now every component of that script now lost all its references / values and I have to now remember how everything was set...
    Its come to a point where hardcoding things in (for my simple game) has become more welcoming.
     
    GarBenjamin likes this.
  27. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    I'm reading it like a programmer... words have specific meanings, and the subtleties of those meanings are really important. For instance, in a programming conversation the distinction between a "class" and an "object" is fundamental, as is the difference between "and" and "or".
     
  28. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Fair enough. A lot of programmers are this way. I am definitely not a typical one. When I am talking to the machine I focus on being more explicit but here in a forum or general conversation I just don't get wrapped up in that stuff. Especially with other programmers because they should already understand what I am saying. If one way makes no sense and another way does then it should be crystal clear. At least that is how I see it.

    I also tend to view this stuff as being quite simple. So when I do talk about it generally speak in a very simple manner. I just had an interview a couple of weeks ago for a different job in fact and the interviewer told me "I must say I really like the way you answer breaking all of this technical mumble jumble down into simple descriptions."

    It's just how I am wired I guess. I've never cared much for the technical jargon and prefer a more conversational... conversation. One of the questions was to explain a Clustered Index and my reply was along the lines of "it simply refers to the way the data and index are actually stored. The data is stored ordered by its indexed values and that order itself is the index. This is why you can only have one Clustered Index on a table." Although I think it was a simpler & shorter than that. I see no need to get into leaf nodes and all of the other mumble jumble. Nobody really needs to know all of these details.
     
    Last edited: Mar 24, 2017
  29. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    Even when you're in a forum talking to people about talking to a machine?
     
  30. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,850
    But instead of just accepting the Unity paradigm with the Editor Inspector being your switchboard hub and an at-a-glance displayer of game state and play status you prattle on about using classes, inheritance and interfaces and other technical mumbo jumbo that does not get you understanding..just derive everything from MonoBehaviour, use the Inspector for drag and drop references at init and get on with it. You can alter those references in Start or Update or whenever a condition is met. I am writing a huge suite of VR collaborative project management visualization tools for a prominent AEC industry software company and I am simply writing a slew of self contained components. Sub-controllers to display data that report their state to master controllers and managers that hold data. I can make 1000 components this way and it will not get out of hand and I only turn on what i need from the master controller. I know everything about the state at any time and can query anything i need to make the bells and whistles work. They were doing what you do and the whole Unity component thing to them could not be understood. I send them tech updates daily and they get what i am doing and are very pleased with the direction.
     
    frosted, zenGarden and GarBenjamin like this.
  31. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    @ippdev I'm getting on with it! lol When someone replies to me I generally reply back so that is what kept the rambling going.

    Sub-controllers, master-controllers and managers sounds like an interesting architecture. Managers are almost always a helpful thing I think. And I don't doubt you're more proficient in Unity development than a lot of folks.

    I'm just gonna have fun with it. Think tonight I'll spend a bit of time on laying out a little cube-based settlement then start bringing it to life.
     
  32. zenGarden

    zenGarden

    Joined:
    Mar 30, 2013
    Posts:
    4,538
    This is how i do in some way, general managers controlling groups of other managers that controls controllers that behave in the 3D scene. This is usefull to activate and deactivate parts of the 3D world by only activating managers that manages region groups of the 3D world.
    As you said no need for mumbo jumbo.
     
    GarBenjamin likes this.
  33. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Ha ha. Well see this is the kind of thing I am after. Just curious how others (who have used it a fairly long time) are approaching dev in Unity. It sounds like you two are using a style close to the way I do things. Not everyone does of course. Like up above we see a completely different approach avoiding managers completely.

    There are pros and cons both ways but what I was interested in was hearing from people who have used Unity for many years because way too many of the people writing articles and making videos on Unity game dev seem to have a total of 6 months or less of development experience. It becomes a blind leading the blind kind of thing. At the other extreme, and just as bad, we have "proper" software engineers over engineering everything simply due to their belief in and love of abstraction, buzzwords and complexity.
     
    Last edited: Mar 24, 2017
  34. zenGarden

    zenGarden

    Joined:
    Mar 30, 2013
    Posts:
    4,538
    It's related to the game genre you want to make.Managing a medium to big world is different from some linear game with levels.You are free to do it the way you want, and sometimes you'll learn more from real projects than tutorials on how to manage all stuff.
    Principles like objects pooling, regions activation deactivation , managers etc ... are some times more valuable to learn than some language arctifacts.
     
    Last edited: Mar 24, 2017
    angrypenguin and GarBenjamin like this.
  35. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Code (csharp):
    1.  
    2. _cameraHook = GameObject.Find("CameraHook");
    3. FollowPlayer4 script = _cameraHook.transform.parent.GetComponent<FollowPlayer4>();
    4.  
    A lot of people might freak out at this code "omg dependencies". I have no problem with dependencies or breaking law of demeter or whatever. The problem is these dependencies are hidden in the code and I need to dig around too much to figure out what's happening and where everything is.

    This can go the other way, as much as Find("CameraHook") means I need to dig around in the code to figure out how things are rigged, I also find Unity Events difficult to use, since I need to dig around in the hierarchy to figure out what the handler is. The problem here is that most of the time, the unity events are hidden in some deep nesting in the hierarchy. Again, I need to dig around.

    I don't want to dig, I don't want to engage my brain, I don't really care if it's in code or inspector, but either way I want the clearest, simplest description of what the dependencies are and how the different parts are glued together.
     
    Billy4184 and GarBenjamin like this.
  36. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Well of course the principles are very important. The other stuff is just a way to abstract and manage other areas of concern. But I completely agree the core the fundamentals are always the most important. And those things carry from language to language and engine to engine.

    Also agree completely on hands on. I do loads of experiments just trying things out. Kind of exploratory development / prototyping. Done up front to get a feel for the scope of work, what are the easiest ways to implement things and what are the pitfalls to watch out for, etc. That is where domain knowledge comes in handy. Research and the exploratory development gain that domain specific knowledge.
     
  37. Billy4184

    Billy4184

    Joined:
    Jul 7, 2014
    Posts:
    6,010
    I'm in the habit now of using static references for everything that isn't going to be used more than one at a time, because I'm sick to death of doing component searches on multitudes of scripts. Maybe I use it 'too much' who knows, but it sure works for me.
     
    GarBenjamin likes this.
  38. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    check out the 'quickreference' example in the sample project i posted above - all dependencies are just listed out.

    In actual practice, I have found this approach (or variations) to be the easiest to work with all things considered.
     
  39. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Yeah I agree although LoD is all in how a person view things, right? In this case we could say the camera and the player should be aware of each other should talk to each other because they are working together. It'd be like a friend telling me hey I am going on a hike to try and find (something) and I want you to video it.

    There is an obvious connection between us. I am tied to my friend needing to stay within a certain distance to record his journey. That is how I view the camera and player GOs in this case. But otherwise... in general I don't care much for all of the terminology. At least not just for the sake of the terminology itself.

    Anyway, I try to focus more on the fundamentals. Because I believe the fundamentals are more important than anything else. In this case it would be the fundamental of consistency. Meaning much of the problem you describe would be solved if the project is always doing things in the same way. If a game actually used the 4 different patterns I used in my project that would be a bad thing IMO. Imagine that scaled out to every task in a project.

    So the basics are so important. More important than the fancy stuff.
     
  40. Billy4184

    Billy4184

    Joined:
    Jul 7, 2014
    Posts:
    6,010
    That's how I usually do it, if I understood correctly - by having a single 'manager' object with scripts attached, but I ended up dragging stuff across scenes with DontDestroyOnLoad, and having to create scenemanager.sceneloaded events for loads of scripts. Much easier to just have a static reference in that case.
     
  41. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Again, to be clear - I don't mean that the dependencies are a problem (others might, I'm not). I think you're actually overthinking this.

    Here's the problem, I open up the scene a couple months after writing the code, at this moment. Imagine:
    • I don't remember how anything works.
    • I don't remember what the classes or gameobjects are named.
    • I am making a very small change, and I don't want to think.
    How do I know that the camera controller is assigning its own target?

    I simply need to guess.

    What if I guess that the player object is assigning itself to the camera.


    Then I open the player class, and I need to dig through all the code there and I still can't figure out how the camera target is being assigned. After I spend a couple minutes going through the player code, and find nothing, I need to take another guess.

    The problem isn't the dependencies themselves, the problem is the dependencies are hidden and take too much work to figure out.
     
    Ryiah and GarBenjamin like this.
  42. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Oh yeah I agree completely. And this is again where the fundamentals come into play. In this case documentation. Generally whenever I have a dependency (of any kind loosely coupled or tightly coupled) I document it.

    Actually I think this documentation is even more important in the case of abstract very loosely coupled code. In this case the code of both player and camera would have documentation describing how it works. I also may create a little text file just describing the architecture in general. Tech Notes.

    I didn't bother with it for this tiny example... started to then stopped. Maybe I will update it accordingly. This all just wasn't something I aapproached in a serious manner more just to get some discussion going and see how other people are doing these things.

    It is interesting to see so many different approaches to what has to be one of the most basic tasks in Unity game dev.
     
    Last edited: Mar 24, 2017
  43. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    But wouldn't it be better if you didn't need any docs? What if the code or structure guides you and helps you figure everything out at a glance to the point where you simply don't have to think about it or read any of the code.

    Haven't you ever approached a new project (or even some very old code you wrote) and said "oh god, I can't figure out where this value is being set"?

    Doesn't that suck? Why not just organize things such that the code guides you to figuring everything out.
     
    Ryiah and GarBenjamin like this.
  44. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    One way you *can* (depending on scenario) handle this is to use a service manager. Let the service manager be static, but your implementations not... this allows you to swap out the implementations for testing purposes. Here are a couple of examples, and you can also use them in combination:

    Code (csharp):
    1.  
    2. //In this example, all service types are known up front
    3. public static class ServiceManager
    4. {
    5.       public static IInventoryService InventoryService {get; set;}
    6.       public static IScoringService ScoringService {get; set;}
    7. }
    8.  
    Now in the above, you have to initialize those services... you can initialize a default instance in the static constructor, or when your game starts. When Unit Testing you can also swap those out for fakes which is great especially if they rely on external services.

    Here's another approach that's similar in concept but allows you to dynamically register and unregister services:

    Code (csharp):
    1.  
    2. public static class ServiceManager
    3. {
    4.       private static Dictionary<Type, object> _registrations;
    5.  
    6.       static ServiceManager()
    7.       {
    8.             _registrations = new Dictionary<Type, object>();
    9.       }
    10.  
    11.       public static void RegisterService<T>(T serviceInstance) where T : class
    12.       {
    13.             var serviceType = typeof(T);
    14.             if(_registrations.ContainsKey(serviceType))
    15.                   _registrations[serviceType] = serviceInstance;
    16.             else
    17.                   _registrations.Add(serviceType, serviceInstance);
    18.       }
    19.  
    20.       public static void UnregisterService<T>() where T : class
    21.       {
    22.             var serviceType = typeof(T);
    23.             if(_registrations.ContainsKey(serviceType))
    24.             {
    25.                   var instance = _registrations[serviceType];
    26.                   if(instance is IDisposable)
    27.                         instance.Dispose();
    28.    
    29.                   _registrations.Remove(serviceType);
    30.             }
    31.       }
    32.  
    33.       public static T GetService<T>() where T : class
    34.       {
    35.             var serviceType = typeof(T);
    36.  
    37.             //Could also use TryGetValue here which does the same internally
    38.             return _registrations.ContainsKey(serviceType)
    39.                   ? _registrations[serviceType] as T
    40.                   :  null;
    41.       }
    42.  
    43.       public static bool IsRegistred<T>() where T : class
    44.       {
    45.             return _registrations.ContainsKey(typeof(T));
    46.       }
    47.  
    48.  
    49. }
    **Note - I fixed a couple but apologize for any typos in the code above... I wrote it in the forum, but you get the gist.
     
    Last edited: Mar 24, 2017
    LaneFox and GarBenjamin like this.
  45. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Absolutely it is better. I've always strived to write self-documenting code and structure things in a way that makes it easy to follow. When I revisit my projects or just sections of my code from a few years ago for me it is very easy to follow. Of course, it may still take a bit of time to load my brain "get back into it fully" depending on the scope of what I need to do now.

    The problem is there is no one way to do this that will work to the same degree for all people. This is why I focus on the fundamentals. Because using meaningful names, adding some comments in the code (well when written correctly lol), being consistent (not doing something one way this time, a different way next time) and providing a high level map of architecture is something that will benefit all people. And does not require a huge amount of time. The tech notes often I just put in the code.... a header on the main item. Other classes may have documentation at the top explaining what they are used for, by who, etc.

    I'm definitely not perfect at it but it is definitely something that is on my mind.
     
  46. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,462
    That seems like a little more organized approach to what I generally do right now. You'll definitely have to initialize them but there's never going to be a perfect auto-magic way around that, I think at some point some degree of responsibility has to be assumed and if stuff breaks, well, then you did something wrong and it broke for good reason.
     
    Dustin-Horne and GarBenjamin like this.
  47. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    It's worth noting that this is essentially the same way a simple pub/sub system works, only you're passing around interfaces instead of messages.
     
  48. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Sure, doesn't have to be interfaces though.
     
  49. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    ServiceManager/Locator is useful I agree. It wraps up all of the references inside a single connection point. That's what I was getting at in the OP about setting up a single class containing all of the Editor hard-wired references. A ReferenceManager.

    At least then all of these editor wired references are stored in a single class. One connection point. That is a common theme for me because I view this pattern as simplicity; easy maintenance.
     
    Last edited: Mar 24, 2017
  50. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Exactly. The service/locator example here is really just a thin wrapper on Dictionary<Type, Object>. The functionality itself is kind of irrelevant though, the underlying value in the approach is that it gives you a nice centralized location to look for stuff.

    "Find Usage" on register will give you a list of everywhere a service is registered for example. Having simple, reliable ways like this to find how things are glued together is insanely valuable.

    I still think the real value add of most dependency injection systems is really this. The configuration chunk lets you set up a single centralized and clear location where you can set things up and later use this as a reference. The code/config often acts as its own documentation, it's own "quick reference guide".
     
    Last edited: Mar 24, 2017
    GarBenjamin likes this.