---
id: wiki-2026-0508-combat-system-bullet-pipeline
title: Combat System & Bullet Interaction Pipeline
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [bullet hell, combat system, object pool, broad-phase, narrow-phase, collision detection, homing missile]
duplicate_of: none
source_trust_level: B
confidence_score: 0.85
verification_status: applied
tags: [game-design, combat, collision-detection, object-pool, spatial-partition, bullet-hell, performance]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: TypeScript / C++
  framework: Game engine
---

# Combat System & Bullet Pipeline

## 매 한 줄
> **"매 thousands of bullet 의 60 FPS"**. 매 object pool + 매 broad-phase (spatial grid / quadtree) + 매 narrow-phase (circle-circle / OBB). 매 modern: GPU bullet (compute shader). 매 bullet hell + STG + arena game 의 backbone.

## 매 핵심

### 매 architecture
- **CombatSystem**: 매 hub.
- **BulletPool**: 매 reuse.
- **SpatialPartition**: 매 broad-phase.
- **CollisionResolver**: 매 narrow-phase.
- **HomingController**.
- **DamageEvent**.

### Object pooling
- 매 instantiate / destroy 의 GC pressure.
- 매 N pre-create + reuse.
- 매 typical: 매 1000-10000 bullet pool.

### Spatial partitioning (broad-phase)

#### Uniform Grid
- 매 simple, 매 fast.
- 매 bullet hell 의 typical.
- 매 cell size = 매 max bullet radius × 2.

#### Quadtree
- 매 sparse 의 better.
- 매 dynamic.

#### Spatial Hash
- 매 grid 의 hash version.
- 매 unbounded space.

#### BVH
- 매 raytracing-style.
- 매 complex shape.

### Narrow-phase

#### Circle-Circle
- 매 distance < r1 + r2.
- 매 fastest.

#### AABB
- 매 axis-aligned box.
- 매 bullet hell 의 enough.

#### OBB
- 매 oriented box.
- 매 rotation 의 sensitive.

#### SAT (Separating Axis Theorem)
- 매 polygon-polygon.

### Homing missile
- 매 target acquisition.
- 매 angular velocity 의 limit.
- 매 turn rate.

### Bullet pattern (danmaku)
- 매 spiral, fan, ring, aimed, random spread.
- 매 emitter 의 parameter.

### 매 modern (GPU bullet)
- 매 compute shader 의 1M+ bullet.
- 매 [[Compute Shader]] 참조.

### 매 hit feedback
- 매 visual: hitstop, screen shake, particle.
- 매 audio: 매 spatial.
- 매 game feel 의 make-or-break.

## 💻 패턴

### Object pool
```ts
class BulletPool {
  private pool: Bullet[] = [];
  private active: Set<Bullet> = new Set();
  
  constructor(size: number) {
    for (let i = 0; i < size; i++) {
      this.pool.push(new Bullet());
    }
  }
  
  spawn(pos: Vec2, vel: Vec2): Bullet | null {
    const b = this.pool.pop();
    if (!b) return null;  // 매 pool exhausted
    b.reset(pos, vel);
    this.active.add(b);
    return b;
  }
  
  release(b: Bullet) {
    this.active.delete(b);
    this.pool.push(b);
  }
}
```

### Uniform Grid (broad-phase)
```ts
class SpatialGrid {
  private cells = new Map<string, Bullet[]>();
  
  constructor(private cellSize: number) {}
  
  private key(x: number, y: number): string {
    return `${Math.floor(x / this.cellSize)},${Math.floor(y / this.cellSize)}`;
  }
  
  rebuild(bullets: Bullet[]) {
    this.cells.clear();
    for (const b of bullets) {
      const k = this.key(b.pos.x, b.pos.y);
      if (!this.cells.has(k)) this.cells.set(k, []);
      this.cells.get(k)!.push(b);
    }
  }
  
  nearby(pos: Vec2): Bullet[] {
    const result: Bullet[] = [];
    const cx = Math.floor(pos.x / this.cellSize);
    const cy = Math.floor(pos.y / this.cellSize);
    for (let dx = -1; dx <= 1; dx++) {
      for (let dy = -1; dy <= 1; dy++) {
        const k = `${cx + dx},${cy + dy}`;
        if (this.cells.has(k)) result.push(...this.cells.get(k)!);
      }
    }
    return result;
  }
}
```

### Circle-Circle (narrow)
```ts
function circleCircle(a: Vec2, ra: number, b: Vec2, rb: number): boolean {
  const dx = a.x - b.x;
  const dy = a.y - b.y;
  const sumR = ra + rb;
  return dx * dx + dy * dy < sumR * sumR;  // 매 sqrt 의 avoid
}
```

### Homing missile
```ts
class HomingMissile {
  pos: Vec2;
  vel: Vec2;
  target: Vec2;
  maxTurnRate = 0.05;  // 매 rad / frame
  
  update(dt: number) {
    const toTarget = this.target.sub(this.pos);
    const desiredAngle = Math.atan2(toTarget.y, toTarget.x);
    const currentAngle = Math.atan2(this.vel.y, this.vel.x);
    
    let diff = desiredAngle - currentAngle;
    while (diff > Math.PI) diff -= 2 * Math.PI;
    while (diff < -Math.PI) diff += 2 * Math.PI;
    
    const turn = clamp(diff, -this.maxTurnRate, this.maxTurnRate);
    const newAngle = currentAngle + turn;
    const speed = this.vel.length();
    
    this.vel = new Vec2(Math.cos(newAngle) * speed, Math.sin(newAngle) * speed);
    this.pos = this.pos.add(this.vel.mul(dt));
  }
}
```

