Coding Conventions
Minh Khoa
Author
Below 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
Icapital 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:
MaxHealthormoveSpeed. - Advice: Avoid using
publicfield to prevent other classes from modifying it arbitrarily. UsePropertiesor[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 ofdead)hasKey(Instead ofkey)canJump(Instead ofjumpable)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()orOnLevelCompletedHandler().
8. Coroutines (Unity's asynchronous functions)
-
Rules: Usually add the suffix
RoutineorCoroutineto 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
.csmust exactly match the name of the main Class inside. (E.g.: fileGameManager.csmust contain classGameManager). - 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.).
- Use XML Comments (
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 letterA, 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.
- Add an underscore
- 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-BallsorCoreBounceBalls).
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(orplayBtn)_healthText(orhealthTxt)_loadingBarImage(orloadingImg)_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.
- Class names and file names usually come with the suffix
- 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).
- Use PascalCase. For large projects, it is often also added capitalized prefixes (separated by
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")orhealth -= 15;). -
Solution: Declare them as constant variables (
const),readonlyor 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
#regionand#endregionto 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...
}
}
}