Search Unity

How do you organise your code?

Discussion in 'Scripting' started by Nanako, Apr 21, 2015.

  1. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    I've never really devised a convention for this. I try to keep variable declarations (as well as subclasses and structs) at the top, and functions below, and i try to group together functions in approximate orders that i use them, but otherwise i'm not sure

    my code is getting messy, organising it seems like really annoying work, and i don't even have a target as to HOW to organise it. I could do with some ideas.

    I don't suppose visual studio has some functionality to do this for me? rearrange functions and variables in some sort of logical order?
     
  2. CxydaIO

    CxydaIO

    Joined:
    May 12, 2014
    Posts:
    61
    same problem here ... with my current project i try something different ... i have a basic class where i have my variable declaration as well as start(), update etc and then i split other functionalities as partial classes into several files grouped by a topic, so far my script files are relatively short and easy to read but now i get A LOT of script files and i'm really just at an early stage of my project. But so far working with it is still easy
     
  3. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    comes down to making your classes 1 purpose classes, and keeping clear standards on how you name things. Aside from that you could take advtange of things like regions.

    After that i tend to standerise where i like to put things, like subscribing to events i always but near the top, and try to keep related methods togeather.
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    Well first and foremost, you can check out the open-source side of my framework to see how I organize my code first hand.

    https://github.com/lordofduct/spacepuppy-unity-framework

    I use regions, and I split them into:

    constants
    Events
    Fields
    Constructor
    Properties
    Methods
    Interface Implementations
    Special Types (classes inside a class that are specific to only this class)
    Static Util/Factory methods

    In that order, if and only if the section exists

    My methods, if numerous, may get regions inside the methods region that organize them by general usage... but this is seldom.

    As passerbycmc, a big part is making sure your classes take care of one primary job, and only one primary job.
     
    Last edited: Apr 21, 2015
    ADNCG likes this.
  5. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    vs has some functionalities that help with large code projects, things like "peeking" definitions and the like, there is also an option to sort out indenting and the ever helpful (can't remember what it's called off the top of my head) " populate functions from interface/parent/whatever" which saves a lot of copy/paste. With the new free full(mostly) feature community edition is really worth a look, especially since they announced recently they're planning on natively implementing the integration with Unity and a few other game engines.
     
  6. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    #regions and parting my systems into relevant class structures.
     
  7. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    This topic comes up frequently, and maybe that's a good thing since approaches are constantly evolving. Unity is geared toward component-based design -- which isn't to say you should avoid other techniques such as inheritance and polymorphism. They're all tools in a toolbox, and they each have their place (and their pro's and con's). That said, deep inheritance trees are usually trouble. Try to avoid them.

    Since Unity is so component-oriented, it helps to organize your code by components (e.g., Health component, Input component, HeadLook component, etc.). As passerbycmc and others write, each script should implement exactly one thing. This argues against #regions. Generally speaking (although there are always exceptions), if you think you need #regions, your script is trying to do too much.

    I use design patterns and lots of folders. Sometimes I'll have only one script in a folder if it helps organize the code better. Design patterns, in addition to their other advantages (established solutions, common language), suggest how to organize code. If I'm using the Model-View-Controller (MVC) meta-pattern, I know I'll probably need three scripts: model, view, and controller. When other readers know it's using MVC, they also know to expect those three scripts. It's a kind of shorthand that gives them a head start on understanding how the code works.

    When it comes to organizing the lines of code inside each script, just be consistent. If your scripts are short (and they should be), they're much easier to organize. You might want to follow the MSDN C# Coding Conventions so your style will be in line with what many professional C# developers use. I like the Aviva Coding Guidelines myself, which isn't far from MSDN standard.
     
    _met44 likes this.
  8. Deleted User

    Deleted User

    Guest

    If you have Visual Studio & ReSharper you can set some options so it will format and organize your code however you want it. It's really neat.
     
    Last edited by a moderator: Apr 21, 2015
    TonyLi likes this.
  9. jtsmith1287

    jtsmith1287

    Joined:
    Aug 3, 2014
    Posts:
    787
    How does deep inheritance cause issues with Unity? Please give some examples. Inheritance is a GREAT way to cut down on redundancy, and keep things organized. I usually keep my file structure in line with the inheritance if it makes sense. It helps that C# only allows one inheritance at a time.

    For the OP, I've got a mixture of what everyone else has stated already. Ultimately what's more important that how you organize your code, both in file and out, is consistency. If you have terrible organization but use the exact same horrible technique every time you'll be fine, haha. And I'm only half kidding.

    If your working solo, stay true to your design. If you're working with a team, stay true to your design and communicate clearly what that design is. Please don't waste a ton of time organizing stuff unless you're going to see some gains from it. We had a game push way passed its original scope and didn't have a good file structure. We opted to reorganize the whole thing. it took some time because we broke a couple references (which was bad practice anyway) but we saw a huge amount of time returned to us from the effort. Things weren't impossible to locate anymore.

    Also, refactor early... refactor often. If you're following a specific pattern and you start to notice it's not working, fix it as soon as possible! People are afraid to delete and rewrite code, or move code or whatever. It's a good thing. It means your code is becoming more flexible as you discover the nuances and quirks of your project.

    So again, don't focus on specific techniques for organization. Just pick what works for you now and stick with it. Be consistent, only modifying if you feel it's worth it.
     
  10. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I differ somewhat here. Though I agree scripts should be short, even my short scripts are divided into regions, which I get automatically thanks to a custom script template. The regions are:
    • Public Properties
    • Private Properties
    • MonoBehaviour Events
    • Public Methods
    • Private Methods
    The region divisions, in addition to the #region/#endregion tags, also have a big comment line of dashes to separate them visually.

    I've been living with this template for about half a year now, and it makes me happy. In particular, it's important when looking at the code to know what the MonoBehavior events are, because those are magic names. Even though they're not defined in a superclass or interface, if you change any of those names, your code breaks (often in a hard-to-debug way). The usual C# compiler support is no help here. And, even though they're private, they still get called from outside the class. In short, they're weird, and well worth segregating into their own little region of the script.

    The other regions are not so important, but they help separate interface from implementation. Objectively speaking, I suppose I should group the public properties and methods together, and then group the private properties and methods together somewhere else. I just can't bring myself to do that... the habit of putting all properties at the top is too deep.

    Within each region, sometimes I try to keep my methods alphabetical, but usually I don't. As noted above, usually each class is short enough that it doesn't much matter.

    HTH,
    - Joe
     
  11. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    It's not a problem with Unity per se, but it's common in game development and covered in a lot of code postmortems for games. If I recall, Jason Gregory had a good writeup in Game Engine Architecture. Here's a classic example cited often. Say your inheritance tree is:
    • Entity: base class
      • Obstacle: stationary things that block navigation
        • Barrier: walls, fences...
        • ImpassableTerrain: rivers, lava...
      • Mobile: moving things
        • Vehicle: wagons, cars...
        • Creature: goblins, player characters...
    Creatures can be killed, so they have hitPoints. Later you decide that walls can be broken down by attacking them. You can add a duplicate hitPoints variable to Barrier (which has no relation to Creature.hitPoints) or you can move hitPoints to their common ancestor, Entity.

    If you add a duplicate hitPoints, you have to write and maintain redundant code to handle Barrier.hitPoints and Creature.hitPoints.

    If you move hitPoints to Entity, (1) you've made changes to two classes (Entity and Creature) potentially introducing bugs in either class and all their subclasses, and (2) you've introduced a meaningless variable to things like rivers and lava. (A river is not a thing that "is" hit points.)

    You can apply this logic to just about any property. In many games, you can "talk to" NPCs (Creatures). In some games, you can also "talk to" objects such as wall-mounted intercoms (Barriers). You'd have to promote this property to Entity also. Eventually everything ends up in the Entity class this way.

    It may be better instead to take a component-based approach and simply add a HitPoints component to anything that has hit points. (A wall is a thing that "has" hit points.)

    That said, I'm not against inheritance, it's just not the right tool for every single problem. The Dialogue System uses inheritance for a base QuestLogWindow class, with subclasses for the different GUI systems (NGUI, DF-GUI, etc.). The base class provides generic data handling. The subclasses display the data using their respective GUI systems. Further subclasses slightly alter the display behavior.

    Michael O. Church wrote a great answer to the Quora question "Was object-oriented programming a failure?" that's probably relevant here.

    Okay, using a standard set of regions as part of a template, as others also mentioned, is reasonable. I had in mind the "abuse" of regions to create monolithic scripts. Don't do this. :)
     
    Last edited: Apr 21, 2015
  12. jtsmith1287

    jtsmith1287

    Joined:
    Aug 3, 2014
    Posts:
    787
    What this also sounds like to me is a poor use of inheritance.

    Inheritance should be determined strictly by interaction. What is the purpose of "entity" and how do I need it to interact with other objects. Does it need to take damage? Then I should implement my base class that contains the functionality for taking damage. Does it need to move? Then I should implement the base class needed for movement.

    My inheritance usually looks like this.

    • Destructable -- It can be destroyed
      • Mobile -- It can move, but not autonomously!
        • UserControlled -- Connects user controls to the Mobile 'motors'
          • UserFunctionality -- Game Specific mechancs like abilities or shooting or what-have-you
        • AIControlled -- Connects the AI to the Mobile 'motors'
          • AIFunctionality -- Game Specific mechanics to run autonomously, ie chasing a player or shooting a player
      • Stationary -- Can't move... uhmm... has windows?
        • etc etc. -- Game specific functions this stationary object may have.

    With this model I simply inherit from Destructable if I want objects to be able to be destroyed. AI units need only interface with their parent Mobile class to access movement functionality, and they too can take damage. Stationary objects can be destroyed, but beyond that they share nothing with their distanct Destructable cousins, "The Mobiles". There's not much of a difference in our inheritance visually but the core is different. Yours is based on type, mine is based on purpose. With the purpose model there's very, very little "Oh shoot I need to have X functionality on this obejct" where it's not as simple and just implementing a class. The only thing I need to do to improve on this is Interface usage. I don't use it NEARLY enough and it's crazy awesome. Things could get wild! Haha. Having good interfaces would mean I could slap an interface an any object within the tree, giving it much more diversity.

    I also structure my files in this way as well if I can help it. My user controlled objects will likely be in a directory that's located within the mobile directory which located in the Destructable directory. However that's for one project. I have a different project that uses a different structure because it doesn't use as much inheritance -- but I still like the fine grained organization.
     
  13. jtsmith1287

    jtsmith1287

    Joined:
    Aug 3, 2014
    Posts:
    787
    When I see properties in the middle of a class I cringe, haha. I love your idea of using a template to predefine regions. I'm going to do this tonight. O_O
     
  14. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    The only general practice I can mention that would be helpful to others would be to label your closing brackets.
    Code (CSharp):
    1. public void className()
    2. {
    3.     if(stuff)
    4.     {
    5.         for(stuff)
    6.         {
    7.             //lots of code
    8.         }//for
    9.     }//if
    10. }//className
    It's probably better to move things into functions to have more readable code, but inevitably you may end up with huge functions. Labeled closing braces make it a little easier to know where you are when you're 100 lines into a function and see 3 closing braces next to each other :p
     
  15. Deleted User

    Deleted User

    Guest

    I prefer to eliminate nesting as much as possible. In your example I would do this:

    Code (CSharp):
    1. public void className()
    2. {
    3.     if(!stuff) return;
    4.  
    5.         for(stuff)
    6.         {
    7.             //lots of code
    8.         }
    9. }
     
    vstefaniuk likes this.
  16. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Not to belabor it (to each their own, after all, though notice shooting is repeated in UserFunctionality and AIFunctionality), but I think the takeaway for this thread is that these decisions matter to the organization of the code.

    Now I'm just seeming argumentative, sorry. I agree with your point, @Tomnnn. At the same time, I'd argue that if you have so many levels of braces, you should simplify your your code instead -- er, as
    @supremegrandruler just said.
     
  17. jtsmith1287

    jtsmith1287

    Joined:
    Aug 3, 2014
    Posts:
    787
    I'm with @TonyLi and @supremegrandruler on this one. I don't think you should have 100 line methods very often, and the nesting will likely just make it more difficult to debug.

    This is where interfaces would come in handy, but you're right in that it's repeated. Some times it can't be helped but each project is different. That was just a general example to show the logic as opposed to the specific. You make a good point, however.
     
  18. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Agreed. It was nice to see Unity start using them more in the new EventSystem.
     
  19. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Ugh. I hate this practice and have an unreasonable urge to stomp it out whenever I see it. Comments should be something worth reading. If they just say something that is totally obvious from the code, then they are bad. Indicate which brackets are which with consistent indenting. If you are really struggling you can click on one and see the other highlighted. Or turn on that little status bar that tells you exactly where the cursor is positioned.
     
  20. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    have to agree with this, just use proper indenting, and also take advtange of the syntax with no {} if you only got 1 statemeant in a IF.

    most ide's can tell you what the matching brace is by highlighting it.
     
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Plenty of good tips in here.

    On the large scale the only other thing I use extensively that hasn't been mentioned is namespaces. Namespaces are great ways to keep your systems separate. They help prevent dependency between unrelated systems. It also makes it obvious where those dependencies are. For example if you ever write 'using TerrainGeneration' inside a script called 'PlayerHealth' you are doing something wrong and your systems are too tangled up.

    Inside scripts I tend to try and write things in chronological order. Properties and fields come first. Then Awake and Start, and any private methods intended to be used by these methods. Then Update and its associated methods. Then any public methods with there associated private methods. Finally OnDisable and OnDestroy, and their associated private methods. In theory you can read my scripts from top to bottom, and rarely have to scroll back up. Its rarely that simple, but the general idea is always there.
     
  22. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    ya i find i use namespaces pretty heavily, but that is likly because i wrote in Java and python before c# where it is pretty much a requirmeant.
     
    Kiwasi likes this.
  23. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    I'd say it's pretty much a requirement in C# as well (not literally a requirement, but pretty much).
     
    Kiwasi likes this.
  24. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Quick tip for MonoDevelop, you can change the pad from solution to classes, this lets you switch between seeing the file structure and the namespace structure.

    I'm sure VS has similar functionality.
     
  25. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    It works in that example which was poor on my part to demonstrate the point. In many of the classes I've taken in the past 4 years, I've had to nest 2-3 for loops for big data related things and those functions got very large lol.

    I'm italian, I take great pride in my spaghetti.

    my // comments are usually labels, my /**/ and /// comments are something worth reading.
     
  26. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    That's not a bad way to do it, if you are going to add extraneous comments.

    I'm probably guilty of erroring on the side of lower numbers of comments. I just hate having to maintain them. The labels on closing brackets wod be rough for me. I'm constantly changing for loops to while loops, ifs to methods, and Update to a coroutine and back. Maintaining regular comments is tough. Maintaining end labels sounds like a nightmare.
     
  27. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    Consider who you're talking to :p my code is full of extraneous comments that are brief and describe what a line is doing, as if I'm expecting to forget it later... My high school computer science teacher taught me that the hardest debugging problems will require you to 'play computer' and step through the execution of a program as the computer would. For really complicated functions, I'll have little comments all over it stating what the block is doing and what kind of result to expect.

    I blame this semester's assembly programming for my more recent interest in having blocks of code do more rather than be broken up into more functions.

    It's something I keep mostly to the closing brace of a function. If I do have some random situation where I've got several loops and nested conditions that all happen to close towards the same point, I'll label them in case I come back to make changes. If I do simplify the code, those labels save me a few seconds of scrolling around to find the matching brace.

    It saved me a good deal of trouble 2 or 3 times, so I stuck with it.
     
  28. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    why decribe what a line is doing? that part should explain its self by looking at the code, and by using good varible and method names. if there is a need to decribe something, Why is better than How.
     
    Deleted User likes this.
  29. Deleted User

    Deleted User

    Guest

    I agree. I like to give my methods and variables very descriptive names. With autocomplete (well Mono's sucks but VS' is great) you don't have to worry about "saving keystrokes".
     
  30. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    That's usually the case for something I've done plenty of times, but in the middle of figuring out what some code is doing, it helps not break things while changing other things. My first time trying recursive descent parsing... my first time trying to implement A* with Java... trying to migrate that attempt to Unity & C#...

    My memory stores things better if I have typed / written them. Labeling code is something I'll do the first few times I'm doing it. It's also how I study for exams :D