game-patterns
Provides zero-allocation C# patterns for performance-critical game systems like object pooling and timers.
Install
mkdir -p .claude/skills/game-patterns && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/2214" && unzip -o skill.zip -d .claude/skills/game-patterns && rm skill.zipInstalls to .claude/skills/game-patterns
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Zero-GC game patterns with JEngine using modern C# 9+. Triggers on: game loop, spawn system, wave spawner, cooldown, ability timer, damage over time, DoT, health regen, bullet pool, enemy pool, object pool pattern, projectile system, combat system, zero allocation, no GC, performance optimizationKey capabilities
- →Implement zero-GC game loops
- →Create pooled object systems
- →Manage asynchronous ability timers
- →Build damage-over-time systems
- →Optimize memory usage with JEngine
How it works
The agent uses JAction and JObjectPool to manage state objects as reference types, preventing garbage collection during high-frequency game events.
Inputs & outputs
When to use game-patterns
- →Creating object pools for bullets or enemies
- →Implementing zero-allocation ability cooldowns
- →Building high-performance game loops
About this skill
Game Patterns with JEngine
Zero-GC patterns using JAction + JObjectPool with modern C# 9+ features.
Principles
- Async-First: Always use
ExecuteAsync()(non-blocking) - Zero-GC: Use static lambdas + reference type state + object pooling
- Modern C#: Target-typed new, init properties, Span<T>, in parameters
State Classes for Zero-GC
JAction state parameters must be reference types to avoid boxing. Pool these state objects for true zero-GC:
// Reusable state for abilities
public sealed class AbilityState
{
public bool CanUse { get; set; } = true;
public float Cooldown { get; init; }
}
// Reusable state for DoT effects
public sealed class DoTState
{
public IDamageable Target { get; set; }
public float DamagePerTick { get; set; }
public int TicksRemaining { get; set; }
public void Reset()
{
Target = null;
DamagePerTick = 0;
TicksRemaining = 0;
}
}
// Reusable state for timers
public sealed class TimerState
{
public float Duration { get; set; }
public float Elapsed { get; set; }
public Action OnComplete { get; set; }
public void Reset()
{
Duration = 0;
Elapsed = 0;
OnComplete = null;
}
}
Combat Patterns
Ability with Cooldown (Zero-GC)
public sealed class AbilitySystem
{
private readonly AbilityState _state = new() { Cooldown = 2f };
public async UniTaskVoid UseAbility()
{
if (!_state.CanUse) return;
_state.CanUse = false;
PerformAbility();
using var action = await JAction.Create()
.Delay(_state.Cooldown)
.Do(static s => s.CanUse = true, _state)
.ExecuteAsync();
}
}
Damage Over Time (Zero-GC Pooled State)
public static class DoTSystem
{
public static async UniTaskVoid Apply(
IDamageable target,
float damagePerTick,
int ticks,
float interval)
{
var state = JObjectPool.Shared<DoTState>().Rent();
state.Target = target;
state.DamagePerTick = damagePerTick;
using var action = await JAction.Create()
.Repeat(
static s => s.Target?.TakeDamage(s.DamagePerTick),
state,
count: ticks,
interval: interval)
.ExecuteAsync();
state.Reset();
JObjectPool.Shared<DoTState>().Return(state);
}
}
Combo System (Zero-GC)
public sealed class ComboState
{
public int Count;
public JAction ResetAction;
}
public sealed class ComboSystem : IDisposable
{
private readonly ComboState _state = new();
public int ComboCount => _state.Count;
public void OnHit()
{
_state.ResetAction?.Cancel();
_state.ResetAction?.Dispose();
_state.Count++;
_state.ResetAction = JAction.Create()
.Delay(1.5f)
.Do(static s => s.Count = 0, _state);
_ = _state.ResetAction.ExecuteAsync();
}
/// <summary>
/// Call from OnDestroy to prevent callbacks on destroyed objects.
/// </summary>
public void Dispose()
{
_state.ResetAction?.Cancel();
_state.ResetAction?.Dispose();
_state.ResetAction = null;
}
}
Spawning Patterns
Wave Spawner (Async)
public sealed class WaveSpawner
{
private readonly EnemySpawner _spawner;
// Use ReadOnlyMemory<T> for async (ReadOnlySpan<T> is a ref struct, invalid in async)
// Access .Span inside the loop for zero-allocation iteration
public async UniTask RunWaves(ReadOnlyMemory<WaveConfig> waves, CancellationToken ct = default)
{
foreach (var wave in waves.Span)
{
using var action = await JAction.Create()
.Delay(wave.StartDelay)
.Do(() => SpawnWave(wave.EnemyCount))
.WaitUntil(() => ActiveCount == 0, timeout: 120f)
.ExecuteAsync();
if (action.Cancelled || ct.IsCancellationRequested) break;
}
}
private void SpawnWave(int count)
{
for (var i = 0; i < count; i++)
{
var pos = GetSpawnPosition(i);
_spawner.Spawn(in pos);
}
}
}
Pooled Spawner (Generic, Zero-GC)
public interface IPoolable
{
void OnSpawn();
void OnDespawn();
}
public sealed class PooledSpawner<T> where T : class, IPoolable, new()
{
private readonly JObjectPool<T> _pool;
public PooledSpawner(int maxSize = 64)
{
_pool = new(
maxSize,
onRent: static obj => obj.OnSpawn(),
onReturn: static obj => obj.OnDespawn());
}
public T Spawn() => _pool.Rent();
public void Despawn(T obj) => _pool.Return(obj);
public void Prewarm(int count) => _pool.Prewarm(count);
public int PooledCount => _pool.Count;
}
Resource Patterns
Health Regeneration (Zero-GC)
public sealed class RegenState
{
public float Current;
public float Max;
public float PerTick;
public JAction Action;
public void Cleanup()
{
Action?.Cancel();
Action?.Dispose();
Action = null;
}
}
public static class RegenSystem
{
public static void Start(RegenState state, float hpPerSecond)
{
state.Cleanup();
state.PerTick = hpPerSecond * 0.1f;
state.Action = JAction.Create()
.RepeatWhile(
static s => s.Current = MathF.Min(s.Current + s.PerTick, s.Max),
static s => s.Current < s.Max,
state,
frequency: 0.1f);
_ = state.Action.ExecuteAsync();
}
/// <summary>
/// Stop regeneration. Call Cleanup() from OnDestroy to fully dispose.
/// </summary>
public static void Stop(RegenState state) => state.Cleanup();
}
Projectile Patterns
Bullet Manager (Zero-GC)
public sealed class Bullet
{
public Vector3 Position;
public Vector3 Velocity;
public float Damage;
public bool Active;
public void Reset()
{
Position = default;
Velocity = default;
Damage = 0;
Active = false;
}
}
public sealed class BulletManager
{
public static BulletManager Instance { get; private set; }
private readonly JObjectPool<Bullet> _pool = new(
maxSize: 500,
onReturn: static b => b.Reset());
public void Initialize()
{
Instance = this;
_pool.Prewarm(200);
}
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;
bullet.Active = true;
return bullet;
}
public void Return(Bullet b) => _pool.Return(b);
}
Auto-Return After Lifetime (Zero-GC)
public sealed class BulletLifetimeState
{
public Bullet Bullet;
public BulletManager Manager;
public void Reset()
{
Bullet = null;
Manager = null;
}
}
public static async UniTaskVoid FireWithLifetime(
BulletManager manager,
in Vector3 pos,
in Vector3 dir,
float speed,
float damage,
float lifetime)
{
var bullet = manager.Fire(in pos, in dir, speed, damage);
var state = JObjectPool.Shared<BulletLifetimeState>().Rent();
state.Bullet = bullet;
state.Manager = manager;
using var action = await JAction.Create()
.Delay(lifetime)
.Do(static s => s.Manager?.Return(s.Bullet), state)
.ExecuteAsync();
state.Reset();
JObjectPool.Shared<BulletLifetimeState>().Return(state);
}
UI Patterns
Delayed Tooltip (Zero-GC)
public sealed class TooltipState
{
public JAction Action;
public Action ShowCallback;
public Action HideCallback;
public void Reset()
{
Action = null;
ShowCallback = null;
HideCallback = null;
}
}
public sealed class TooltipTrigger : IDisposable
{
private readonly TooltipState _state = new();
public void OnPointerEnter(Action show, Action hide)
{
_state.Action?.Cancel();
_state.Action?.Dispose();
_state.ShowCallback = show;
_state.HideCallback = hide;
_state.Action = JAction.Create()
.Delay(0.5f)
.Do(static s => s.ShowCallback?.Invoke(), _state);
_ = _state.Action.ExecuteAsync();
}
public void OnPointerExit()
{
_state.Action?.Cancel();
_state.Action?.Dispose();
_state.Action = null;
_state.HideCallback?.Invoke();
}
/// <summary>
/// Call from OnDestroy to prevent callbacks on destroyed objects.
/// </summary>
public void Dispose()
{
_state.Action?.Cancel();
_state.Action?.Dispose();
_state.Reset();
}
}
Typewriter Effect (Zero-GC)
public sealed class TypewriterState
{
public string FullText;
public int CurrentIndex;
public Action<string> OnUpdate;
public StringBuilder Builder;
public void Reset()
{
FullText = null;
CurrentIndex = 0;
OnUpdate = null;
Builder = null;
}
}
public static async UniTask TypeText(string content, float charDelay, Action<string> onUpdate)
{
var state = JObjectPool.Shared<TypewriterState>().Rent();
var sb = JObjectPool.Shared<StringBuilder>().Rent();
state.FullText = content;
state.CurrentIndex = 0;
state.OnUpdate = onUpdate;
state.Builder = sb;
using var action = await JAction.Create()
.Repeat(
static s =>
{
if (s.CurrentIndex < s.FullText.Length)
{
s.Builder.Append(s.FullText[s.CurrentIndex++]);
s.OnUpdate?.Invoke(s.Builder.ToString());
}
},
state,
---
*Content truncated.*
When not to use it
- →Non-game development projects
Prerequisites
Limitations
- →Requires strict adherence to reference-type state management
How it compares
It enforces zero-allocation patterns by pooling state objects, whereas standard C# approaches often create frequent short-lived objects.
Compared to similar skills
game-patterns side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| game-patterns (this skill) | 9 | 6mo | No flags | Advanced |
| dev_unity_unreal | 0 | 6mo | No flags | Advanced |
| csharp-developer | 43 | 2mo | No flags | Advanced |
| csharp-pro | 9 | 4mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by JasonXuDeveloper
View all by JasonXuDeveloper →You might also like
dev_unity_unreal
AllurinsX
dev_unity_unreal — an agent skill by AllurinsX.
csharp-developer
zenobi-us
Expert C# developer specializing in modern .NET development, ASP.NET Core, and cloud-native applications. Masters C# 12 features, Blazor, and cross-platform development with emphasis on performance and clean architecture.
csharp-pro
sickn33
Write modern C# code with advanced features like records, pattern matching, and async/await. Optimizes .NET applications, implements enterprise patterns, and ensures comprehensive testing. Use PROACTIVELY for C# refactoring, performance optimization, or complex .NET solutions.
performance-benchmark
dotnet
Generate and run ad hoc performance benchmarks to validate code changes. Use this when asked to benchmark, profile, or validate the performance impact of a code change in dotnet/runtime.
write-script-csharp
windmill-labs
MUST use when writing C# scripts.
add-api
mono
Add new C# APIs to SkiaSharp by wrapping Skia C++ functionality. Structured 6-phase workflow: C++ analysis → C API creation → submodule commits → binding generation → C# wrapper → testing. Triggers: - Issue classified as "New API" (after fetching and classification) - Direct request: "add DrawFoo method", "expose SkSurface::draw", "wrap sk_foo_bar" - Keywords: "add API", "expose function", "wrap method", "create binding for"