Under Construction
Unity14 min201 views

Coding Conventions

Minh Khoa

Minh Khoa

Author

image.pngBelow is a compilation of the most standard and common code-writing conventions that game developers (especially in Unity/C#) usually apply. Following these rules makes code easier to read, easier to maintain, and easier to work in teams.


1. Class, Struct, Enum, and Interface

  • Rules: Use PascalCase (Capitalize the first letter of each word).

  • Interface: Start with a I capital letter.

  • Example:

    public class PlayerController : MonoBehaviour { }
    public struct WeaponStats { }
    public enum GameState { Playing, Paused, GameOver }
    public interface IDamageable { }
    
    

2. Variable Names (Variables / Fields)

Classifying variable names is very important so that when looking at the code, devs immediately know which scope this variable belongs to.

  • Private & Protected Fields (Internal variables of a class):

    • Rules: Use camelCase (lowercase the first letter, capitalize the following words) and add an underscore _ at the beginning.

    • Example: _health, _moveSpeed, _playerRigidbody.

    • Note: If you want to display a private variable in the Unity Inspector, use [SerializeField].

      [SerializeField] private int _maxHealth = 100;
      
      
  • Public Fields:

    • Rules: Use PascalCase (standard C#) or camelCase (many Unity devs use to make the Inspector display prettier because Unity automatically separates words).
    • Example: MaxHealth or moveSpeed.
    • Advice: Avoid using public field to prevent other classes from modifying it arbitrarily. Use Properties or [SerializeField] private.
  • Local Variables (Variables declared in a function) & Parameters (Passed-in parameters):

    • Rules: Use camelCase.

    • Example:

      public void TakeDamage(int damageAmount)
      {
          int currentDamage = damageAmount - armor;
      }
      
      

3. Functions / Methods (Methods / Functions)

  • Rules: Use PascalCase. The function name must be a verb that clearly expresses the action.
  • Example: MovePlayer(), CalculateScore(), SpawnEnemy(), UpdateUI().

4. Properties (Properties - Getters/Setters)

  • Rules: Use PascalCase.

  • Example:

    public int CurrentHealth { get; private set; }
    public bool IsGrounded => _isGrounded; // Expression-bodied property
    
    

5. Constants (Constants) & Static Readonly

  • Rules: Use PascalCase or UPPER_SNAKE_CASE (All uppercase, separated by underscores).

  • Example:

    public const int MaxPlayers = 4;
    // Hoặc
    public const float MAX_MOVE_SPEED = 10f;
    
    

6. Boolean Variables (True / False)

  • Rules: Should start with question prefixes (like a question Yes/No) such as is, has, can, should. This helps code read smoothly like English.
  • Example:
    • isDead (Instead of dead)
    • hasKey (Instead of key)
    • canJump (Instead of jumpable)
    • shouldSpawn

7. Events (Events) & Delegates

  • Rules: Use PascalCase. Event names usually start with the word On.
  • Example: OnPlayerDeath, OnLevelCompleted.
  • Event handler function name (Event Handlers): Should be named clearly, for example HandlePlayerDeath() or OnLevelCompletedHandler().

8. Coroutines (Unity's asynchronous functions)

  • Rules: Usually add the suffix Routine or Coroutine to clearly distinguish from normal functions that run in one frame.

  • Example:

    private IEnumerator FadeOutRoutine() { ... }
    private IEnumerator SpawnWavesCoroutine() { ... }
    
    

9. Script and Comment Organization

  • File Name: The file name .cs must exactly match the name of the main Class inside. (E.g.: file GameManager.cs must contain class GameManager).
  • Namespace: Use Namespace to group large features and avoid name collisions (E.g.: MaruGame.Core, MaruGame.UI).
  • Comment:
    • Use XML Comments (///) at the top of important class/hpublic methods to explain their purpose.
    • For comments inside functions (//), don't explain what the code is doing (because the code already says it), explain why that logic was written (explain business logic, bug fix tricks, v.v.).

10. Folder Naming Rules (Folder Naming)

In large game projects, managing resources and having a sensible folder structure is extremely important.

  • General rules: Use PascalCase just like Class names.
  • Technique for pinning folders to the top (Top-level Folders):
    • Add an underscore _ before the names of important folders or the ones you work with most often.
    • Reason: Unity Project Window (as well as Windows/Mac) sorts files alphabetically. The _ character before the letter A, therefore these folders will always automatically rise to the top, helping you navigate very quickly without having to scroll to search.
    • Example: _Scripts, _Prefabs, _Scenes, _Art.
  • Rules for sub-systems (Sub-systems): If there are subfolders divided by features, you can use a hyphen or PascalCase continuous (E.g.: Core-Bounce-Balls or CoreBounceBalls).

11. Variable Naming UI Components (Interface)

When working with UI (Canvas, Text, Button,...), recognizing the Component type from the variable name is very important to avoid confusion and null reference errors.

  • Rules: Add the suffix (Suffix) or prefix (Prefix) corresponding to the Component type.
  • Example (Using suffixes is the most common):
    • _playButton (or playBtn)
    • _healthText (or healthTxt)
    • _loadingBarImage (or loadingImg)
    • _mainPanel / _inventoryDialog

12. Naming ScriptableObjects & Prefabs

  • ScriptableObjects (Static data storage files):
    • Class names and file names usually come with the suffix Data, Config, Settings, Stats.
    • Example: WeaponData, EnemyStats, GameConfig.
  • Prefabs:
    • Use PascalCase. For large projects, it is often also added capitalized prefixes (separated by _) to group asset types when searching.
    • Example: VFX_Explosion (Effects), SFX_Jump (Audio), UI_Popup_GameOver (UI), ENV_PineTree (Environment), CHR_Player (Characters).

13. Remove "Magic Numbers" and "Magic Strings"

  • Rule: Never hard-code a specific number or a fixed string in the middle of logic code lines (e.g.: if (gameObject.tag == "Player") or health -= 15;).

  • Solution: Declare them as constant variables (const), readonly or variables [SerializeField].

  • Example:

    // SAI
    Invoke("SpawnEnemy", 3f);
    
    // ĐÚNG
    private const string SPAWN_METHOD = nameof(SpawnEnemy);
    [SerializeField] private float _spawnDelay = 3f;
    
    // Nơi gọi hàm:
    Invoke(SPAWN_METHOD, _spawnDelay);
    
    

14. Decorate the Inspector & Organize Classes (Attributes & Regions)

Making games is not only about writing code that runs, but also about creating tools (Inspector) that are easy for Game Designers to configure.

  • Attributes: Use [Header("...")], [Tooltip("...")], [Space] to divide groups of variables in the Unity Inspector.
  • RequireComponent: Always add [RequireComponent(typeof(TenComponent))] at the top of the Class if your script must have that Component to run (e.g.: needs Rigidbody2D). It will automatically add that component GameObject and prevent others from accidentally deleting it.
  • Regions: Use #region and #endregion to collapse code sections, making the file tidy. It is usually divided into: Variables, Unity Methods (Awake/Start/Update), Public Methods, Private Methods.

Quick summary for a sample Class:

using UnityEngine;

namespace MaruGame.Core
{
    public class PlayerStats : MonoBehaviour // PascalCase cho Class
    {
        public const int MAX_LEVEL = 99; // UPPER_SNAKE_CASE cho Hằng số

        [SerializeField] private float _baseHealth = 100f; // _camelCase cho Private Field
        [SerializeField] private float _moveSpeed = 5f;

        public float CurrentHealth { get; private set; } // PascalCase cho Property

        private bool _isDead = false; // Tiền tố 'is' cho Boolean

        public void TakeDamage(float damageAmount) // PascalCase cho Hàm, camelCase cho Tham số
        {
            if (_isDead) return;

            CurrentHealth -= damageAmount;

            if (CurrentHealth <= 0)
            {
                Die();
            }
        }

        private void Die()
        {
            _isDead = true;
            // Xử lý logic chết ở đây...
        }
    }
}