--- 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 pool; void Start() { pool = new List(); 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.