← ソースコード説明書

エンジンアーキテクチャ(コンパイラ・ランナー・レンダラ分離)

作成日: 2026-03-22 / 更新日: 2026-04-29

ℹ️ 本文書は 2026-03-22 時点の概要で、KS / KSC + IOpHandler モデル中心です。Phase 1(2026-04-21〜29)で導入された host-binding パターン、TS VM / C++ KscVM の VM 二重化、ECS / Camera / Tween / Collision の host 関数群については 22-ksc-pipeline-overview.md を参照してください。両者は補完関係にあります(本文書は IOpHandler 経由の VN dispatch を、22 は新しい HostAPI 経由の dispatch を中心に説明)。


全体構成

KS (@コマンド)                    KSC (TypeScript風)
    ↓                                ↓
@kaedevn/compiler              @kaedevn/ksc-compiler
    ↓                                ↓
CompiledScenario (Op[])         KSC AST
    ↓                                ↓
OpRunner                        KscRunner
    ↓                                ↓
    └──────── IOpHandler ────────────┘
                  ↓
    ┌─────────────┴─────────────┐
    ↓                           ↓
WebOpHandler              SDL2Engine (IEngineAPI)
  PixiJS / WebGL            SDL2 / OpenGL
  Web Audio API             SDL_mixer
  IndexedDB                 FileStorage
    ↓                           ↓
  ブラウザ                  macOS / iOS / Android / Switch

レイヤー解説

1. スクリプト言語(2系統が共存)

言語記法コンパイラ用途
KS@bg forest @ch hero smile center@kaedevn/compilerTyranoScript 互換、エディタのブロックから生成
KSCTypeScript 風 bg("forest") ch("hero", "smile")@kaedevn/ksc-compiler上級者向け、KSC エディタから直接記述

2. ランナー(実行エンジン)

ランナー入力動作
OpRunnerCompiledScenario (Op 配列)Op を1つずつ実行、PC(プログラムカウンタ)で管理
KscRunnerKSC スクリプト文字列KSC AST を直接解釈して実行

両方とも 同じ IOpHandler インターフェース を呼ぶ。

3. IOpHandler / IEngineAPI(抽象化層)

ランナーとレンダラを分離するインターフェース。主要メソッド:

メソッド内容
bgSet(id, fadeMs, effect)背景表示
chSet(name, pose, pos)キャラクター表示
text(who, text)テキスト表示
choice(options)選択肢表示
playBgm(id, vol)BGM 再生
playSe(id, vol)SE 再生
wait(type, ms)待機(クリック/タイムアウト/音声終了)

4. レンダラ実装(2系統)

Web 版: WebOpHandler

担当技術ファイル
レンダリングPixiJS / WebGLpackages/web/src/renderer/WebOpHandler.ts
音声Web Audio APIpackages/web/src/audio/AudioManager.ts
入力PointerEvent / KeyboardEventpackages/web/src/input/InputManager.ts
保存IndexedDBpackages/web/src/storage/StorageManager.ts

ネイティブ版: SDL2Engine

担当技術ファイル
レンダリングSDL2 / OpenGLpackages/native-engine/src/engine/SDL2Engine.cpp
音声SDL_mixerpackages/native-engine/src/engine/AudioManager.cpp
テクスチャSDL_imagepackages/native-engine/src/engine/TextureManager.cpp
フォントSDL_ttfpackages/native-engine/src/engine/FontManager.cpp
保存ファイルシステムpackages/native-engine/src/engine/FileStorage.cpp
アセット読込PAK アーカイブpackages/native-engine/src/engine/PakReader.cpp

プラットフォーム分岐:

#ifdef __APPLE__    // macOS / iOS
#ifdef __ANDROID__  // Android
// (Switch は Nintendo SDK で別途対応)

Core Abstractions(CLAUDE.md より)

Switch 移植時に全面書き直しを避けるための抽象化:

