JA

チュートリアル1 — Hello world:メニュー、テキスト、サウンド

メニュー(プレイ、サウンド、終了)のあるタイトル画面と、矢印キーで動くテキスト、スペースキーで鳴るサウンドだけの、いちばん小さなJumPHPゲームです。ファイルは2つ。サンドボックスですぐに書き換えられますし、手元で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

シーンには、大事なメソッドが3つあります。

  • enter($params):シーンの開始時に呼ばれます。ここですべてを初期状態に戻してください。同じシーンが何度も使われるからです。
  • update($dt):1秒に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。

次は:動いてアニメーションするキャラクター。