Search Unity

Better object referencing than drag and dropping GameObjects to components?

Discussion in 'Editor & General Support' started by arvzg, Apr 11, 2014.

  1. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    Hi guys,

    This is a pretty common question that I've seen asked a fair few times, but one I've never really gotten a good answer for.

    There are 2 main ways I know of for referencing other components or other GameObjects from a script.

    Code (csharp):
    1. GameObject.Find("object").GetComponent<component>();
    The problem with this is that it is object-name dependent. If in the future I decide that my Player object is going to now be called "Cat", I would have to go into my scripts and change them one by one. Also, I hear GameObject.Find() is a fairly expensive operation - though I would mostly be using it in the Start() function and never in Update().


    Code (csharp):
    1. public GameObject object //drag and drop object in the editor
    The problem with this way is if you have multiple components referencing this 1 object (for example, the player object) and you need to delete the object or change the reference to another object for any reason you would need to manually drag and drop the object to every single component. (Multi-object editing does help, but still..). This method doesn't work in run-time either obviously.

    ---

    I suppose a third option would be to use GameObject.FindWithTag("Player"); in the Start() function. Would this operation be cheaper than GameObject.Find()?

    A method I thought of was to use a class which just contains references to every object you need a reference to and have every other script use the reference in that class, that way you only need to drag and drop the reference to the objects once to that script component only. This is probably a known design pattern, but I don't know what it is called
     
    Last edited: Apr 11, 2014
  2. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    They're Unity scene specific ways. You can also traverse the scene via transform's, as they all reference their parents and children, so it doesn't have to be name dependent.

    You can also do a lot of the reference hookup in prefabs.

    But remember that you've got a whole Mono/.NET framework at your disposal. You can do any software design stuff in Unity that you can do anywhere else. I think the reason that there's no answer to this is that the question assumes that Unity is some kind of special case for this stuff, where it really isn't. Whatever approach to object management you might use elsewhere will typically also work here.
     
  3. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    Do you mean using something like transform.parent or Component.GetComponentInChildren() ?

    This would probably work as long as the objects are in a parental hierarchy, but not if they're seperate gameobjects

    I suppose this is what I'm really asking - what are some good object management patterns that work well in Unity?
     
  4. Smooth-P

    Smooth-P

    Joined:
    Sep 15, 2012
    Posts:
    214
    Gonna echo angrypenguin here, and also note that while doing things the Unity way is generally simple in terms of up front costs, in most cases it will result in fragile code with high maintenance costs. Applying solid design principles and leveraging language features trumps reliance on Unity APIs.
     
  5. minionnz

    minionnz

    Joined:
    Jan 29, 2013
    Posts:
    391
    Just wondering: would some kind of Dependency Injection framework for Unity be a good idea? I'd imagine the performance cost to be fairly minimal, with most of the injection happening up front on start up. With some basic modifications, it could be updated to use the transform hierarchy as well so parent/child relationships are taken into account.

    Injection could also be done at Edit time as the scene is updated, meaning no drag-and-dropping required
     
    Last edited: Apr 11, 2014
  6. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    That's just it - what are these principles and language features that I can use? :) I don't mean to ask for a whole lesson in C# / solid design tutorial, just a point in the right direction

    There is StrangeIOC you could use http://strangeioc.github.io/strangeioc/
     
  7. minionnz

    minionnz

    Joined:
    Jan 29, 2013
    Posts:
    391
    Wow, had no idea this existed and I might start using it in my code. The problem with Unity is that I tend to write code that is tightly coupled with other components. The examples definitely seem a much better approach:
    Code (csharp):
    1.  
    2. public class MyShip : MonoBehaviour
    3.  {
    4.      private FacebookService facebook;
    5.      private int myScore;
    6.      
    7.      void Awake()
    8.      {
    9.           facebook = FacebookService.getInstance();
    10.      }
    11.      
    12.      void onDie()
    13.      {
    14.           if (score > highScore)
    15.                facebook.PostScore(myScore);
    16.      }
    17. }
    18.  
    vs
    Code (csharp):
    1.  
    2. public class MyShip : MonoBehaviour
    3. {
    4.    void onDie()
    5.    {
    6.       dispatcher.Dispatch(GameEvent.SHIP_DEAD);
    7.    }
    8. }
    9.  
    I suppose it could be done with SendMessage, but I think it's a great approach to developing components - The Ship shouldn't care or know what happens when it dies, only that it has died.
     
  8. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    That's kind of what I mean. But... why do you want one GO's components to have direct references to components in another GO? That sounds like spaghetti to me!

    My general policy is that components only ever directly modify their own GameObject, local components, and children. Anything outside of that scope is handled through a more appropriate system, typically loosely-coupled events.
     
  9. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    The way I've got things set up right now is I have a Player object (an object with a rigidbody2d and 1 script attached, and a child gameobject of the Player with the sprite attached) and a GameManager sort of object that manages things such as what happens when the player has died, so it needs to know when the player died and act accordingly - therefore it needs a reference to the Player object

    Am I doing it wrong?
     
  10. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    How does your GameManager object currently know that the Player object has died? Is it sitting there polling some variables every frame?

    Why does it "need" a reference to the player?

    How else could you let it know that the player has died?
     
  11. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    The player "dies" when his Y value ever gets below a certain point, so yes actually it is polling player.transform.position at every frame in Update() which now that I think about it is obviously not very optimal.

    I suppose the better way is to have the player detect its own death and let GameManager know that it has died. But this way means the player needs to have a reference to GameManager, so we're sort of back to the original problem
     
  12. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Are you?

    Check out event systems, as one approach. Basically this means having some way for your Player object to send a message "I just died!", and some way for any other objects that could be interested to listen for that event. That way things only have to be aware of events, they don't have to care how they are generated, where they come from, or what is listening.

    Also check out the concepts of loose coupling and related topics. Your components should not need intimate knowledge of other areas of your code or architecture to function.
     
    Last edited: Apr 11, 2014
    LGW_The7thCrest likes this.
  13. TrentSterling

    TrentSterling

    Joined:
    Jan 4, 2013
    Posts:
    99
    Loose coupling and events is surely the proper way to do what you're looking for- but as an alternative answer to your original question...

    [SUP][SUP]You can use tags to find a GameObject. Tag your GameObject as Player, and you can name the GameObject Cat, Dog, or Truck. ;)[/SUP][/SUP]

    EDIT: Apparently I didn't read the entire OP. I feel dumb now. :confused:
     
    Last edited: Apr 11, 2014
    LGW_The7thCrest likes this.
  14. cynic

    cynic

    Joined:
    May 21, 2013
    Posts:
    142
    Exactly right.

    It is not that good an idea to write such kind of circular references to objects. Your code will end up spaghetti and will become hard to read, understand and debug. Most importantly however, it will become needlessly long and complex, because you end up cross referencing objects or writing endless amounts of methods in your game manager object just to handle messages between child objects around.

    I think using event systems is the best way to go here. Your objects shouldn't have direct dependencies in order to respond to certain gameplay mechanics. Each object should care about itself and its own function. When your player dies, you dispatch an event and handle the player object dying. That's as much as the player is concerned. Thus, there doesn't have to be any object listening for the player dying for the player object to function. However, other objects can listen to this notification if they are interested in it. You could have your monsters react to this notification, which makes them laugh frantically and return to their original path. You could have a tree object listening and reacting by starting to cry and your game manager object could start preparing resetting the scene to the last checkpoint.

    It is up to you how to implement this really, but I'm sure you're starting to see the advantage of using events. The example above is super simple, but imagine how complicated it would get in your code to pass the appropriate messages along all those objects your player would need to actually know about, in the correct order, just to achieve something like that.
     
    LGW_The7thCrest likes this.
  15. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    GameObject.Find has to execute before it happens, and that particular command isn't very fast. If you looked up everything like that, on mobile, it will add a perhaps sizeable delay before you can start playing the actual game. So dragging and dropping game objects and transforms into the Inspector is the way to go. You mentioned some of this :)

    I usually do a mix where I drag the containers into the inspectors, then find the objects under it, which isn't as expensive (FindChild calls only, not Find).

    If you're worried about circular dependencies, you can create some singleton scripts that just keep track of a bunch of related game objects by reference (like all enemies of a certain type on one script and all weapons on another). Then you can access those game objects via static methods on that script that return the singleton's instance, from ANY other script. Voila, no more problems. This is a trick I figured out after awhile. You hinted at this. I don't know if there's a name or if this is a pattern per se, but it's pretty useful just in its organizational purposes.
     
    Last edited: Apr 11, 2014
  16. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    I disagree. My Player script has no reference to GameManager (I call mine GameTracker). You can just make STATIC methods in GameManager for Player to call when things happen. And other objects can call other static methods in GameManager. The static methods can access the non-static fields in the GameManager with an "Instance" property you can add with lazy lookup in a few lines.

    I can paste the "Instance" property code here if you've not seen it before.
     
    Last edited: Apr 11, 2014
  17. Smooth-P

    Smooth-P

    Joined:
    Sep 15, 2012
    Posts:
    214
    You don't even really need static methods, just a public static field / property for the current instance.

    Of course, Unity's fundamental GO / component model is inherently race condition prone, so you have to take care that any components you access this way are instantiated in the proper order.

    Also, you can't directly raise an event from outside the declaring class, so I use simple structs for my events, like:

    Code (csharp):
    1.  
    2. public struct GenericEvent {
    3.     public event DelegateAction Handle;
    4.     public void Raise() { var handle = Handle; if (handle != null) { handle(); } }
    5. }
    6.  
    7. public struct GenericEvent<T1> {
    8.     public event DelegateAction<T1> Handle;
    9.     public void Raise(T1 t1) { var handle = Handle; if (handle != null) { handle(t1); } }
    10. }
    11.  
    12. public struct GenericEvent<T1, T2> {
    13.     public event DelegateAction<T1, T2> Handle;
    14.     public void Raise(T1 t1, T2 t2) { var handle = Handle; if (handle != null) { handle(t1, t2); } }
    15. }
    16.  
    17. // etc
    18.  
    I'm currently cleaning up and documenting some of my foundation code for release as a free to use, free to redistribute asset, but if anyone wants to download the current version it's available here.
     
    Last edited: Apr 11, 2014
  18. cynic

    cynic

    Joined:
    May 21, 2013
    Posts:
    142
    Aye, the pattern is called Singleton. ;)

    Sure, this is also a way that works in many situations. The only disadvantage is, that all objects that might need to know about something happening need to be called explicitly and therefore a plater script would need to know which methods on which objects it needs to call. Can do that, but not as flexible as event dispatching.
     
    LGW_The7thCrest likes this.
  19. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    Thanks guys! Yes i think events and delegates is exactly what i was looking for! I had heard about them nefore but never really looked into it. I have certainly ended up with spaghetti code in earlier projects implementing references using gameobject.find and editor drag and drop. I hope this will help me write better code from now on
     
  20. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    If you want to handle many Object references of a specific type dynamically at runtime (such as Players) here is one way you can do it.

    Create a Manager object that will always exist in your game/scene. On the manager component script you will make a List of Transforms or something like that.

    The objects that you want to track should always point to the Manager object (prefabbed)
    In Start() on the Objects you want to add, you would then call Object.Manager.GetComponent<ManagerScriptName>().Add(ObjectToAdd) OR you can use SendMessage("Add", objectToAdd) but I think that's a bit slower.

    When you spawn an Object it will automatically add itself to your Manager List and you can always access this from Object.Manager.GetComponent<ManagerScriptName>().ListName[index]

    You now have a way to set a reference to specific object types (defined in your code) dynamically at runtime. You can find the index/remove/whatever you want to these in the List on your Manager.


    IN YOUR CASE: Add a List of Transforms to your GameManger named something like PlayerList. Add reference to GameManager on Player. On your Player script in Start() Add your player to the List in Manager. Player death should be handled by the player though, not the GameManager. Your Player script likely has an Update function anyways so you can check death conditions here.
     
    Last edited: Apr 11, 2014
  21. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Delegates are one way to implement event systems, but they're not the only way.

    Also, drag-and-drop in the Editor shouldn't lead to spaghetti. If it does then you're not using the best tool for the job. Your decision making should go along the lines of "Do these things need to be hooked up at all?" and then, if the answer is yes, "What is the appropriate tool to use to hook them up?"

    Broadly speaking, then, you have three broad options.

    The first is using an Inspector interface for design-time hookups. The strength of this is that anyone in the Editor can change the hookups. The downside is that... anyone in the Editor can change the hookups. ;) So, for any given relationship, ask yourself things like "Can this easily be broken if I expose it in the Inspector?" and "What benefits does exposing this in the Editor have?" Generally speaking, if you're going to expose something then it should be able to accept and handle arbitrary hookups, as well as allowing you to make it very clear what's not right. For example, lets say I have a bunch of triggers set up to allow different camera views of the scene, each with a reference to a CameraLocation or something. If I haven't assigned a CameraLocation in the Editor then they should make sure I know that the moment I try to run the scene. Otherwise, they should be able to hum along nicely regardless of which CameraLocation I drop in the slot.

    The second is automating the hookup. Basically, if the relationship is one you don't want messed with in the Editor then don't expose it in the Editor! In my experience, if an automated hookup is non-trivial to get right then there's an overarching design issue you should look at first. Otherwise, a GetComponent<> or similar should do the trick. Also remember that there's [RequireComponent(...)] and so on to make life easy in the Editor. If you have to automate a hookup to an object that's not easily accessible (ie: requires knowing the name of arbitrary other objects, or having the scene set up just so, or anything else that makes you groan) then you should question whether the relationship should be direct at all. Can this be done through an event or other intermediary? Can you do this without the two sets of objects having intimate knowledge of one another? For an example of this, a Player might have a bunch of Weapons that are set up in the Editor as child GameObjects with scripts that derive from the Weapon class. To hook up an internal array of those I can just call GetComponentsInChildren<Weapon>()... that's all, done. On the other hand, the Player should almost certainly not have an internal reference to, say, a light switch found in the environment.

    The third is custom tools. Sometimes things need human brains to make things nice but there's also stuff that you want the computer to take care of. So you create tools that allow a non-coder to define things at as high a level as possible while making sure they don't break the rules. For instance, lets think about that Weapon list again. Lets say that each Weapon has a weight and the Weapons carried by a player can't exceed some total weight. On one hand you could check at startup and print an error if the rules are broken, but then the designer needs to apply brain power to solve it, and it's going to require a bit of manual back-and-forth. Not the end of the world, but isn't this what computers are for? So, you could easily make a custom GUI to handle assembling the Weapon list in the first place, displaying each Weapon's weight and remaining available weight, and only allowing designers to add Weapons that fit. Rather than back-and-forth with an error message it's now a no-brainer config that allows for designer flexibility and just takes care of its own hookup. Nice!
     
  22. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    How about for something like this?

    An endless runner game where the ground is being generated as the player goes along. The ground needs to be deactivated so that it can go back to the object pool to be used again later on. The ground needs to know how far away it is from the player to know when it is an appropriate time to deactivate itself. How would you give reference to the player without using FindWithTag or Find? You obviously can't drag and drop to the editor at runtime. Events isn't going to work in this situation either
     
  23. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    Personally I'd go with a PoolManager that has a singleton-like Instance reference. There's only one of these it can find the player on Start() or by assignment. Individual blocks can then do something like:

    PoolManager.Instance.CheckReturnToPool(this);

    But really this looks like a lot of overthinking. Use events when things need to be decoupled and represent things that "happen". Use GetComponent if you need to get parts of something. Use Singleton like instance references for manager type classes. Or use whichever patterns you are comfortable with!

    More importantly do it, do it now, and refactor if you need to. The time it takes to change between events and GetComponent or SendMessage or whatever is minuscule in comparison to the overall development effort.

    - John A
     
  24. seitor

    seitor

    Joined:
    Feb 18, 2012
    Posts:
    36
    I have one game being developed this way. A List represent the tiles(I use enum to represent different tile), when tile go out of camera view, it go back to object pool.

    For level design, I drag and drop as usual. When I am done with level design, I save the tile data back into list , at run time I load this list.

    Edit : I divide one level into many zones, I display current zone, next zone and previous zone all the time, when player step into next zone, I use a coroutine to put objects in previous zone back to object pool
     
    Last edited: Apr 13, 2014
    Pikarie likes this.
  25. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,369
    In case anyone is looking for bullet prof ways to design your game code structure: http://gameprogrammingpatterns.com/
    They also includes a pretty good explanation of Singleton, Observer and States design patterns (those are widely used in our framework).
     
  26. Aurecon_Unity

    Aurecon_Unity

    Joined:
    Jul 6, 2011
    Posts:
    241
    I'd just like to say that this thread is really fascinating for beginner programmers and I wish there were more like it (discussing issues / methods in a broader sense).

    Thanks everyone!
     
    diliupg likes this.
  27. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    So, you're measuring the distance between two GameObjects and resetting one based on that? There's nothing about that which isn't generic and can't be coded as such.

    Why care about Players and world chunks when what really matters is a pair of Transforms? ;)

    And if you were to describe the resetting of world chunks as responsibilities, would it sound appropriate to assign those responsibilities to either the Player or the world chunks themselves? I'd say no, you should have some 3rd party for that. And since that third party only has to worry about Transforms, there's no reason you couldn't make it super generic and reusable - the only thing that would have to change in different usages is probably the criteria used to decide when something gets reset.
     
  28. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I'm really glad that someone's getting something out of it. The response to these things is almost always "the way I'm already doing it works, so I ain't changin'!"

    But whether or not something works shouldn't be the question. It should be "How well does it work?" and then "How can I make it work better next time?"
     
  29. minionnz

    minionnz

    Joined:
    Jan 29, 2013
    Posts:
    391
    Also generic code should be easier to optimize/change without worrying about breaking everything.

    I wouldn't have an Update on the tile if that's what all it was doing (checking distance and resetting) - I'd have a single gameobject that is responsible for creating and placing/removing tiles and just use some kind of queue system - if we can't display more than 10 tiles on screen at once anyway, do we really need to know what the actual distance between the first tile and the player is? It'd be up to the TileManager gameobject to remove/add tiles, so it'd already have references to each tile - no need for GameObject.Find at all.
     
  30. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    I see, so what you're saying is I should have a third object that handles deactivation of the ground objects.But even if it does only need to worry about the transforms, it needs to be able to refer to the transform.

    The way I ended up doing it is by using a singleton PlayerScript - so each ground object can know the location of the player by using
    Code (csharp):
    1. PlayerScript.instance.transform.position
    The ground itself has a GroundScript that queries the location of the player in Update(). If it is a certain distance away then it deactivates itself and hence goes back to the object pool. This is probably not optimal though and I plan to make it go by camera view instead to handle different resolutions.

    Is this not a good way?

    Agreed, I'm somewhat obsessed with the idea of "Doing it the best way"
     
  31. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Ha, that just made my day; who cares if something works, haha.

    Why?? I can't even begin to explain what a waste of time that is. "The best way"? What on earth does that even mean? I could structure an FPS 1,000,000 different ways and each one would be completely valid.
     
  32. arvzg

    arvzg

    Joined:
    Jun 28, 2009
    Posts:
    619
    Because I'm not doing this to beat a horse 1000 times over. I want to improve, I want to learn. I'll do something one way and always figure out that I should have done it another way later, and by then it's way too late to make the changes

    The "Best" way is probably a terrible word for it, what I really mean is "A way that is not horrible". Unfortunately the easiest ways are often the most horrible

    Having said that it's actually somewhat of a weakness of mine. I could have made 100 games by now if I didn't care so much, so that's a huge downside
     
  33. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    @arvzg: I'm tellin' you man, you're sucked in a trap. It's an endless loop, and you will never feel satisfied until you accept that no way is "right".

    Hmm, I disagree. I say absolutely go for the easy ways. If you can get the job done easily, then what's the problem?

    Exactly my point. You're investing so much energy into something that doesn't even exist. As the song says, "Let it go, let it go, Turn away and slam the door".
     
  34. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Haha, seriously, quote the whole thing next time.

    On the topic of "the best way", it's not at all a waste of time. It's just that "the best way" has to factor in resource usage, including time. A perfect result that you can never implement is of course not the "best" outcome for anyone, because it's no outcome. On the other hand, going for quick and dirty stuff because it's initially easy also often isn't best because even small amounts of complexity lead to large amounts of wasted time.

    "The best way" is the one where you most effectively achieve the required results. Technical things are only a small part of that.

    All I personally care about is a) getting things shipped and b) doing better than last time. I don't strive for perfection, but if I make a mistake multiple times or I find that something is a repeated time sink... then I'm doing it wrong, and then I know I need to change something. If something looks like it's going to take ages or be particularly error prone then I try to automate it as much as possible. And so on. If you're not constantly trying to do better next time than you did this time then you're quite simply wasting your time, because you're not improving your productivity as much as you could be. That's what "the best way" is about.
     
  35. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Define "easy".

    A few years ago I was working with a guy who decided to implement a rather large decision tree by writing a stack of nested "if {...}" statements over several hundred lines of code. I asked why he'd done it. He said it was easy, there's no point overcomplicating things is there?

    Then I gave him a variation requested by the client.

    Suddenly it wasn't so easy any more.

    Sometimes "the easy way" is the same as "the most immediately straightforward way". Sometimes it's not. If all you're thinking about is code and the straightest, most easy way from A to B without considering the bigger picture you'll often end up costing far more time in the long run.
     
  36. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    I don't see it this way. When working on a project, I know what problems I solved last project and what problems I need to solve in this project. If I need to solve the same problem then I'll go back, open my old project and copy over some scripts.
     
  37. NTDC-DEV

    NTDC-DEV

    Joined:
    Jul 22, 2010
    Posts:
    593
    Thank you for this.
     
  38. Smooth-P

    Smooth-P

    Joined:
    Sep 15, 2012
    Posts:
    214
    And thus, taking the time to write code that cleanly solves a wider class of problems is often far more productive in the long run than spending less time on code that is specific to the issue at hand.

    A good coder doesn't just produce code, he/she produces code and ideas that increase the productivity of the entire team. In fact, a good coder actively targets productivity growth, not specific issues, because unless your company is going to fail unless you get that issue done immediately, increasing productivity has way, way, way more value than more quickly completing any specific task.
     
  39. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    I agree with Smooth P in that trying to write code clearly and cleanly should be top priority.

    Also another thing I feel is worth mentioning is pattern recognition. Software dev is largely about creating patterns, and sticking to them (being consistent). The question shouldn't be "Is it right" but more should be "What's the pattern" or "Is the pattern obvious".
     
  40. Gigiwoo

    Gigiwoo

    Joined:
    Mar 16, 2011
    Posts:
    2,981
    Hear that? Somewhere, an astronaut architect just jumped for joy! ... "super generic and reusable" is the kind of phrase an over-eager developer utters right before he creates a "SYSTEM"(tm) which becomes a permanent, unmaintainable, behemoth buried deep within the software. I prefer simplicity. Period.

    Gigi
     
  41. Gigiwoo

    Gigiwoo

    Joined:
    Mar 16, 2011
    Posts:
    2,981
    A TileMananger would be a simple solution that sacrifices 'generic', 'all-purpose', and 'reusable', in order to become 'simple', 'easy-to-maintain', and 'implementable'. Maintain 2 lists - tileActiveList (sorted by creation/distance) and tileObjectPool. At startup, instantiate from a perfab and add to tileObjectPool. Then, at runtime, call GetNextAvailableTile(), which pops from tileObjectPool (create more as needed) and adds it to tileActiveList. Then, in Update(), compare the distance of a few items in tileActiveList, pop if too far, and add back to tileObjectPool.

    You could make that pool behavior generic. Though, even that might be over-complicating things... Having spent 7 years as co-lead for a large open source gaming engine (Delta3D.org), I found countless examples of innocuous sounding 'systems' that were hard to maintain.

    Simple is rarely a bad investment - and it costs less.

    Gigi
     
    Last edited: Apr 14, 2014
  42. Gigiwoo

    Gigiwoo

    Joined:
    Mar 16, 2011
    Posts:
    2,981
    Fixed? Though I could probably make the sentence more broadly applicable by refactoring it some more...
    Gigi
     
  43. BrainMelter

    BrainMelter

    Joined:
    Nov 20, 2012
    Posts:
    572
    It largely depends on what your requirements are. Unity itself is super-generic and reusable, and parts of it, such as some stuff on the Asset Store, have fallen out of maintenance. If you're planning on using something in several places, then reusability is indeed good. There are certain assets, such as Daikon Forge, that need to be built this way.

    But if you're just going to make some app code and toss it, then you don't really need to be generic.
     
  44. Smooth-P

    Smooth-P

    Joined:
    Sep 15, 2012
    Posts:
    214
    You're setting the productivity bar pretty low and replicating a lot of code / effort if you consider something as simple and widely applicable as generic pools over-complicated, astronaut architecture.

    Good library design doesn't increase the overall complexity of your code base, it reduces it by localizing the complexity of often used patterns into one place and allowing all the code that uses that pattern to become simpler. Which is kind of the point of having things like collections libraries and language features like generics, lambdas, delegates, etc.

    Of course, bad design is bad design whether it's bad library design or bad code replicated all over the place. If your developers aren't up to the task of writing good code, well, you're gonna have a bad time.
     
    Last edited: Apr 14, 2014
  45. minionnz

    minionnz

    Joined:
    Jan 29, 2013
    Posts:
    391
    In this particular scenario, I think I'd agree with Gigiwoo - I don't really see much use for this code outside of the TileManager class. If I happen to need a similar Manager for another type of asset, then I'd refactor at that point - but not before.
    Though, the code should be pretty generic anyway - it's just instantiating/removing a prefab and maintaining a list of them.

    While reusable, DRY code is the end goal - I don't think you should obsess over it. If you think something MIGHT be useful in the future if it was more generic, just leave it alone - that's not a good enough reason to waste time making it generic. I'm pretty sure this is what Gigiwoo was getting at? (Though I may be wrong)

    http://en.wikipedia.org/wiki/You_aren't_gonna_need_it
     
    Last edited: Apr 14, 2014
  46. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Are you serious? :p

    I'm shocked if you honestly can't find a simple way to make what I described there.

    We're literally talking about something that checks the positions of a bunch of Transforms each frame and performs some kind of reset function. Unity provides simple ways to achieve both of those things out of the box. It is simplicity. Period.

    I know the things I'm talking about here are scary, but I can guarantee you I've tried both ways being discussed here (and others), and the small, generic (where relevant) components + events approach is easily the most productive of those I've tried so far. Like I said in my response to _Daniel_, the concept of a "best way" just has to take more into account than the technical implementation.

    It's important to ask "Am I overcomplicating this? Could I use a simpler solution?" and respond accordingly. It's also important to have some idea of where flexibility is likely to be useful and to plan accordingly. Throwaway generalisations and catch phrases don't help anyone - there wouldn't be a conversation to be had if generalisations could provide real solutions.
     
    Last edited: Apr 15, 2014
  47. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Also, I want to reiterate everything Smooth P said.
     
  48. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Aside form "all purpose" I don't at all see how the things you mention in the first sentence there are mutually exclusive. I do want to do away with the idea that "generic" means "all purpose", though. It doesn't. Just like Smooth says, "generic" is about identifying patterns and making reusable solutions for those patterns. It is not about making reusable solutions to arbitrary and/or imaginary problems.

    On the matter of "simple" as an investment - yes, it's a great investment. I suggest taking that investment every time.

    I live by the following quote, from Einstein I think: "Things should be as simple as possible... but no simpler."

    You need to consider "simple" from more than one use case. "Simple to write" is not necessarily "simple to use" later on, and it absolutely does not necessarily mean "simple to maintain". See my use case about the guy who took a "simple" approach and then got bitten on the bum big time when a simple variation request came along. A data-driven solution that solved the same problem at the same time as being highly flexible was barely more complex, took less code, and actually took less time to implement than he'd spent. It also meant that the next variation that came along could be handled quickly and easily by a designer who had access to a simple data interface... no code time at all!
     
  49. BrainMelter

    BrainMelter

    Joined:
    Nov 20, 2012
    Posts:
    572
    I'd agree with you on this one. The idea of "simple" is not as simple as we may initially think it is.

    Never! Some people have made entire careers from tossing out such generalizations ;)
     
  50. Meltdown

    Meltdown

    Joined:
    Oct 13, 2010
    Posts:
    5,822
    A good alternative to having global state using the Singleton pattern is to use the Service Locator pattern, which basically decouples your gameObject specific script dependencies from your global managers.

    So for instance if you have a Singleton instance called SoundManager, and it has a method called PlaySound(), if you're using the Singleton you may have hundreds of objects dependant on this SoundManager instance. With a Service Locator your gameObjects don't need to know about your managers or have hard dependencies on them or other gameObjects.

    A simple but clear explanation of how to implement this pattern in C# can be found here..
    http://stefanoricciardi.com/2009/09/25/service-locator-pattern-in-csharpa-simple-example/