Unity code convention proposal

Hi guys!
I was doing a code convention guide for Unity at home as training excercise and i thought it will be a good idea to share with the community.
The idea of this guide is to offer a “complete guide”, not only to offer some naming examples.
I have designed the guide as a .cs file so you can add it in your project and check it quickly.

Do not hesistate to give feedback, i would love to see this guide improving thanks to the community.

1 Like

Some comments/things I do differently. Feel free to take it or leave it.

What’s a routine? Convention in C# is to call them methods. In general I name method parameters the same way as local and class variables. I reserve the underscore for backing variables which should only be accessed via properties. You don’t seem to have a convention for backing variables anywhere?

Because all consuming code treats properties as variables, I prefer to use the same convention as variables. Using the method convention implies that the property should be invoked. This convention offers several advantages, the most obvious is that you can convert a variable to a property without needing to change any consuming code.

Controversial choice :stuck_out_tongue:

Sometimes useful. But frequently more effort is required to maintain then benefit is derived from this one.

Should be stronger. ‘Inspector fields should only be used to set up initial conditions. These should always be private and marked with the [SerialiseField] attribute. If needed, these can be exposed to other scripts via public properties. Avoid changing inspector fields via script, as this can lead to unexpected behavior. Inspector fields should not be used to provide debug information to developers.’

Needs to be stronger. ‘Do not use a constructor in classes that inherit from MonoBehaviour. Instead use Awake for internal initialization, and start for external initialization.’

Mention that blank Unity methods should not be included. If a Unity method is blank, delete it.

As an alternative, you can explicitly assign values to your enums, which will prevent inspector changes when the enum changes.

I would suggest bracketing the == operator here. Its not super quick to read otherwise.

Its frequently better to start with the if with the least performant check. For example checking if a target is in range is cheaper then checking if a target is in line of site. (It might be worth making this a separate best practice line, its not perfectly related to the one I quoted.)

Ugh. I hate this style. It doesn’t really add much to code readability. And it is fairly costly to maintain (and fairly easy to get the comments out of sync with the brackets). Every modern IDE has bracket matching functionality. Use it.

Or better yet ‘Don’t call StartCoroutine on another Component. If you need to do this, create a public method on the other Component that will call StartCoroutine. Each Component should be responsible for starting and ending its own co routines’.

You should also make a comment about calling StopAllCoroutines, which should normally only be done in a few situations, like object pooling.

I’m not super attached to any of these opinions. Feel free to use it as a starting point for discussion.

4 Likes

BTW, Unity Technologies started to use .NET standard conventions:

1 Like

The point of style conventions is to standardize. Creating your own without a good reason to deviate from existing standards, makes no sense. And often you will eventually have to do it right and it’s a minor pain to convert. Like what Unity is doing now.

Personal preferences don’t count here, that’s kind of the whole point, developers don’t get to decide individually what they like, you do it as a team, and teams likewise conform to higher up standards unless they have good reasons not to.

I have been reviewing the .Net Standard convention and is quite simple. The idea of doing my own guide was to have a more detailed guide with info from several sources (you can see the References.txt to see which ones). Also as you have said, i will give a final review with my team, it is important that all of us accept this guide before start working with it.

Code convention should be per team or per project.

2 Likes

I just have updated the guide with your feedback. I am still waiting to have a meeting with my coworkers to decide what to do with some controversial decisions

Suggestion: no line in the code should be longer than 80 characters except if the code itself does not make any sense in two lines. Just try to read your own convention file inside github viewer… Unity-code-convention/CodeConventionForUnity.cs at master · elhispano/Unity-code-convention · GitHub

While I agree with this, I also think it’s valuable to have options to choose from as a starting point. For new teams, new studios, or ones just adopting Unity / C#, it may be worthwhile to start with an existing guide and then just modify the bits you want for your team.

Alot of things I do not agree with. But the if block and the execution block on the same line is very clean code for some scenarios like exit early.

        public void IndexNewBullet() {
            if (Magazine == null) return;
            if (Magazine.Empty) return;

            Bullet = Magazine.TakeBullet();
        }

I am going to upload a PDF version, but the idea of this file is to open it in your progamming IDE (VisualStudio or Monodevelop) and see the code with the style you are used to.

I want to make a Markdown version but only after i review the guide in the office, to avoid maintaining two files.

I just uploaded a new version with some modifications based on the feedback of a reddit user. (also de .PDF file)