### Danmaku pattern (spiral)
```ts
class SpiralEmitter {
  angle = 0;
  fireRate = 0.05;  // 매 sec between
  rotation = 0.1;   // 매 rad / fire
  
  update(dt: number) {
    this.fireTimer += dt;
    while (this.fireTimer > this.fireRate) {
      this.fireTimer -= this.fireRate;
      
      const angle = this.angle;
      const vx = Math.cos(angle) * 200;
      const vy = Math.sin(angle) * 200;
      
      bulletPool.spawn(this.pos, new Vec2(vx, vy));
      
      this.angle += this.rotation;
    }
  }
}
```

### Hitstop (game feel)
```ts
class HitStop {
  remaining = 0;
  
  trigger(durationMs: number) {
    this.remaining = Math.max(this.remaining, durationMs);
  }
  
  shouldUpdateGame(dt: number): boolean {
    if (this.remaining > 0) {
      this.remaining -= dt;
      return false;  // 매 freeze game logic
    }
    return true;
  }
}

// 매 trigger 매 hit
onHit() {
  hitStop.trigger(50);  // 매 50ms freeze — 매 impact feel
  screenShake.trigger(amplitude=5, duration=200);
  particleSystem.spawn(...);
}
```

### Bullet update loop (60 FPS)
```ts
function gameLoop(dt: number) {
  // 매 1. spawn (emitter)
  for (const e of emitters) e.update(dt);
  
  // 매 2. update bullet positions
  for (const b of activeBullets) {
    b.pos = b.pos.add(b.vel.mul(dt));
    if (b.pos.outside(screenBounds)) bulletPool.release(b);
  }
  
  // 매 3. broad-phase
  grid.rebuild([...activeBullets]);
  
  // 매 4. narrow-phase
  for (const player of players) {
    for (const b of grid.nearby(player.pos)) {
      if (circleCircle(player.pos, player.radius, b.pos, b.radius)) {
        player.takeHit(b);
        bulletPool.release(b);
        hitStop.trigger(50);
      }
    }
  }
  
  // 매 5. render
  render(activeBullets, players, enemies);
}
```

### GPU bullet (compute shader)
```js
// 매 [[Compute Shader]] 참조
// 매 1M bullet 의 GPU 의 update + collision
const computeShader = device.createComputePipeline({
  compute: { module: bulletUpdateShader, entryPoint: 'main' },
});

// 매 매 frame
const pass = encoder.beginComputePass();
pass.setPipeline(computeShader);
pass.dispatchWorkgroups(Math.ceil(N_BULLETS / 64));
pass.end();
```

### Damage event
```ts
class DamageEvent {
  constructor(
    public source: Bullet,
    public target: Entity,
    public amount: number,
    public type: DamageType,  // 매 burst, sustain, area
  ) {}
  
  apply() {
    const reduced = this.target.applyResistance(this.amount, this.type);
    this.target.hp -= reduced;
    return reduced;
  }
}
```

## 🤔 결정 기준
| 상황 | Approach |
|---|---|
| 100s of bullet | Uniform Grid + circle |
| 1000s | Uniform Grid + tight loop |
| 100K+ | GPU compute shader |
| Static obstacle | BVH / quadtree |
| Polygon collision | SAT |
| Homing | Angular velocity limit |
| Game feel | Hitstop + shake + particle |

**기본값**: Object pool + Uniform Grid + Circle-Circle + Hitstop.

## 🔗 Graph
- 부모: [[Game-Design]] · [[Game-Engine]]
- 변형: [[Object-Pool]] · [[Collision-Detection]] · [[Bullet-Hell]]
- 응용: [[Compute Shader]] · [[Boss-Orchestration-and-Gimmick-Management]] · [[Combat Controls Update (Feb 2014)]]
- Adjacent: [[Baiting]] · [[BioShock (2007)]] · [[Combined Arms (제병협동) 전술]]

## 🤖 LLM 활용
**언제**: 매 game design. 매 STG / bullet hell. 매 arena combat. 매 performance optimization.
**언제 X**: 매 turn-based (different mechanic). 매 puzzle.

## ❌ 안티패턴
- **No object pool**: 매 GC stutter.
- **N² collision check**: 매 100+ object 의 fail.
- **Cell size 의 too small / large**: 매 inefficient.
- **No hitstop**: 매 hits 의 weightless.
- **Sub-frame collision miss**: 매 fast bullet 의 tunneling — 매 swept check.
- **No bullet visual cleanup**: 매 visual clutter.

## 🧪 검증 / 중복
- Verified (Real-Time Collision Detection (Ericson), bullet hell game design).
- 신뢰도 B.
- Related: [[Compute Shader]] · [[Boss-Orchestration-and-Gimmick-Management]] · [[Game-Design]] · [[CSS Animations]] (game feel).

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — Actor-Proxy + grid + 매 pool / homing / spiral / hitstop / GPU code |
