# AI 꾸러미 — 시차 별층 (패럴랙스 스크롤 · 성간 먼지) — Parallax scrolling
> 성운 · 먼 별 · 별 · 먼지 구름 · 밝은 별 · 가까운 바위 6층을 미리 그려 두고 멀수록 느리게 옮기기만 해서, 2D 화면에 깊이를 준다.  
> 견본: https://ai-techstudio.web.app/#t/i535

이 문서를 코드 도우미(Claude Code · Cursor · ChatGPT 등)에 그대로 주면 돼요. 「## 주문서」 가 할 일, 나머지는 참고 자료예요.

## 주문서

### 만들어 줘: 시차 별층 (패럴랙스 스크롤 · 성간 먼지) — Parallax scrolling

#### 1. 목표
2D 슈팅 게임 배경에 시차 스크롤 우주 배경을 넣어 줘 — 6층을 미리 그려 멀수록 느리게, 느낌은 깊은 우주.

#### 2. 핵심 기술 용어
- **Parallax scrolling** — 층마다 다른 빠르기로 옮겨 생기는 깊이
- **Pre-rendered layers (offscreen canvas)** — 층 그림을 한 번만 그려 두기
- **Seamless horizontal tiling** — 가로로 이어 붙여 끝없이 흐르기

#### 3. 환경
- 플랫폼: HTML Canvas 2D (TypeScript), requestAnimationFrame 루프, 라이브러리 없이
- 화면: 2D · 브라우저 — PC 와 폰(390px 폭) 모두, devicePixelRatio 맞춰 또렷하게, 60fps 목표

#### 4. 조건
- 층 그림은 시작할 때 한 번만 (오프스크린 캔버스) — 프레임마다 별을 다시 그리지 않는다
- 빠르기 배율은 먼 층일수록 작게 (0.03 ~ 1.5), 시차를 끄면 모두 같은 0.5 (비교용)
- 층은 가로로 이음매 없이 — 왼쪽 끝과 오른쪽 끝이 이어지게 그린다
- 스크롤은 dt × 빠르기로 쌓는다 (프레임 수 아님)
- 화면 높이에 맞춰 배율 s = 높이 / 900 로 크기를 맞춘다

#### 5. 완성 기준 (이게 보이면 성공)
- 성운은 거의 안 움직이고 가까운 바위 · 먼지는 휙휙 지나가 깊이가 느껴진다
- 「시차」를 끄면 모든 층이 같은 빠르기로 흘러 납작해 보인다 (켬과 비교)
- 층이 이어지는 자리에 이음매가 보이지 않는다
- 「층 이름 · 빠르기 보기」로 층마다 배율이 표시되고, 폰에서도 60fps

#### 6. 진행 방식
- 핵심 코드 위주로, 설명은 짧게. 내 프로젝트에 끼워 넣기 쉬운 함수 · 클래스로 나눠 줘.
- 처음 화면에 바로 결과가 보이게, 그리고 켬/끔(또는 전/후) 비교를 할 수 있게 만들어 줘.
- 그림 · 소리 · 모델 파일이 필요하면 코드로 만든 임시 대체물로 먼저 돌아가게 하고, 진짜 파일로 바꿀 자리를 표시해 줘.
- 마지막에 「확인 방법」(무엇을 보면 성공인지)과 「조절할 값」 목록을 짧게 정리해 줘.
- 답변과 코드 주석은 한국어로 해 줘.

## 원리
- 멀리 있는 것은 느리게, 가까운 것은 빠르게 지나간다 — 이 빠르기 차이만으로 눈은 깊이를 느낀다.
- 층 6장(2048 × 1000)을 시작할 때 한 번만 캔버스에 그려 둔다. 매 프레임은 drawImage 로 옮겨 찍기만 한다.
- 층마다 빠르기 배율: 0.03 · 0.08 · 0.17 · 0.33 · 0.62 · 1.5 (가장 먼 성운 → 가장 가까운 바위).
- 가로 위치 = -(스크롤 × 배율) mod 층 폭, 화면이 찰 때까지 이어 붙여 찍는다. 위아래 흔들림도 배율만큼 다르게.
- 우주선은 5번째 층(밝은 별) 다음에 그려 바위 층이 그 앞을 지나가게 한다.

