KO

튜토리얼 1 — Hello world: 메뉴, 텍스트, 사운드

가장 작은 JumPHP 게임이에요. 메뉴(플레이, 사운드, 종료)가 있는 타이틀 화면, 방향키로 움직이는 텍스트, 스페이스를 누르면 나는 소리가 전부예요. 파일은 두 개뿐이에요. 샌드박스에서 바로 수정해 볼 수도 있고, 내 컴퓨터에서 bin/jumphp new hello-world로 만들 수도 있어요.

설정: config/game.yaml

game:
  id: hello-world
  title: "HELLO WORLD"
  width: 320                    # 게임 해상도, 화면 크기에 맞게 확대돼요
  height: 180
  start_scene: App\Scene\TitleScene
  touch: { a: Space, b: Escape, start: Return }    # 휴대폰의 패드 버튼
  smoke:                        # `bin/jumphp test`가 화면 없이 혼자 플레이하는 장면
    scene: App\Scene\HelloScene
    params: { demo: true }

키 설정: 엔진은 up down left right confirm back pause를 이미 정의해 두었어요(방향키, Enter, Esc, 게임패드, 터치스크린). 액션을 추가하려면 config/input.yaml에 적어요.

actions:
  jump: [Space, pad0:a, touch:tap]

타이틀 화면: src/Scene/TitleScene.php

메뉴는 MenuScene을 상속해서 만들어요. 제목과 항목만 주면 나머지(그리기, 키보드·게임패드·터치 이동, 메뉴 사운드)는 엔진이 알아서 처리해요.

final class TitleScene extends MenuScene
{
    protected string $title = 'HELLO WORLD';

    public function enter(array $params = []): void
    {
        parent::enter($params);
        // PHP로 만든 사운드: 오디오 파일이 필요 없어요
        if (!$this->sound()->hasSfx('menu_move')) {
            $this->sound()->synth('menu_move', Synth::tone(660, 0.04));
            $this->sound()->synth('menu_confirm', Synth::sequence([[660, 0.05], [990, 0.08]]));
        }
    }

    /** 항목: [라벨, 오른쪽에 표시할 값, Enter를 눌렀을 때의 동작, 좌우 키를 눌렀을 때의 동작] */
    protected function items(): array
    {
        $items = [
            ['PLAY', null, fn () => $this->scenes()->switchTo(HelloScene::class)],
            ['SOUND', fn () => $this->sound()->isMuted() ? 'OFF' : 'ON', fn () => $this->toggleSound(), fn () => $this->toggleSound()],
        ];
        if (Platform::isDesktop()) {
            $items[] = ['QUIT', null, fn () => $this->engine->quit()];   // 브라우저에는 "종료"가 없어요
        }
        return $items;
    }

    private function toggleSound(): void
    {
        $this->sound()->setMuted(!$this->sound()->isMuted());
        $this->refresh();    // 메뉴가 새 값을 다시 표시해요
    }
}

menu_move와 menu_confirm 사운드는 메뉴가 자동으로 재생해요. 라벨은 config/lang.yaml(fr: { PLAY: JOUER })로 번역돼요.

게임: src/Scene/HelloScene.php

장면에는 핵심 메서드가 세 개 있어요.

  • enter($params): 장면이 시작돼요. 여기서 모든 것을 초기화하세요. 같은 장면이 여러 번 재사용돼요.
  • update($dt): 초당 60번 게임 상태를 바꿔요. $dt는 경과 시간(초)이에요.
  • render($r): 그리기만 하고 다른 일은 하지 않아요.
final class HelloScene extends AbstractScene
{
    private float $x = 0.0;
    private float $y = 0.0;

    public function enter(array $params = []): void
    {
        $this->x = $this->width() / 2;
        $this->y = $this->height() / 2;
        if (!$this->sound()->hasSfx('beep')) {
            $this->sound()->synth('beep', Synth::sweep(440, 880, 0.12));   // 올라가는 "삐" 소리
        }
    }

    public function update(float $dt): void
    {
        if ($this->actions()->isPressed('back')) {          // Esc: 타이틀로 돌아가요
            $this->scenes()->switchTo(TitleScene::class);
            return;
        }
        // axis()는 -1, 0, 1 중 하나: 초당 90픽셀
        $this->x += $this->actions()->axis('left', 'right') * 90 * $dt;
        $this->y += $this->actions()->axis('up', 'down') * 90 * $dt;

        if ($this->actions()->isPressed('jump') || $this->actions()->isPressed('confirm')) {
            $this->sound()->playSfx('beep');
        }
    }

    public function render(Renderer $r): void
    {
        $r->clear([16, 16, 40]);
        // 내장 폰트(5 × 7 픽셀), 2배 확대, 가운데 정렬, 검은 그림자
        $this->font()->draw($r, 'HELLO WORLD!', $this->x, $this->y, 2, [255, 220, 80], 'center', [0, 0, 0]);
    }
}

실제 파일에는 소리가 날 때마다 튀어 오르는 별, 색이 바뀌는 텍스트, 테스트용 데모 모드가 더 들어 있어요. 게임 소스에서 읽어 보세요.

직접 해 보기

  • 샌드박스에서 텍스트, 색 [255, 220, 80], 속도, 사운드(Synth::tone(220, 0.3, 'triangle'))를 바꾼 뒤 ▶ 테스트를 눌러 보세요.
  • 내 컴퓨터에서: bin/jumphp run hello-world, bin/jumphp test hello-world.

다음: 움직이고 애니메이션하는 캐릭터.