Manus에서 모든 스킬 실행
원클릭으로
원클릭으로
원클릭으로 Manus에서 모든 스킬 실행
시작하기game-loop
Server-side game loop implementation with fixed timestep, physics simulation, and tick rate optimization
스타1
포크1
업데이트2025년 12월 30일 11:46
파일 탐색기
7 개 파일SKILL.md
readonly메뉴
Server-side game loop implementation with fixed timestep, physics simulation, and tick rate optimization
Asynchronous programming models including coroutines, async/await, and reactive patterns
Game server communication protocols including gRPC, REST, and custom binary protocols
Efficient data serialization for game networking including Protobuf, FlatBuffers, and custom binary
Game data persistence with player profiles, leaderboards, inventory systems using Redis and PostgreSQL
Game server deployment with Docker, Kubernetes, and global distribution strategies
Game server design patterns including ECS, command pattern, and event sourcing
| name | game-loop |
| description | Server-side game loop implementation with fixed timestep, physics simulation, and tick rate optimization |
| sasmp_version | 1.3.0 |
| version | 2.0.0 |
| bonded_agent | 05-game-loop-developer |
| bond_type | PRIMARY_BOND |
| parameters | {"required":["tick_rate"],"optional":["physics_enabled","max_tick_catchup"],"validation":{"tick_rate":{"type":"integer","min":1,"max":128},"physics_enabled":{"type":"boolean","default":true},"max_tick_catchup":{"type":"integer","min":1,"max":10,"default":5}}} |
| retry_config | {"max_attempts":1,"fallback":"skip_frame"} |
| observability | {"logging":{"level":"info","fields":["tick_number","tick_time_ms","entity_count"]},"metrics":[{"name":"game_tick_duration_seconds","type":"histogram"},{"name":"game_tick_count","type":"counter"},{"name":"game_entity_count","type":"gauge"}]} |
Implement deterministic game loops with fixed timestep for consistent gameplay.
class GameLoop {
constructor(tickRate = 60) {
this.tickRate = tickRate;
this.tickMs = 1000 / tickRate;
this.tick = 0;
this.running = false;
}
start() {
this.running = true;
this.lastTime = process.hrtime.bigint();
this.accumulator = 0n;
this.loop();
}
loop() {
if (!this.running) return;
const now = process.hrtime.bigint();
const deltaNs = now - this.lastTime;
this.lastTime = now;
this.accumulator += deltaNs;
const tickNs = BigInt(Math.round(this.tickMs * 1_000_000));
let ticksProcessed = 0;
while (this.accumulator >= tickNs && ticksProcessed < 5) {
this.update(this.tickMs);
this.tick++;
this.accumulator -= tickNs;
ticksProcessed++;
}
// Prevent spiral of death
if (ticksProcessed === 5) {
this.accumulator = 0n;
console.warn('Tick catchup limit reached');
}
const sleepMs = Math.max(1, this.tickMs - Number(this.accumulator) / 1_000_000);
setTimeout(() => this.loop(), sleepMs);
}
update(dt) {
this.processInputs();
this.updatePhysics(dt);
this.updateEntities(dt);
this.checkGameState();
this.broadcastState();
}
}
| Game Type | Rate | Budget | Rationale |
|---|---|---|---|
| FPS | 60-128 Hz | 8-16ms | Hit precision |
| MOBA | 30-60 Hz | 16-33ms | Balance |
| BR | 20-30 Hz | 33-50ms | Scale |
| MMO | 10-20 Hz | 50-100ms | Massive |
class TickMetrics {
constructor(size = 100) {
this.samples = [];
this.size = size;
}
record(ms) {
this.samples.push(ms);
if (this.samples.length > this.size) this.samples.shift();
}
stats() {
const sorted = [...this.samples].sort((a, b) => a - b);
return {
avg: this.samples.reduce((a, b) => a + b, 0) / this.samples.length,
p50: sorted[Math.floor(sorted.length * 0.5)],
p99: sorted[Math.floor(sorted.length * 0.99)]
};
}
}
| Error | Root Cause | Solution |
|---|---|---|
| Tick spikes | GC pauses | Object pooling |
| Drift | Float precision | Fixed-point math |
| Explosion | Large dt | Cap dt value |
| Stuttering | Variable tick | Fixed timestep |
const stats = metrics.stats();
console.log(`Avg: ${stats.avg.toFixed(2)}ms`);
console.log(`P99: ${stats.p99.toFixed(2)}ms`);
console.log(`Budget: ${(stats.avg / tickBudget * 100).toFixed(1)}%`);
describe('GameLoop', () => {
test('maintains tick rate', async () => {
const loop = new GameLoop(60);
const ticks = [];
loop.update = () => ticks.push(Date.now());
loop.start();
await new Promise(r => setTimeout(r, 100));
loop.running = false;
expect(ticks.length).toBeGreaterThan(4);
});
});
assets/ - Loop templatesreferences/ - Optimization guides