## 핵심 코드 — 미리 그린 6층을 빠르기 배율대로 이어 찍기
(발췌: demos/demosSpace.ts i535 make() draw() 를 정리 (층 그림은 parallaxLayers()))
```ts
const LW = 2048, LH = 1000;                       // 층 그림 크기
const layers: HTMLCanvasElement[] = parallaxLayers(); // 시작할 때 한 번만 그린 6장
const SPEED = [0.03, 0.08, 0.17, 0.33, 0.62, 1.5];  // 성운 → 먼 별 → 별 → 먼지 → 밝은 별 → 바위
let scroll = 0;

function draw(g: CanvasRenderingContext2D, w: number, h: number, t: number, dt: number, parallax = true): void {
  scroll += dt * 260;
  const s = h / 900;                                  // 화면 높이에 맞춘 배율
  const bob = Math.sin(t * 0.5) * 30;                 // 위아래로 천천히 흔들림
  g.fillStyle = '#000';
  g.fillRect(0, 0, w, h);
  for (let i = 0; i < layers.length; i++) {
    const k = parallax ? SPEED[i]! : 0.5;             // 끄면 모두 같은 빠르기 — 납작해진다
    const lw = LW * s, lh = LH * s;
    let x = -((scroll * k * s) % lw);
    const y = (h - lh) / 2 - bob * k * 0.4 * s;       // 흔들림도 가까울수록 크게
    while (x < w) {                                   // 화면이 찰 때까지 이어 붙이기
      g.drawImage(layers[i]!, x, y, lw, lh);
      x += lw;
    }
    if (i === 4) drawShip(g, w * 0.36, h * 0.5 - bob * 0.25 * s, s * 1.3, t); // 바위 층이 우주선 앞을 지나게
  }
}
```

## 흔한 실수 · 확인 목록
- [ ] **매 프레임 별 수천 개를 다시 그리면 폰에서 느리다** — 층마다 한 번 그린 캔버스를 drawImage 로 옮겨 찍기만 한다.
- [ ] **층 끝이 이어지지 않게 그리면 이음매가 지나간다** — 별 · 구름을 그릴 때 가로로 -폭 · 0 · +폭 세 번 찍어 양끝을 잇는다.
- [ ] **느린 폰에서 프레임 시간을 잘라 쓰면 배경이 느려진다** — 스크롤은 실제 dt 로 — 새 게임은 처음부터 이 방식으로.

## 완성 기준 체크리스트
- [ ] 성운은 거의 안 움직이고 가까운 바위 · 먼지는 휙휙 지나가 깊이가 느껴진다
- [ ] 「시차」를 끄면 모든 층이 같은 빠르기로 흘러 납작해 보인다 (켬과 비교)
- [ ] 층이 이어지는 자리에 이음매가 보이지 않는다
- [ ] 「층 이름 · 빠르기 보기」로 층마다 배율이 표시되고, 폰에서도 60fps

## 이 기술 정보
- id: `i535` · 분류: 그래픽 · 셰이더 › 우주 표현 · 2D · 난이도 쉬움 · 폰 부담 가벼움 (폰 OK) — 매 프레임 drawImage 약 12번(층 6 × 이어 붙이기 2)뿐. 층 그림 6장(2048 × 1000)이 메모리를 조금 쓴다.
- 라이브 견본 (브라우저에서 직접 조작): https://ai-techstudio.web.app/#t/i535
- 쓰면 좋을 때: 2D 횡스크롤 게임 배경 / 가벼운 깊이감이 필요한 메뉴 · 로딩 / 폰에서 3D 없이 우주 느낌
- 쓰지 말 때: 카메라가 자유롭게 도는 3D 장면 — 대신 별 하늘(i522) / 층마다 매 프레임 그림을 다시 그리기 — 미리 그려 두는 것이 핵심