インターフェース用途Web 実装Native 実装
IInput入力アクション dispatchPointerEvent / KeyboardSDL_Event
IAudioBGM/SE/VOICE 再生Web Audio APISDL_mixer
IStorageセーブ/ロードIndexedDBファイル I/O

なぜ KS と KSC が同時に存在できるか

  1. コンパイラが別: KS → Op[], KSC → AST。それぞれ別のパッケージ
  2. ランナーが別: OpRunner と KscRunner。ksc-demo.ts で両方インスタンス化
  3. ハンドラが共通: 両ランナーとも IOpHandler の同じメソッドを呼ぶ
  4. レンダラは1つ: WebOpHandler を両方のランナーに渡す
// ksc-demo.ts
const handler = new WebOpHandler(layers, textWindow, choiceOverlay, input, audio, ticker, renderer);
const runner = new OpRunner();
const kscRunner = new KscRunner();

// KS モード
await runner.start(scenario, handler);

// KSC モード
await kscRunner.run(script, handler);

ファイル構成

packages/
├── core/           # 共有型・定数・Op定義・IOpHandler・OpRunner
├── compiler/       # KS (@コマンド) → Op[] コンパイラ
├── ksc-compiler/   # KSC (TypeScript風) → AST コンパイラ
├── web/            # Web版エンジン (WebOpHandler, PixiJS)
├── native-engine/  # ネイティブ版 (SDL2Engine, C++)
│   ├── src/
│   │   ├── engine/         # SDL2Engine, AudioManager, TextureManager
│   │   ├── interpreter/    # Interpreter (Op実行)
│   │   └── platform/       # iOS固有処理
│   ├── external/           # SDL2, SDL2_image, SDL2_mixer, SDL2_ttf
│   ├── ios/                # iOS ビルド設定
│   └── CMakeLists.txt
└── interpreter/    # OSS版インタープリタ (TypeScript)

macOS パッケージング(.app バンドル生成)

エディタで作成した作品を macOS の .app バンドルとしてエクスポートする機能が実装済み。

フロー

エディタ / プロジェクトページ
  ↓ POST /api/export/:projectId/mac
  ↓
API サーバー (apps/hono/src/routes/export.ts)
  1. プロジェクト・アセット・キャラクターを DB から取得
  2. KSC スクリプト生成 (generateKSCScript)
  3. アセットファイルをダウンロード → /tmp に展開
  4. assets.json 生成(slug → ファイルパスのマッピング)
  5. package-mac.sh 実行
  6. .app → ZIP 圧縮
  7. ZIP をレスポンスとして返却
  ↓
ブラウザでダウンロード → ダブルクリックで起動

package-mac.sh

packages/native-engine/tools/package-mac.sh <アセットフォルダ> [アプリ名]

処理内容:

  1. pak_tool でアセットを PAK アーカイブに圧縮
  2. .app バンドルのディレクトリ構造を生成:
    <AppName>.app/
    ├── Contents/
    │   ├── Info.plist
    │   ├── MacOS/
    │   │   └── kaedevn_native    ← SDL2 バイナリ
    │   └── Resources/
    │       ├── game.pak           ← PAK アーカイブ
    │       └── icon.icns
    
  3. dist/<AppName>.app に出力

必要なビルド済みツール

ツールパス用途
kaedevn_nativepackages/native-engine/build/kaedevn_nativeSDL2 ゲームエンジン本体
pak_toolpackages/native-engine/build/pak_toolアセット → PAK 圧縮

API エンドポイント

POST /api/export/:projectId/mac
→ Content-Type: application/zip
→ Content-Disposition: attachment; filename="<AppName>.zip"

対応プラットフォーム

プラットフォームビルドガイドパッケージング
macOS17-build-guide-macos.md✅ .app バンドル + ZIP
Android18-build-guide-android.md✅ APK (CMake + Gradle)
iOS19-build-guide-ios.md✅ Xcode プロジェクト
Nintendo Switch🔲 Nintendo SDK で別途対応
Ad: stickyBottom (728x90)
kaedevn - ノベルゲームを作れるプラットフォーム