Unity C# Best Practices and Coding Standards
unitycsharpcode-styleperformancesecurity
Description
This rule provides best practices for Unity C# development, covering code style, organization, performance, and security to ensure maintainable and efficient projects.
Globs
**/*.cs
---
description: This rule provides best practices for Unity C# development, covering code style, organization, performance, and security to ensure maintainable and efficient projects.
globs: **/*.cs
---
# Unity C# Best Practices and Coding Standards
This document provides a comprehensive guide to best practices for Unity C# development, covering code style, organization, performance, and security, to ensure maintainable and efficient projects.
## I. Code Organization and Structure
### A. Directory Structure Best Practices
A well-organized directory structure is crucial for maintainability and collaboration. Consider the following structure:
Assets/
├── Animations/
│ ├── AnimationClips/
│ └── Animators/
├── Audio/
│ ├── Music/
│ └── SFX/
├── Editor/
│ └── EditorScripts/
├── Fonts/
├── Materials/
├── Models/
├── Plugins/
├── Prefabs/
├── Resources/
├── Scenes/
├── Scripts/
│ ├── Core/
│ ├── Gameplay/
│ ├── UI/
│ ├── Data/
│ ├── Editor/
│ └── Utilities/
├── Textures/
│ ├── UI/
│ └── Environment/
* **Animations:** Contains all animation-related assets.
* **Audio:** Contains music and sound effects.
* **Editor:** Contains custom editor scripts.
* **Fonts:** Contains font assets.
* **Materials:** Contains material assets.
* **Models:** Contains 3D models.
* **Plugins:** Contains third-party plugins.
* **Prefabs:** Contains prefab assets.
* **Resources:** Contains assets loaded at runtime (use sparingly due to performance implications).
* **Scenes:** Contains scene files.
* **Scripts:** Contains all C# scripts, further organized by functionality.
* **Core:** Fundamental scripts and systems.
* **Gameplay:** Scripts related to gameplay mechanics.
* **UI:** User interface scripts.
* **Data:** Scripts related to data management (e.g., ScriptableObjects).
* **Editor:** Custom editor tools and scripts.
* **Utilities:** General-purpose utility scripts.
* **Textures:** Contains texture assets.
### B. File Naming Conventions
Consistent file naming improves project readability and searchability.
* **Scripts:** `PascalCase.cs` (e.g., `PlayerController.cs`)
* **Prefabs:** `PascalCase.prefab` (e.g., `EnemyPrefab.prefab`)
* **Scenes:** `PascalCase.unity` (e.g., `MainMenu.unity`)
* **Materials:** `PascalCase.mat` (e.g., `WaterMaterial.mat`)
* **Textures:** `PascalCase.png` or `PascalCase.jpg` (e.g., `GroundTexture.png`)
* **Animations:** `PascalCase.anim` (e.g., `PlayerIdle.anim`)
Follow these conventions:
* Use PascalCase for class names, methods, and properties.
* Use camelCase for variables and parameters.
* Use UPPER_SNAKE_CASE for constants.
### C. Module Organization Best Practices
For larger projects, consider organizing code into modules or namespaces.
* **Namespaces:** Group related classes within a namespace. This avoids naming collisions and improves code organization. Use namespaces that reflect the folder structure.
csharp
namespace MyGame.Gameplay
{
public class PlayerController : MonoBehaviour
{
// ...
}
}
* **Assembly Definitions:** Use Assembly Definition files (`.asmdef`) to define modules. This enables faster compilation times and better code isolation. Place each module in its own folder with an assembly definition file.
### D. Component Architecture Recommendations
Unity uses a component-based architecture. Design your game objects with small, reusable components that handle specific responsibilities.
* **Single Responsibility Principle:** Each component should have one specific responsibility.
* **Composition over Inheritance:** Favor composition over inheritance to create complex behavior.
Example:
Instead of a monolithic `Player` script, use separate components like `PlayerMovement`, `PlayerHealth`, and `PlayerAttack`.
### E. Code Splitting Strategies
* **Partial Classes:** Split large classes into multiple files using the `partial` keyword.
csharp
// PlayerController.cs
public partial class PlayerController : MonoBehaviour
{
// Movement logic
}
// PlayerController.Combat.cs
public partial class PlayerController : MonoBehaviour
{
// Combat logic
}
* **Extension Methods:** Add functionality to existing classes without modifying their source code.
csharp
public static class StringExtensions
{
public static string Capitalize(this string str)
{
return char.ToUpper(str[0]) + str.Substring(1);
}
}
// Usage
string name = "john";
string capitalizedName = name.Capitalize(); // John
## II. Common Patterns and Anti-patterns
### A. Design Patterns
* **Singleton:** Ensure a class has only one instance and provides a global point of access to it (use carefully, as overuse can lead to tight coupling).
csharp
public class GameManager : MonoBehaviour
{
private static GameManager _instance;
public static GameManager Instance
{
get { return _instance; }
}
private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(this.gameObject);
} else {
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
}
* **Object Pooling:** Reuse objects instead of creating and destroying them frequently to reduce garbage collection overhead.
csharp
public class ObjectPool : MonoBehaviour
{
public GameObject pooledObject;
public int poolSize = 10;
private List<GameObject> pool;
void Start()
{
pool = new List<GameObject>();
for (int i = 0; i < poolSize; i++)
{
GameObject obj = (GameObject)Instantiate(pooledObject);
obj.SetActive(false);
pool.Add(obj);
}
}
public GameObject GetPooledObject()
{
for (int i = 0; i < pool.Count; i++)
{
if (!pool[i].activeInHierarchy)
{
return pool[i];
}
}
return null; // Or Instantiate more if needed
}
}
* **Factory:** Create objects without specifying the exact class of object that will be created.
* **Observer:** Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
* **Command:** Encapsulate a request as an object, thereby allowing for parameterizing clients with queues, requests, and operations.
### B. Recommended Approaches for Common Tasks
* **Input Handling:** Use the new Input System for more flexible and customizable input handling.
* **UI Development:** Utilize Unity's UI system (Canvas, RectTransform, UI components) for creating user interfaces.
* **Data Persistence:** Use `PlayerPrefs` for simple data storage, and consider using serialization for more complex data structures. Alternatively, consider using a database like SQLite for more complex data storage needs.
* **Networking:** Use Unity's `Netcode for GameObjects` for multiplayer games. Also consider third-party networking solutions like Photon.
* **Asynchronous Operations:** Employ `async/await` to avoid blocking the main thread when performing long-running operations (e.g., loading assets, networking).
### C. Anti-patterns and Code Smells
* **God Classes:** Avoid creating classes that do too much. Split functionality into smaller, more manageable classes.
* **Spaghetti Code:** Avoid complex, unstructured code that is difficult to understand and maintain. Use modular design and clear coding conventions.
* **Magic Numbers:** Avoid hardcoded numerical values in your code. Use named constants instead.
* **Overuse of `FindGameObjectWithTag` or `GetComponentInChildren`:** These methods can be slow. Cache references to frequently used objects and components.
* **Using `Resources.Load` excessively:** `Resources.Load` can lead to performance issues. Use AssetBundles or Addressables for better asset management.
* **Relying heavily on `Update()` for everything:** Minimize the code in the `Update()` loop to avoid performance bottlenecks. Use events, coroutines, and other techniques to handle tasks outside of the main loop.
### D. State Management Best Practices
* **State Machines:** Use state machines to manage complex object behavior.
csharp
public enum PlayerState
{
Idle,
Walking,
Jumping,
Attacking
}
public class PlayerController : MonoBehaviour
{
public PlayerState currentState = PlayerState.Idle;
void Update()
{
switch (currentState)
{
case PlayerState.Idle:
// Handle idle state logic
break;
case PlayerState.Walking:
// Handle walking state logic
break;
// ...
}
}
}
* **ScriptableObjects:** Use ScriptableObjects to store game data and configuration parameters.
csharp
[CreateAssetMenu(fileName = "WeaponData", menuName = "Game Data/Weapon Data", order = 1)]
public class WeaponData : ScriptableObject
{
public string weaponName;
public int damage;
public float fireRate;
}
### E. Error Handling Patterns
* **Try-Catch Blocks:** Use try-catch blocks to handle exceptions gracefully. Log exceptions and provide informative error messages.
* **Assertions:** Use assertions to validate assumptions in your code.
* **Null Checks:** Check for null references before accessing objects to prevent NullReferenceExceptions.
* **Custom Exceptions:** Create custom exception classes to handle specific error conditions in your game.
## III. Performance Considerations
### A. Optimization Techniques
* **Object Pooling:** Reuse objects to reduce garbage collection.
* **Caching:** Cache frequently accessed data to avoid repeated calculations or lookups.
* **String Concatenation:** Use `StringBuilder` for efficient string concatenation.
* **Minimize Garbage Collection:** Avoid creating temporary objects in frequently executed code.
* **Disable Unused Components:** Disable components that are not currently needed.
* **Reduce Draw Calls:** Batch static objects, use texture atlases, and optimize materials to reduce draw calls.
* **LOD (Level of Detail):** Use LOD groups to reduce the polygon count of objects at a distance.
* **Occlusion Culling:** Occlude objects that are not visible to the camera.
* **Use Profiler:** Regularly use the Unity Profiler to identify performance bottlenecks.
### B. Memory Management
* **Asset Bundles:** Use Asset Bundles to load and unload assets dynamically, reducing the memory footprint of your game.
* **Addressable Asset System:** Use Addressables for an even more flexible asset management system.
* **Unload Unused Assets:** Call `Resources.UnloadUnusedAssets()` to release unused assets from memory.
* **Weak References:** Use weak references to avoid memory leaks when referencing objects that may be destroyed.
### C. Rendering Optimization
* **Optimize Shaders:** Use simple shaders and avoid complex calculations in shaders.
* **Texture Compression:** Compress textures to reduce memory usage and improve rendering performance.
* **Mipmapping:** Use mipmaps to reduce aliasing and improve performance.
* **Lightmapping:** Bake static lighting to reduce real-time lighting calculations.
* **Shadows:** Optimize shadow settings and reduce the number of real-time shadows.
* **Post-Processing:** Use post-processing effects sparingly, as they can be performance intensive.
### D. Bundle Size Optimization
* **Texture Compression:** Compress textures to reduce their size.
* **Audio Compression:** Compress audio files to reduce their size.
* **Remove Unused Assets:** Delete unused assets from your project.
* **Use Asset Bundles:** Split your game into multiple Asset Bundles to allow users to download only the content they need.
* **Stripping Level:** Configure stripping level in project settings to remove unused code.
### E. Lazy Loading
* **Load Assets Asynchronously:** Load assets in the background using `async/await` or coroutines.
* **Load Scenes Additively:** Load scenes additively to avoid interrupting gameplay.
* **Stream Assets:** Stream large assets from disk or the network instead of loading them into memory all at once.
## IV. Security Best Practices
### A. Common Vulnerabilities
* **Code Injection:** Prevent code injection by validating user input and avoiding the use of `eval` or similar functions.
* **Data Tampering:** Protect game data from tampering by using encryption and checksums.
* **Man-in-the-Middle Attacks:** Use HTTPS for all network communication to prevent man-in-the-middle attacks.
* **Denial of Service (DoS):** Protect your server from DoS attacks by implementing rate limiting and input validation.
### B. Input Validation
* **Validate All User Input:** Validate all user input to prevent code injection, data tampering, and other attacks.
* **Use Whitelisting:** Use whitelisting to allow only specific characters or values in user input.
* **Limit Input Length:** Limit the length of user input to prevent buffer overflows.
* **Sanitize Input:** Sanitize user input to remove potentially harmful characters or code.
### C. Authentication and Authorization
* **Use Secure Authentication:** Use a secure authentication method such as OAuth 2.0 or JWT (JSON Web Tokens).
* **Implement Authorization:** Implement authorization to control access to resources based on user roles and permissions.
* **Store Passwords Securely:** Hash passwords using a strong hashing algorithm such as bcrypt or Argon2.
* **Use Multi-Factor Authentication:** Use multi-factor authentication to add an extra layer of security.
### D. Data Protection
* **Encrypt Sensitive Data:** Encrypt sensitive data such as passwords, API keys, and payment information.
* **Use Secure Storage:** Store sensitive data in a secure storage location such as the keychain or a hardware security module (HSM).
* **Obfuscate Code:** Obfuscate your code to make it more difficult for attackers to reverse engineer.
### E. Secure API Communication
* **Use HTTPS:** Use HTTPS for all API communication.
* **Validate API Responses:** Validate API responses to prevent data injection and other attacks.
* **Use API Keys:** Use API keys to authenticate requests to your API.
* **Implement Rate Limiting:** Implement rate limiting to prevent abuse of your API.
## V. Testing Approaches
### A. Unit Testing
* **Isolate Components:** Write unit tests for individual components in isolation.
* **Use a Testing Framework:** Use a unit testing framework such as NUnit or Unity Test Runner.
* **Test Edge Cases:** Test edge cases and boundary conditions.
* **Write Mock Objects:** Use mock objects to simulate dependencies.
* **Follow AAA Pattern:** Arrange, Act, Assert.
### B. Integration Testing
* **Test Interactions:** Test the interactions between multiple components.
* **Use Test Scenes:** Create test scenes to isolate integration tests.
* **Simulate Real-World Scenarios:** Simulate real-world scenarios to test the behavior of your game under different conditions.
* **Use Data-Driven Tests:** Use data-driven tests to test multiple scenarios with different input data.
### C. End-to-End Testing
* **Test the Entire Game Flow:** Test the entire game flow from start to finish.
* **Use Automated Testing Tools:** Use automated testing tools such as Selenium or Appium.
* **Test on Multiple Platforms:** Test your game on multiple platforms to ensure compatibility.
* **Involve Testers:** Involve human testers to identify usability issues and other problems.
### D. Test Organization
* **Create a Test Directory:** Create a separate directory for your tests.
* **Mirror the Source Directory:** Mirror the structure of your source directory in your test directory.
* **Name Test Classes Consistently:** Name your test classes consistently (e.g., `PlayerControllerTests`).
* **Group Tests by Functionality:** Group your tests by functionality.
### E. Mocking and Stubbing
* **Use Mocking Frameworks:** Use mocking frameworks such as Moq or NSubstitute.
* **Create Interfaces:** Create interfaces for your dependencies to make them easier to mock.
* **Avoid Hardcoded Dependencies:** Avoid hardcoded dependencies in your code.
* **Use Dependency Injection:** Use dependency injection to inject mock objects into your code.
## VI. Common Pitfalls and Gotchas
### A. Frequent Mistakes
* **Ignoring Performance:** Neglecting performance optimization from the beginning of the project.
* **Overcomplicating Code:** Writing complex code when simpler solutions exist.
* **Not Using Version Control:** Failing to use version control (e.g., Git) to manage code changes.
* **Poor Asset Management:** Poorly organizing and managing assets, leading to project bloat.
* **Neglecting Testing:** Not writing unit tests, integration tests, or end-to-end tests.
* **Misunderstanding Coroutines:** Improper use or overuse of coroutines leading to unexpected behavior or memory leaks.
* **Incorrect Use of `Time.deltaTime`:** Using `Time.deltaTime` incorrectly, leading to frame-rate dependent behavior.
### B. Edge Cases
* **Floating Point Precision:** Be aware of floating-point precision issues when comparing floating-point numbers.
* **Garbage Collection Spikes:** Be aware of garbage collection spikes and try to minimize garbage generation.
* **Platform Differences:** Test your game on different platforms to ensure compatibility.
* **Screen Size and Resolution:** Handle different screen sizes and resolutions gracefully.
### C. Version-Specific Issues
* **API Changes:** Be aware of API changes between different versions of Unity.
* **Bug Fixes:** Be aware of bug fixes in different versions of Unity.
* **Feature Deprecations:** Be aware of feature deprecations in different versions of Unity.
### D. Compatibility Concerns
* **.NET Framework:** Be aware of the .NET Framework version used by your project.
* **Third-Party Plugins:** Ensure that third-party plugins are compatible with your version of Unity.
* **Platform SDKs:** Ensure that the platform SDKs you are using are compatible with your version of Unity.
### E. Debugging Strategies
* **Use the Unity Debugger:** Use the Unity debugger to step through your code and inspect variables.
* **Use Debug.Log:** Use `Debug.Log` to print messages to the console.
* **Use Assertions:** Use assertions to validate assumptions in your code.
* **Use the Unity Profiler:** Use the Unity Profiler to identify performance bottlenecks.
* **Remote Debugging:** Use remote debugging to debug your game on a device.
## VII. Tooling and Environment
### A. Recommended Development Tools
* **Visual Studio or Visual Studio Code:** Use a powerful IDE for C# development.
* **Unity Asset Store:** Explore the Unity Asset Store for useful tools and assets.
* **Version Control System (Git):** Use a version control system to manage code changes.
* **Project Management Tool (Jira, Trello):** Use a project management tool to track tasks and bugs.
* **Code Editor Extensions:** Use code editor extensions for linting, formatting, and code completion.
### B. Build Configuration
* **Use Development Builds:** Use development builds for testing and debugging.
* **Use Release Builds:** Use release builds for production deployments.
* **Configure Build Settings:** Configure build settings such as scripting backend, target architecture, and optimization level.
* **Use Scripting Define Symbols:** Use scripting define symbols to enable or disable code based on the build configuration.
### C. Linting and Formatting
* **Use StyleCop Analyzers:** Use StyleCop Analyzers to enforce coding style rules.
* **Use EditorConfig:** Use EditorConfig to define coding style settings for your project.
* **Use a Code Formatter:** Use a code formatter to automatically format your code.
### D. Deployment Best Practices
* **Test on Target Platforms:** Test your game on the target platforms before deployment.
* **Submit to App Stores:** Submit your game to the appropriate app stores.
* **Monitor Performance:** Monitor the performance of your game after deployment.
* **Gather User Feedback:** Gather user feedback to improve your game.
### E. CI/CD Integration
* **Use a CI/CD Platform:** Use a CI/CD platform such as Jenkins, Travis CI, or GitHub Actions.
* **Automate Builds and Tests:** Automate builds and tests to ensure code quality.
* **Automate Deployments:** Automate deployments to streamline the release process.
* **Integrate with Version Control:** Integrate your CI/CD pipeline with your version control system.
By following these best practices, you can create maintainable, efficient, and secure Unity C# projects.