エンジンの仕組み — どこでも動くスクリプトエンジン
作成日: 2026-03-06 / 更新日: 2026-06-10
kaedevn で作った作品が Web でも Android でも PC でも同じように動く秘密は、プラットフォームに依存しないスクリプトエンジンの設計にあります。約80,000行のソースコードが、この「どこでも動く」を支えています。
2つのスクリプト処理系
kaedevn には2つのスクリプト処理系があり、どちらも事前にコンパイルしてから実行します。
- packages/compiler — KS (.ks) — スクリプトを Op[](命令配列)にコンパイル。実行時は OpRunner が命令を順に処理
- packages/ksc-compiler(KscVM) — KSC (.ksc) — TypeScript 風の構文を型チェックし、IR(中間表現バイトコード)にコンパイルしてスタックベース VM で実行(旧
packages/interpreterの逐次解釈方式は 2026-04-29 deprecated)
どちらもスクリプトを事前に機械が読める形(KS は Op[]、KSC は IR)へ変換してから実行します。型チェックや検証を実行前に行えるのが利点です。
2つのスクリプト形式
kaedevn には用途の異なる2つのスクリプト形式があります。
KS(Kaede Script)— タグベース、約60コマンド
TyranoScript に近いシンプルな形式です。セリフや基本演出を素早く記述するのに向いています。引数はスペース区切りで、属性はクォートしません。
@bg room
@ch 赤音 smile C
#赤音
おはよう!今日もいい天気だね。@l
choice {
"一緒に出かける" { @jump go_out }
"家でのんびり" { @jump stay_home }
}
主な対応コマンド(全体は KS 仕様書 を参照):
- 基本 — @bg, @ch, @ch_hide, @ch_clear, @ch_move, @overlay, @overlay_hide, @bgm, @se, @voice, @wait
- キャラ表現(2D/Live2D/VRM 共通) — @ch_expr, @ch_lip, @ch_motion / @live2d* / @vrm*
- 演出・エフェクト — @camera, @shake, @flash, @fade_black, @filter, @filter_mix, @color_adjust, @particle, @effect, @slash
- 制御 — @jump, @call, @return, @call_template、選択肢
choice {}、条件if () {}、変数x = / += / -= - インライン(テキスト内) — @l(クリック待ち), @p(改ページ), @r(改行)
KSC(Kaede Script Code)— TypeScript 風、型安全
TypeScript に近い構文で、変数・条件分岐・関数定義・型チェックなどの高度なロジックが書ける形式です。
bg("room")
ch("sakura", "smile", "C")
#sakura
「おはよう!今日もいい天気だね」
#
if (affection >= 10) {
choice {
"一緒に出かける" { jump("go_out") }
"デートに誘う" { jump("date") }
}
} else {
choice {
"一緒に出かける" { jump("go_out") }
"家でのんびり" { jump("stay_home") }
}
}
KSC の型システム:
- number —
affection = 5 - string —
playerName = "太郎" - boolean —
isClear = false - object —
{hp: 100, mp: 50} - array —
[1, 2, 3] - union —
number | string - 関数 —
def get_rank(score) { ... }
コンパイルから実行までの流れ
KS の場合(4段階パイプライン)
.ks スクリプト
|
v
Tokenizer ── 字句解析(行を分類)
| lineClassifier で COMMAND / TEXT / LABEL を判定
v
Parser ───── 構文解析(コマンドレジストリ参照)
| commandRegistry.ts が全コマンドの定義源
v
Transformer ─ 変換・最適化
|
v
Finalizer ── 検証 + Op[] 出力
| プラットフォーム非依存の命令列
v
OpRunner ─── 1命令ずつ実行、描画エンジンに委譲
KSC の場合(コンパイラ + スタック VM)
.ksc スクリプト
|
v
Lexer ────── 字句解析(Token[])
|
v
Parser ───── 構文解析(AST)
|
v
Checker ──── 静的型チェック(型不一致をコンパイル時に検出)
|
v
Emitter ──── IR(中間表現バイトコード)を生成
|
v
KscVM ────── スタックベース VM で IR を実行
| Web: TypeScript VM / ネイティブ: C++ 移植(ksc-vm-cpp、同一 IR を実行)
v
HostAPI ────── 描画エンジンに命令を委譲
KSC は実行前に Checker が静的型チェックを行い、型の不一致をコンパイル時に検出します(affection = "高い" のような誤りを実行前に弾く)。コンパイル済みの IR は Web の TypeScript VM でもネイティブの C++ VM でも同一に実行されます。
重要なのは、コンパイラの出力する Op[] がプラットフォームを問わず共通 である点です。同じ命令列を Web ブラウザでも Nintendo Switch でも実行できます。
Op 型(中間表現)— エンジンの共通言語
コンパイラが出力する命令の種類は以下の通りです。
- テキスト — TEXT_APPEND(話者名 + 本文)
- 映像 — BG_SET, CH_SET, CH_HIDE, CH_CLEAR, CH_MOVE, OVERLAY_SET, OVERLAY_HIDE, SCREEN_FILTER, FILTER_MIX, CAMERA_SET
- 音声 — BGM_PLAY, BGM_STOP, SE_PLAY, VOICE_PLAY
- 制御 — CHOICE, JUMP, JUMP_IF, CALL, RETURN, VAR_SET / VAR_ADD / VAR_SUB
- 待機 — WAIT_CLICK / WAIT_MS / WAIT_VOICE_END
スクリプトエンジンの役割
スクリプトエンジン(KS の OpRunner / KSC の KscVM)はスクリプトの「頭脳」です。以下を担当します。
- 実行制御: コンパイル済み命令の順次実行(KS の Op[])/ コンパイル済み IR の VM 実行(KSC)
- 変数の保持: フラグや好感度などのゲーム状態を管理(グローバル/ローカルスコープ対応)
- フロー制御: if 分岐、ジャンプ、サブルーチン呼び出し(call / ret)、for / while ループ
- 選択肢の処理: プレイヤーの選択を受け取り、対応するラベルへ移動
スクリプトエンジン自体は画面描画や音声再生を一切行いません。「背景を room に変えて」「BGM を再生して」といった命令を発行するだけです。実際の描画は、各プラットフォームの実装(Web なら PixiJS、Switch なら SDL2)が担当します。
IOpHandler / HostAPI — プラットフォームを繋ぐインターフェース
スクリプトエンジンと描画エンジンの間には抽象インターフェースが定義されています。KS の Op[] は IOpHandler(packages/core)を、KSC の VM は HostAPI(packages/ksc-compiler)を通じて、描画・音声・入力を委譲します。
IOpHandler の主なメソッド:
IOpHandler
├── textAppend(who, text) テキスト表示
├── choice(options) 選択肢表示 → 選択結果を返す
├── bgSet(id, ...) 背景変更
├── chSet(name, pose, pos, ...) キャラクター表示
├── chHide(name, ...) キャラクター非表示
├── bgmPlay(id, ...) BGM 再生
├── sePlay(id, ...) 効果音再生
└── waitMs / waitClick / waitVoiceEnd 待機
このインターフェースを実装するだけで、新しいプラットフォームに対応できます。実際に以下の実装が存在します。
- WebOpHandler — Web ブラウザ(PixiJS)— 本番用
- SDL2Engine + GLRenderer — macOS / Windows / iOS / Android — ネイティブ(OpenGL)
型チェック(コンパイル時)
Kaede Script のコード記法(KSC)は、実行前に Checker が静的型チェックを行います。
- 型不一致の検出: number 変数に string を代入、など
- 未定義の変数・関数・ラベルの参照を検出
- 関数の引数の数・型の検証
- 戻り値の型の検証
これらは IR 生成の前段(コンパイル時)に検出されるため、実行前にミスに気づけます。
// コンパイル時に型エラーを検出
affection = "高い" // Error: number型の変数にstringを代入できません
jump(123) // Error: jump()の引数はstringである必要があります
なぜこの設計なのか
従来のノベル系エンジンは、特定のプラットフォーム(Windows や Web)に密結合していました。kaedevn は最初から Nintendo Switch への移植を見据えて、エンジンのコア部分とプラットフォーム固有の処理を明確に分離しています。
この設計により:
- 作者は1回書くだけ: 同じスクリプトが全プラットフォームで動く
- 移植コストが最小: 新しいプラットフォームに対応するには IOpHandler(KS)と HostAPI(KSC)を実装するだけ
- テストが容易: スクリプトエンジン単体でスクリプトの正しさを検証できる(画面なしで多数のユニットテストが通る)
- 2つの入口: 初心者は KS のタグベース、上級者は KSC の TypeScript 風構文を選べる
関連ドキュメント
- KS スクリプト仕様書 — KS コンパイラの全コマンド
- KSC スクリプト仕様書 — KSC の全コマンド
- マルチプラットフォーム — SDL2 ネイティブエンジン
- アセットのポータビリティ — 共通アセット設計