Thanks, but generally it’s a good idea to not having code lines longer than 80 characters. :wink: Not just because of the github viewer, there are a lot of pro reasons:

  • humans can’t really read longer lines properly, it gets easier to lost the line you’re reading
  • if all the code is formatted on the same way (no more than 80 characters per line), you can rely on it, you can put more than one window side by side on a modern, high resolution monitor/screen
  • the code becomes more sectioned, more airy

But of course the secondary lines should be tabbed in more. We usually use 2 spaces worth of tabs normally and 4 worth of tabs for secondary lines:

public void Cool() {
  ThisIsAVeryLongLineJustToDemonstrate.TheLongLineBehavior().SoYouCanSeeIt()
      .ForYourSelf();
}

@ In the guide, the problem of the length is in the comments isn’t it?

The guide have a lot of conventions that help avoid having long lines of code, but you are right that there isn’t any metric related with line length to use as reference.

In the guide, yes. In real life, the “try to give as short names as possible” is not working. And does not give you solution for the chained commands like my example above. I admit, it’s not the same in Unity than for example in Java, but still, a lot of FindObjectOfType or GetComponentInChildren can be very long.

You expect programmer to use monosapced font, and pdf version has broken formatting.

3410501--268645--upload_2018-3-2_22-50-1.png

Overal impression:

  • Trying to police plural/singular is overdoing it.
  • Too many rules.
  • (1.1) Uppercase method names are a poor tradition, that is, sadly used by unity. It makes it difficult to distinguish between a method and a constructor call (does not apply to C# to the same degree).
  • (1.1)ALL_CAPS is normally reserved for macros and not constants. Constant is for all practical purposes is a variable that cannot be written into. Same applies to readonly.
  • (1.1)Marking prefab, template explicitly is overdoing it.
  • (1.2)Try to enforce order of class memebrs within class declaration is seriously overdoing it, and placing nested structs/ classes at the end will make it complicated.
  • (1.3)Valid opposites is overdoing it, “new” is a keyword, up/down are used by unity in fseveral cases.
  • (1.4) Hungarian notation sucks, and Marking SO/SZ smells just like it
  • (2) Someone who doesn’t know that multiplication is performed first has no job in programming. Adding braces after JUST two statements is mainly waste of time.
  • (2) Placing a code brace on a separate line does nothing useful, and wastes vertical space in your editor. This is largely a matter of preferences, but I lean toward “{ at the end of the line”.
  • (2) “Inline method difficulty to debug” - use conditional breakpoints when needed, nothing prevents you from temporarily adding new line.
  • (2) “begin-end pair to always designate” - no. “if (asdf) return” needs no braces.
  • (2) Aligning right side of argument - Absolutely no. Aligning stuff with whitespaces makes code unreadable in any non-monospace environment, meaning you won’t be able to open them in anything aside from your IDE. Whitespace alignment is cancer and indicates that your programmer is wasting time making things pretty instead of making them work while retaining readability.
  • (2) Absolutely no “one data declaration” per line. Some things are short and do not need this.
  • (2)When you use CamelCase and your identifiers suddenly start having underscores, it indicates that something might want to be wrapped into a class or a namspace.

…
That’s just a few.

I’m not using it and do not recommend it. Too many rules, convention oversteps bounds on many occasions.

Recommendation:

Make convention shorter reduce it to the point where the whole thing fits on one page of text along with code examples.

The reason for that is that if you have too many rules, expect them to be broken.

Here’s the kind of standard I use:

  • All identifier names are written in camelcase. ThisIsCamelCase
  • All types and class names start with capital letter. SomeType, SomeClass
  • Methods, constants, enum values, and variables, start with lowercase letter. thisIsAMethodName
  • ALL_CAPS strictly reserved for macros.
  • Namespace (upper/lower) is up to programmer. (due to C++ ambiguity)
  • Tab at the beginning of the line, spaces spaces everywhere. (indent with tab, separate with space). Absolutely no aligning anything with spaces, because it breaks with non-monospace fonts and is unreadable in other editors. Aligning anything is IDEs job and not yours. (also see smart tabs)
  • “{” at the end of the line, no space in front of it.
  • If something is hard to read at a glance, break it into pieces - with braces, newlines, etc.
  • In general, try to have one class per file instead of jamming several of them into one file.
  • Programmers should adhere to KISS, DRY, YAGNI, Murphy’s law and use their brain.

And that’s pretty much it.

3 Likes

As far as these things go its a pretty good guide, but I’m seeing a movement away from these kind of heavy conventions.

For example, its ‘common knowledge’ that you should use properties, not public variables… but why? Why not instead make the convention use public variables if you are writing a property that a user can directly change. The argument usually runs along the lines of what if you need to refactor or protect from invalid data. But in the rare occasions this happens you can often just as effectively validate at the point of usage or refactor by introducing an internal wrapper.

This is blasphemy to some but we are finding the impacts (in terms of time to delivery and defect rate) of writing additional plumbing code often outweigh the benefits of the supposedly best-practice approach.

Back on topic, good list, one I would strongly question in the context of Unity is keeping variable declarations of the same type together. For a Component the order variables appear in the class controls how they appear in the inspector so probably better to group in terms of function, using the [Header] attribute to delineate (unless using custom inspectors for everything).

I am a strong believer in following your boolean convention in code line 1330 of the PDF.“Use boolean variables to increase readability and maintenance in logic expressions”

For the enums, you could go so far as to require numerical values be assigned manually to enum values if it is serialized in Unity.

Interesting that you require empty else clauses at line 1536.
Some people might be puzzled seeing that, assuming it is unfinished code or something.

You require an assignment to be made when declaring variables.
Such as GameObject foundGameObject = null;
I don’t know if this is really beneficial for all types. I do however, always assign false to booleans when declaring them
bool found = false;

Ddeclaring constant variables with all capital letters is usually frowned upon in C#.

I am also against multiple declarations per line, such as
int i,j,k;

I don’t see any information about usage of “var” for dynamic typing. What is your stance on that?

Al lot of good information, though. I might read it a gain later on.

It is a thing from borland delphi times. When you have a property, then you can initially set it up to forward get/set to an internal variable, but later replace those with methods that actually do something. In other words, it is a breach of YAGNI principle, because you don’t need getter/setters right now, but “maybe one day…”.

Another problem with properties is that they aren’t really member variables, even though they try to act as them.

This makes to violation of principle of least astonishment when you try to write into a field of the property. C# compiler, unless I’m mistaken, in this situation throws a fit and tells you that you can’t do it. Your’e supposed to read the value from the property, modify it, then set it back. Which increases number of lines of code and reduces readaibility.

The worst abuser of properties to the date I know is unity particle module system. ParticleSystem component exposes bunch of modules, which to the programmer look like classes. The thing is, they aren’t classes, they are handles to internal comonents, and the handles are wrapped into properties, often without setter. So at one point of time it went like this: you try to do like “particleSystem.emission.something = X”, and compiler tells you “Nope. It is a property. No can do”. Then you try “var temporary = particleSystem.emission; temporary.xomething = X; particleSystem.emission = temporary;”, and compiler complains again, because there is no setter method. At this point you start scratching your head, because this particuler module for all practical purpsoes looked like something very close to a struct. Meaning on assignment to temporary, you are supposed to get a copy which is decoupled from original particle system… but in the end it turned out that you are supposed to do “var temporary = particleSystems.module; temproary.something = newValue;”, to modify the state of parcitle systems.

That was a massive headscratcher when I found it out.

Back in the day of borland delphi I used to be a massive fan of properties and was disappointed when I found out that C++ doesn’t have them (and evne though it is possible to jury rig a kinda-sorta-replacement, it is not trivial). However, after working in C++ for a long time, I started doubt their usefulness. Overall property often sounds a lot like a syntaxic sugar and obfuscation than a useful abstraction. Haven’t made my mind on them yet.

However, in unity the issue with properties is that editor doesn’t pick them up at all. You’re supposed to write custom inspector for any class that has properties. Which can be inconvenient.

public void CodeBlocks()
{
// DO: Add a blank line for the begin brace

The idea of putting an opening brace on a new line is just retarded to me… especially when some standards completely break the rule if the opening brace is used in an if,do, for, switch etc codeblock… just dumb really

// GOOD:
if (example != null)
{
Debug.Log(example);
}

// BAD:
if (example != null){
Debug.Log(example);
}

other way around for me… the later is GOOD… the former is STUPID and just a wasted line space… I only accept a closing brace on its own separate line

its like those same idiots that voted on PSR2 standards… opening method brace on a next line was voted for… while opening brace on the next line for EVERYTHING else was voted against… :roll_eyes: a so called standard that throws out consistency in its own ruleset is no standard to me.

1 Like