agentleFS
Common-kaCommon-ka/ai-agent-unity-rules/.cursor/rules/unity-performance.mdc

ai-agent-unity-rules / rules

Cursor rule40 starsChanged 12 months agoMITView on GitHub

Performance Optimization

The file

We split this unity-performance.mdc into the rules it gives an agent

2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Unity 6.2 Note: Do not write custom Queue-based pools.

    line 58
  2. Very specific:

    Unity 6.2 Note: Use Awaitable instead of System.Threading.Tasks.

    line 122

≈ “Common” is an estimate: at least 1 in 5 of the 6,668 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
unity-performance.mdc
---
description: "Performance Optimization"
alwaysApply: false
---

# Unity Performance Rules

## Update/FixedUpdate/LateUpdate

### Usage Rules

```
using UnityEngine;

public class PerformanceExample : MonoBehaviour
{
    private Rigidbody _rigidbody;
    
    // ✅ DO: Cache components in Awake/Start
    private void Awake()
    {
        _rigidbody = GetComponent<Rigidbody>();
    }
    
    // ❌ DON'T: Frequent GetComponent calls in Update
    /* 
    private void Update()
    {
        GetComponent<Rigidbody>().AddForce(Vector3.up); // Bad! Expensive call every frame.
    }
    */
    
    private void FixedUpdate()
    {
        // ✅ DO: Physics calculations in FixedUpdate
        _rigidbody.AddForce(Vector3.up);
    }
    
    private void Update()
    {
        // ✅ DO: Input processing and frame-logic in Update
        ProcessInput();
    }
    
    private void LateUpdate()
    {
        // ✅ DO: Camera following logic in LateUpdate (after player moves)
        UpdateCameraPosition();
    }
    
    private void ProcessInput() { /* ... */ }
    private void UpdateCameraPosition() { /* ... */ }
}
```

## Object Pooling (UnityEngine.Pool)

**Unity 6.2 Note:** Do not write custom Queue-based pools. Use the native `UnityEngine.Pool` API.

```
using UnityEngine;
using UnityEngine.Pool;

public class BulletManager : MonoBehaviour
{
    [SerializeField] private Bullet _bulletPrefab;
    
    // Native Unity Pool interface
    private IObjectPool<Bullet> _bulletPool;
    
    private void Awake()
    {
        // Initialize the pool
        _bulletPool = new ObjectPool<Bullet>(
            createFunc: CreateBullet,
            actionOnGet: OnGetBullet,
            actionOnRelease: OnReleaseBullet,
            actionOnDestroy: OnDestroyBullet,
            collectionCheck: true, // Checks for double-return errors (debug only)
            defaultCapacity: 50,
            maxSize: 100
        );
    }

    private Bullet CreateBullet()
    {
        Bullet bullet = Instantiate(_bulletPrefab, transform);
        bullet.SetPool(_bulletPool); // Inject pool reference into the bullet
        return bullet;
    }

    private void OnGetBullet(Bullet bullet) => bullet.gameObject.SetActive(true);
    private void OnReleaseBullet(Bullet bullet) => bullet.gameObject.SetActive(false);
    private void OnDestroyBullet(Bullet bullet) => Destroy(bullet.gameObject);
    
    public void FireBullet(Vector3 position, Vector3 direction)
    {
        Bullet bullet = _bulletPool.Get();
        bullet.transform.position = position;
        bullet.Fire(direction);
    }
}

public class Bullet : MonoBehaviour
{
    private IObjectPool<Bullet> _pool;

    public void SetPool(IObjectPool<Bullet> pool) => _pool = pool;

    public void Fire(Vector3 direction) { /* Physics logic */ }

    private void DisableSelf()
    {
        // ✅ DO: Return to pool instead of Destroy()
        _pool.Release(this);
    }
}
```

## Addressables (NOT Resources)

**Unity 6.2 Note:** Use `Awaitable` instead of `System.Threading.Tasks`.

```
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class AssetLoader : MonoBehaviour
{
    // ❌ DON'T: Resources.Load (Synchronous, increases memory footprint)
    /*
    private void BadLoad()
    {
        GameObject prefab = Resources.Load<GameObject>("Prefabs/Enemy");
    }
    */
    
    private AsyncOperationHandle<GameObject> _handle;
    private GameObject _loadedInstance;

    // ✅ DO: Addressables with Awaitable (Unity 6)
    public async Awaitable LoadEnemyAsync()
    {
        // Avoid loading if already loaded
        if (_handle.IsValid()) return;

        _handle = Addressables.LoadAssetAsync<GameObject>("Enemy");
        
        try 
        {
            await _handle.Task; // Native await integration

            if (_handle.Status == AsyncOperationStatus.Succeeded)
            {
                GameObject prefab = _handle.Result;
                _loadedInstance = Instantiate(prefab);
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"Failed to load asset: {e.Message}");
        }
    }
    
    // ✅ DO: Correct Memory Release
    private void OnDestroy()
    {
        if (_loadedInstance != null) Destroy(_loadedInstance);

        if (_handle.IsValid())
        {
            // Crucial: Release the handle to free memory
            Addressables.Release(_handle);
        }
    }
}
```

## Memory Management (Zero Allocations)

```
using UnityEngine;
using System.Collections.Generic;
using TMPro;
using System.Text;

public class MemoryOptimization : MonoBehaviour
{
    // ✅ DO: Pre-allocate collections to avoid resizing in runtime
    private readonly List<int> _numbers = new List<int>(100); 

    private void Update()
    {
        // ❌ DON'T: New List allocation or resizing in Update
        // List<int> temp = new List<int>(); 
        
        // ✅ DO: Reuse collections
        _numbers.Clear();
        // Fill list...
    }
}

public class UiOptimization : MonoBehaviour
{
    [SerializeField] private TMP_Text _scoreText;
    
    // ✅ DO: Reusable StringBuilder
    private StringBuilder _sb = new StringBuilder(64);

    public void UpdateScore(string playerName, int score)
    {
        _sb.Clear();
        _sb.Append("Player: ").Append(playerName).Append(", Score: ").Append(score);
        
        // ✅ DO: Pass StringBuilder directly to SetText.
        // Avoids .ToString() which creates a new string allocation every frame.
        _scoreText.SetText(_sb);
    }
}
```

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.