{"id":14960953,"url":"https://github.com/leotgo/unity-coding-standards","last_synced_at":"2025-10-16T05:18:44.674Z","repository":{"id":91873365,"uuid":"333631972","full_name":"leotgo/unity-coding-standards","owner":"leotgo","description":"A composition of coding standards for programming in Unity3D, acquired from multiple game developers across the community.","archived":false,"fork":false,"pushed_at":"2021-05-22T18:27:13.000Z","size":15,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-20T23:36:15.144Z","etag":null,"topics":["best-practices","coding-standards","coding-style","csharp","gamedev","unity3d","unity3d-script"],"latest_commit_sha":null,"homepage":"http://leotgo.github.io/unity-coding-standards","language":null,"has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leotgo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-28T03:16:32.000Z","updated_at":"2025-05-10T11:04:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"c30bc17a-67a3-433a-9892-6ce6c6dca1b5","html_url":"https://github.com/leotgo/unity-coding-standards","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/leotgo/unity-coding-standards","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leotgo%2Funity-coding-standards","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leotgo%2Funity-coding-standards/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leotgo%2Funity-coding-standards/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leotgo%2Funity-coding-standards/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leotgo","download_url":"https://codeload.github.com/leotgo/unity-coding-standards/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leotgo%2Funity-coding-standards/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279159007,"owners_count":26116372,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-16T02:00:06.019Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["best-practices","coding-standards","coding-style","csharp","gamedev","unity3d","unity3d-script"],"created_at":"2024-09-24T13:23:31.381Z","updated_at":"2025-10-16T05:18:44.640Z","avatar_url":"https://github.com/leotgo.png","language":null,"readme":"# Unity3D Coding Standards\n\nAn attempt at documenting a composition of coding standards acquired from multiple game developers across the community.\n\n## Table of Contents\n\n1. [Code Formatting](#code-formatting)\n2. [Code File Layout](#code-file-layout)\n3. [Naming Conventions](#naming-conventions)\n4. [Code Documentation](#code-documentation)\n5. [References](#references)\n\n## Code Formatting\n\n* Use spaces instead of tabs. Do not mix spaces and tabs;\n* Each identation level should be 4 spaces wide;\n* Each brace should have its own line;\n\n```csharp\n// Avoid\nif(playerWasHit) {\n    PlaySound(playerHitSound);\n    Damage(player, damageAmount);\n}\n```\n\n```csharp\n// Prefer\nif(playerWasHit)\n{\n    PlaySound(playerHitSound);\n    Damage(player, damageAmount);\n}\n```\n\n``` csharp\n// Bad\npublic float Health { get { return health; } }\n```\n\n```csharp\n// Good\npublic float Health\n{\n    get\n    {\n        return health;\n    }\n}\n```\n\n* It is acceptable to use the [expression body definition](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members) operator `=\u003e` for property getters and setters for simple, single-statement properties;\n\n```csharp\npublic float Health\n{\n    get =\u003e health;\n    set =\u003e health = value;\n}\n```\n\n* Every statement after a conditional should be inside braces, even if it is a single statement;\n\n```csharp\n// Bad\nif(enemyInRange)\n    Explode();\n```\n\n```csharp\n// Good\nif(enemyInRange)\n{\n    Explode();\n}\n```\n\n* Avoid using ternary operator;\n* Use [string interpolation](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated) instead of LogFormat to increase readability;\n\n```csharp\n// Avoid\nDebug.Log(\"Player \" + playerId + \" took a hit from \" + damageSource + \" for \" + damageAmount + \" damage.\");\n```\n\n```csharp\n// Avoid\nDebug.LogFormat(\"Player {0} took a hit from {1} for {2} damage.\", playerId, damageSource, damageAmount);\n```\n\n```csharp\n// Prefer\nDebug.Log($\"Player {playerId} took a hit from {damageSource} for {damageAmount} damage.\");\n```\n\n* Switch-case code should be implemented inside braces;\n\n```csharp\nswitch(colorId)\n{\n    case PlayerBodyColors.White:\n        {\n            playerBody.SetTexture(whiteTexture);\n        }\n        break;\n    case PlayerBodyColors.Red:\n        {\n            playerBody.SetTexture(redTexture);\n        }\n        break;\n    default:\n        {\n            playerBody.SetTexture(defaultTexture);\n        }\n        break;\n}\n```\n\n* Encode the document in UTF-8 if possible;\n* End-Of-Line character should be CRLF;\n\n## Code File Layout\n\n* Library usings should be the first lines of a file, followed by *typedef*-like usings;\n* Put every class definition inside an appropriate namespace;\n* Prefer defining only one class per file;\n* File name should be the same as the class name.\n\n```csharp\n// File: AiPathfinder.cs\nusing System.Collections.Generic;\nusing UnityEngine;\n\nusing WaypointMap = Dictionary\u003cVector3,Waypoint\u003e;\n\nnamespace MyGame.AiNavigation\n{\n    public class AiPathfinder\n    {\n        ...\n    }\n}\n```\n\n* Usings should be defined in the following order:\n    1. System or .NET libraries\n    2. Unity libraries\n    3. Third-Party plugins (asset store)\n    4. Your own utility libraries\n    5. Project namespaces\n    6. Type name aliases\n\n* All namespace categories should be blocked together, without separating with spaces or comments.\n* An exception to this is *typedef*-like usings, which should be separated from library usings with an empty line.\n\n```csharp\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.Events;\nusing ExampleCompany;\nusing OtherCompany.BoostedInspector;\nusing MyUtilityLib;\nusing MyUtilityLib.DebugUtilities;\nusing MyOtherLib.Patterns;\nusing ThisProject;\nusing ThisProject.Audio;\nusing ThisProject.Combat;\n\nusing EntityPrefabMap = Dictionary\u003cEntityType,GameObject\u003e;\n```\n\n* Define a class in the following order:\n    1. Nested classes\n    2. Constants\n    3. Enums\n    4. Properties\n    5. Fields\n    6. Constructors (if applicable)\n    7. Unity Messages\n    8. Public methods\n    9. Private methods\n\n```csharp\npublic class MyClass : MonoBehaviour\n{\n    private class MyNestedClass\n    {\n        ...\n    }\n\n    private const int SOME_CONSTANT = 1;\n\n    public enum SomeEnum\n    {\n        FirstElement,\n        SecondElement\n    }\n\n    public int SomeProperty\n    {\n        get =\u003e someField;\n    }\n\n    private int someField;\n\n    private void Start()\n    {\n        ...\n    }\n\n    public void SomePublicMethod()\n    {\n        ...\n    }\n\n    private void SomePrivateMethod()\n    {\n        ...\n    }\n}\n```\n\n* Prefer defining methods in the following order:\n    1. Initialization methods\n    2. Core functionality methods\n    3. Helper or explanatory methods\n\n```csharp\n// Initialization\nprivate void Initialize()\n{\n    ...\n}\n\n// Core functionality\nprivate void Move(Vector3 direction)\n{\n    ...\n}\n\n// Helper\nprivate bool CheckIfPositionIsWalkable(Vector3 position)\n{\n    ...\n}\n```\n\n## Naming Conventions\n\n* Identifiers for classes, methods, namespaces, enums, properties, attributes and coroutines are `PascalCase`;\n\n```csharp\nnamespace OpenSoulsGame.Debug\n```\n\n```csharp\npublic class RichTextFormatter\n```\n\n```csharp\npublic string StringToBold(this string inputString)\n```\n\n```csharp\npublic float DefaultSpacing\n{\n    ...\n}\n```\n\n```csharp\n[ConsoleAttribute] private int letterSpacing;\n```\n\n* Identifiers for fields, local variables, parameters are `camelCase`;\n\n```csharp\npublic  int playerId;\nprivate InputHandler playerInput;\nprivate float health;\n```\n\n```csharp\nvar name = GetPlayerName(playerId);\n```\n\n* Constants are written in `UPPER_CASE`;\n\n```csharp\npublic const int MAX_SCENE_OBJECTS = 256;\n```\n\n* Acronyms should be treated as words and are written in `PascalCase`.\n\n```csharp\npublic class XmlFormatter\n```\n\n```csharp\npublic class AiBehaviour\n```\n\n* The conventions for casing are unaffected by the modifiers `public`, `private`, `protected`, `internal`, `static` or `readonly`;\n\n* Namespace identifiers should briefly describe the systems or sets of definitions contained in the namespace.\n\n```csharp\nnamespace Utilities.Debug\n```\n\n```csharp\nnamespace TowerDefenseGame.Combat\n```\n\n```csharp\nnamespace TowerDefenseGame.UI\n```\n\n* Class identifiers should briefly describe its responsibilities or data. Prefer including the suffix *\"Base\"* in abstract classes where applicable.\n\n```csharp\n// A class responsible for performing movement on the player character's transform\nclass PlayerMotor\n```\n\n```csharp\n// An abstract class for implementing behaviors for AI Agents\nabstract class AiBehaviourBase\n```\n\n* Interfaces should include the prefix \"I\". The interface name should briefly describe the purpose of its members or the components it interacts with.\n\n```csharp\ninterface IMotorTarget\n```\n\n```csharp\ninterface IUiElement\n```\n\n* Method identifiers should describe the effect caused by the method, or the return value if the method has no effect.\n\n```csharp\n// A method that performs movement on the player character\npublic void Move(Vector3 movement)\n{\n    ...\n}\n// Tipically, the identifier for methods without a return type should be a verb\n```\n\n```csharp\n// A method that converts radians to degrees.\nprivate float RadianToDegrees(float radians)\n{\n    ...\n}\n// The identifier helps to understand how the returned value should be interpreted\n```\n\n```csharp\n// A method to determine if a position in the world can be traversed by the player\nprivate bool IsPositionWalkable(Vector3 position)\n```\n\n* Coroutines are written with the prefix 'CO_', and the rest of its identifier should follow the same rules as methods.\n\n```csharp\nIEnumerator CO_SpawnPlayer(int playerId)\n```\n\n## Code Documentation\n\n* Write self-documenting code when possible. Avoid overly abbreviated variables which don't have semantic value for the reader.\n\n```csharp\n// Bad:\np = p + v * dt;\n```\n\n```csharp\n// Good:\nposition = position + velocity * deltaTime;\n```\n\n* You can also use explanatory variables to avoid writing complicated and unreadable lines:\n\n```csharp\n// Bad:\npos = Vector3.Lerp(targetEnemy.position, player.GetComponent\u003cAutoMovementController\u003e().targetWaypoint.nextPosition, elapsedTime / max);\n```\n\n```csharp\n// Good:\nvar waypoint = player.GetComponent\u003cAutoMovementController\u003e().targetWaypoint;\nvar startPosition = targetEnemy.position;\nvar finalPosition = waypoint.nextPosition;\nvar lerpPoint = elapsedTime / maxMovementTime;\nposition = Vector3.Lerp(startPosition, finalPosition, lerpPoint);\n```\n\n* Write useful comments. Avoid being redundant with what the code is telling the reader. Instead, disclose valuable information that might not be directly perceivable.\n\n```csharp\n// Bad:\n// increment player id\nplayerId++;\n```\n\n```csharp\n// Good:\n// We know that a new player has joined, generate a new identifier.\nplayerId++;\n```\n\n* Do not comment bad or unreadable code. Prefer rewriting it instead.\n\n```csharp\n// Bad:\n// Increase current position by the velocity scaled by deltaTime to perform movement.\np = p + v * dt;\n```\n\n```csharp\n// Good:\nposition = position + velocity * deltaTime;\n```\n\n* Do not contradict the code!\n\n```csharp\n// Bad:\n// Health value should be between 0 and 100.\nprivate int health;\n\n...\n\nthis.health = 150;\n```\n\n```csharp\n// Good:\n// Base health values should be between 0 and 100.\nprivate int health;\n\n...\n\n// Apply the temporary health buff from consuming potion.\nthis.health = 150;\n```\n\n## References\n\n* [Microsoft Naming Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines)\n* [raywenderlich/c-sharp-style-guide](https://github.com/raywenderlich/c-sharp-style-guide)\n* [Unreal coding standards](https://docs.unrealengine.com/en-US/ProductionPipelines/DevelopmentSetup/CodingStandard/index.html)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleotgo%2Funity-coding-standards","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleotgo%2Funity-coding-standards","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleotgo%2Funity-coding-standards/lists"}