From jengine
Delivers thread-safe object pooling for Unity C# to cut GC in games. Use Rent/Return for bullets/enemies/effects; prewarm pools, shared access, job-system safe.
npx claudepluginhub jasonxudeveloper/jengine --plugin jengineThis skill uses the workspace's default tool permissions.
Thread-safe, lock-free generic object pooling for Unity using CAS operations. Works with job system and async operations.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Thread-safe, lock-free generic object pooling for Unity using CAS operations. Works with job system and async operations.
new JObjectPool<T>(
int maxSize = 64, // Maximum pooled objects (excess discarded)
Action<T> onRent = null, // Called when renting (for initialization)
Action<T> onReturn = null // Called when returning (for cleanup)
)
Note: T must be a reference type with a parameterless constructor (where T : class, new()).
.Rent() - Get object from pool or create new.Return(T obj) - Return object to pool (null values ignored).Clear() - Remove all pooled objects.Prewarm(int count) - Pre-allocate objects (won't exceed maxSize).Count - Current available count (approximate, thread-safe)JObjectPool.Shared<T>() - Global shared pool per type (default config: maxSize=64)var pool = new JObjectPool<Bullet>(maxSize: 100);
var bullet = pool.Rent();
// ... use bullet ...
pool.Return(bullet);
var pool = new JObjectPool<Enemy>(
maxSize: 50,
onRent: static enemy => enemy.Reset()
);
var pool = new JObjectPool<List<int>>(
maxSize: 32,
onReturn: static list => list.Clear()
);
var pool = new JObjectPool<Effect>();
pool.Prewarm(50); // Pre-create during loading screen
// Good for simple reusable objects without custom callbacks
var sb = JObjectPool.Shared<StringBuilder>().Rent();
sb.Append("Hello");
sb.Clear(); // Clean up before returning
JObjectPool.Shared<StringBuilder>().Return(sb);
public sealed class Bullet
{
public Vector3 Position;
public Vector3 Velocity;
public float Damage;
public float Lifetime;
public void Reset()
{
Position = default;
Velocity = default;
Damage = 0;
Lifetime = 0;
}
}
public sealed class BulletManager
{
private readonly JObjectPool<Bullet> _pool = new(
maxSize: 200,
onReturn: static b => b.Reset());
public void Initialize() => _pool.Prewarm(100);
public Bullet Fire(in Vector3 pos, in Vector3 dir, float speed, float damage)
{
var bullet = _pool.Rent();
bullet.Position = pos;
bullet.Velocity = dir * speed;
bullet.Damage = damage;
return bullet;
}
public void Return(Bullet b) => _pool.Return(b);
}
public sealed class Enemy : IPoolable
{
public float Health { get; set; }
public Vector3 Position { get; set; }
public event Action OnDeath;
public void OnSpawn()
{
Health = 100f;
}
public void OnDespawn()
{
OnDeath = null; // Clear delegates to prevent leaks
}
}
public sealed class EnemySpawner
{
private readonly JObjectPool<Enemy> _pool;
public EnemySpawner(int maxSize = 50)
{
_pool = new(
maxSize,
onRent: static e => e.OnSpawn(),
onReturn: static e => e.OnDespawn());
}
public Enemy Spawn(in Vector3 position)
{
var enemy = _pool.Rent();
enemy.Position = position;
return enemy;
}
public void Despawn(Enemy e) => _pool.Return(e);
}
// Use in hot paths to avoid List<T> allocations
public void ProcessNearbyEnemies(in Vector3 center, float radius)
{
var list = JObjectPool.Shared<List<Enemy>>().Rent();
try
{
FindEnemiesNonAlloc(center, radius, list);
foreach (var enemy in list)
{
ProcessEnemy(enemy);
}
}
finally
{
list.Clear();
JObjectPool.Shared<List<Enemy>>().Return(list);
}
}
public static string FormatDamage(float damage, string targetName)
{
var sb = JObjectPool.Shared<StringBuilder>().Rent();
try
{
sb.Append(targetName);
sb.Append(" took ");
sb.Append(damage.ToString("F1"));
sb.Append(" damage");
return sb.ToString();
}
finally
{
sb.Clear();
JObjectPool.Shared<StringBuilder>().Return(sb);
}
}
pool.Return(obj) when donePrewarm() during loading screens