← About

SDL2 と VM の境界設計 — 「VM は一切いじらない」

AI との対話から生まれた設計文書(原文)

ChatGPT との対話から生まれた設計文書(2026年2月25日) 自作 VM(Kaede Script Code)と SDL2 の接続設計。Switch 移植のアーキテクチャを決めた一枚


了解。VM が完成していて、さらにクラス実装の"受け"まで整っているなら、SDL2 側は VM に寄せ切る のが最短です。やることは「VM が要求する副作用(I/O)を SDL2 で実装する」だけになります。

ゴール

  • VM は一切いじらない(原則)
  • SDL2 は Host(実行環境) として、VM の命令を受けて描画・音・入力・ファイルを処理する
  • 追加機能は「命令セット(API)」追加で回す

1) まず固定する"境界": Host API(これが全て)

VM ↔ SDL2 の境界は次の 2つだけ に絞るのが安定です。

A. VM → Host(副作用要求)

  • spawn / destroy
  • transform(move / scale / rotate)
  • render(sprite / text / rect)
  • audio(bgm / se)
  • storage(load / save)
  • time(wait / sleep)
  • debug / log

B. Host → VM(入力イベント)

  • key down / up
  • mouse move / down / up / wheel
  • gamepad
  • window resize / quit

ここを インターフェース(C++ なら純仮想、TS なら型)で固定 します。 VM はこのインターフェースだけ知っていて、SDL2 実装はその具象。


2) 実装の最短手順(VM が完成してる前提)

Step 1: "命令の副作用"を全部イベント化する

VM が今「命令を実行して直接何かする」形でも、外へ出すのはこうします。

  • VM 実行 → HostRequest を吐く(またはコールバックを呼ぶ)
  • Host が処理して結果を VM へ返す(同期/非同期どっちでも)

重要: SDL2 のメインループと VM の逐次実行を衝突させないこと。 解決は「VM は1フレームに実行していい命令数/時間を制限」か「yield 命令で止める」。

Step 2: 命令を「即時」と「待ち」に分ける

  • 即時: spawnSprite, setTexture, setText, playSE
  • 待ち: wait(t), moveTo(..., duration), fade(..., duration)

待ち系は VM 側が既に yield できるなら、

  1. 命令実行で Task / Coroutine を作る
  2. Host は進捗だけ返す

が一番綺麗です。


3) クラス実装準備ができてるなら: Host API も"オブジェクト指向"で揃える

クラス導入で効くのは「シーン/エンティティ/コンポーネント」を VM 側で表現できる点です。

ただし SDL2 側は凝らない。SDL2 は以下だけ持てばいい:

  • NodeId(整数)
  • Transform
  • Drawable(Sprite / Text / Rect)
  • Z(描画順)
  • Visible

これを SceneGraph っぽく持って、VM は NodeId を参照するだけ。


4) "移植しやすい命令セット"の形(推奨フォーマット)

将来、SDL2 以外(WebGL、Metal、Switch 向け独自)に替えるために、命令はこういう形が強いです。

  • 命令は データ(struct / JSON / バイナリ)で表現
  • Host は 命令を解釈して実行
  • VM は 命令を生成して流す

例(概念):

CMD_SPAWN_SPRITE { nodeId, assetId, x, y }
CMD_TWEEN { nodeId, prop, from, to, duration, easing }

命令がデータなら、ログ・リプレイ・テストが強くなります(ここが AI 実装とも相性良い)。


5) ここから先、Claude Code に投げる"具体タスク"

VM が完成している前提で、最初のタスクはこれが最短です。

  1. SDL2 プロジェクト雛形(CMake)
  2. Host インターフェース作成(上の A/B を型として固定)
  3. SdlHost 実装(画像1枚 + テキスト表示 + 入力イベント送出)
  4. VM をリンクし、固定スクリプト1本を毎回実行できるようにする
    • spawnSprite → 表示
    • moveTo(…, 1.0) → 1秒移動
    • wait(0.5) → 停止
    • fade(…, 0.5) → フェード

ここまで行けば、あとは命令を増やしていくだけ になります。


6) VM の実行モデルによる分岐

VM が「yield / コルーチン」をどう表現しているかで、Host 側の作りが決まります。

  • パターン A: VM が wait 等で自分で停止し、dt で再開できる → Host は単純(毎フレーム vm.update(dt)

  • パターン B: VM は停止できない(全部同期実行) → Host 側で命令キュー+タイムラインを持つ必要あり(少し重くなる)

必要なら、既存 VM の「実行モデル(update / yield / 命令キュー)」を前提にして、Host API(インターフェース定義)と命令セット初版(20〜30個)をそのまま Claude Code に投げられる形で出します。

Ad: inContent (336x280)
Ad: stickyBottom (728x90)
kaedevn - ノベルゲームを作れるプラットフォーム