## 견본 실제 코드 (라이브 견본이 돌리는 코드 — three.js · TypeScript)
### i535 견본 항목 — `src/demos/demosSpace.ts:2905`
```ts
  i535: {
    kind: '2d',
    caption: '시차 스크롤 — 멀리 있는 층(성운 · 먼 별)은 느리게, 가까운 층(밝은 별 · 먼지 · 바위)은 빠르게 지나가 깊이가 생긴다. 층 6장을 미리 그려 두고 옮기기만',
    make() {
      const layers = parallaxLayers();
      const SPEED = [0.03, 0.08, 0.17, 0.33, 0.62, 1.5];
      const NAMES = ['성운 (가장 멀다)', '먼 별', '별', '먼지 구름', '밝은 별', '바위 · 먼지 (가장 가깝다)'];
      let speed = 1;
      let parallax = true;
      let labels = false;
      let ship = true;
      let scroll = 0;
      return {
        draw(g, w, h, t, dt) {
          scroll += dt * 260 * speed;
          const s = h / 900;
          const bob = Math.sin(t * 0.5) * 30;
          g.fillStyle = '#000';
          g.fillRect(0, 0, w, h);
          for (let i = 0; i < layers.length; i++) {
            const L = layers[i]!;
            const k = parallax ? SPEED[i]! : 0.5;
            const lw = LW * s;
            const lh = LH * s;
            let x = -((scroll * k * s) % lw);
            const y = (h - lh) / 2 - bob * k * 0.4 * s;
            while (x < w) {
              g.drawImage(L, x, y, lw, lh);
              x += lw;
            }
            if (i === 4 && ship) drawShip(g, w * 0.36, h * 0.5 + Math.sin(t * 1.3) * 12 * s - bob * 0.25 * s, s * 1.3, t);
            if (labels) {
              g.font = `600 ${Math.max(11, 15 * s)}px ${FONT}`;
              g.fillStyle = 'rgba(0,0,0,0.55)';
              const txt = `${i + 1}. ${NAMES[i]} — 빠르기 ×${k}`;
              const tw = g.measureText(txt).width;
              const ly = 14 * s + i * 24 * Math.max(0.75, s);
              g.fillRect(10, ly, tw + 16, 20 * Math.max(0.75, s));
              g.fillStyle = '#e8eeff';
              g.fillText(txt, 18, ly + 15 * Math.max(0.75, s));
            }
          }
        },
        controls: [
          range('날아가는 빠르기', 0, 4, 0.05, 1, (v) => (speed = v)),
          tog('시차 (끄면 모든 층이 같은 빠르기 — 납작해 보인다)', true, (v) => (parallax = v)),
          tog('층 이름 · 빠르기 보기', false, (v) => (labels = v)),
          tog('우주선', true, (v) => (ship = v)),
        ],
      };
    },
  }
```

## 관련 기술
- 먼저 알면 좋은 기술: [2D 시차 층 · 무한 확대](https://ai-techstudio.web.app/ai/t/i104.md) `i104` · [잡음으로 그린 성운 (fbm)](https://ai-techstudio.web.app/ai/t/u54.md) `u54`
- 다음에 해 볼 기술: [별 하늘 (실제 등급 · B-V 색 · 반짝임)](https://ai-techstudio.web.app/ai/t/i522.md) `i522` · [워프 · 초공간 이동](https://ai-techstudio.web.app/ai/t/i530.md) `i530`
- 참고 문서: [Wikipedia — Parallax scrolling](https://en.wikipedia.org/wiki/Parallax_scrolling) · [MDN — CanvasRenderingContext2D.drawImage()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)
