← ソースコード説明書
エンジンアーキテクチャ(コンパイラ・ランナー・レンダラ分離)
作成日: 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/compiler | TyranoScript 互換、エディタのブロックから生成 |
| KSC | TypeScript 風 bg("forest") ch("hero", "smile") | @kaedevn/ksc-compiler | 上級者向け、KSC エディタから直接記述 |
2. ランナー(実行エンジン)
| ランナー | 入力 | 動作 |
|---|---|---|
| OpRunner | CompiledScenario (Op 配列) | Op を1つずつ実行、PC(プログラムカウンタ)で管理 |
| KscRunner | KSC スクリプト文字列 | 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 / WebGL | packages/web/src/renderer/WebOpHandler.ts |
| 音声 | Web Audio API | packages/web/src/audio/AudioManager.ts |
| 入力 | PointerEvent / KeyboardEvent | packages/web/src/input/InputManager.ts |
| 保存 | IndexedDB | packages/web/src/storage/StorageManager.ts |
ネイティブ版: SDL2Engine
| 担当 | 技術 | ファイル |
|---|---|---|
| レンダリング | SDL2 / OpenGL | packages/native-engine/src/engine/SDL2Engine.cpp |
| 音声 | SDL_mixer | packages/native-engine/src/engine/AudioManager.cpp |
| テクスチャ | SDL_image | packages/native-engine/src/engine/TextureManager.cpp |
| フォント | SDL_ttf | packages/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 | 入力アクション dispatch | PointerEvent / Keyboard | SDL_Event |
IAudio | BGM/SE/VOICE 再生 | Web Audio API | SDL_mixer |
IStorage | セーブ/ロード | IndexedDB | ファイル I/O |
なぜ KS と KSC が同時に存在できるか
- コンパイラが別: KS → Op[], KSC → AST。それぞれ別のパッケージ
- ランナーが別: OpRunner と KscRunner。
ksc-demo.tsで両方インスタンス化 - ハンドラが共通: 両ランナーとも
IOpHandlerの同じメソッドを呼ぶ - レンダラは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 <アセットフォルダ> [アプリ名]
処理内容:
pak_toolでアセットを PAK アーカイブに圧縮.appバンドルのディレクトリ構造を生成:<AppName>.app/ ├── Contents/ │ ├── Info.plist │ ├── MacOS/ │ │ └── kaedevn_native ← SDL2 バイナリ │ └── Resources/ │ ├── game.pak ← PAK アーカイブ │ └── icon.icnsdist/<AppName>.appに出力
必要なビルド済みツール
| ツール | パス | 用途 |
|---|---|---|
kaedevn_native | packages/native-engine/build/kaedevn_native | SDL2 ゲームエンジン本体 |
pak_tool | packages/native-engine/build/pak_tool | アセット → PAK 圧縮 |
API エンドポイント
POST /api/export/:projectId/mac
→ Content-Type: application/zip
→ Content-Disposition: attachment; filename="<AppName>.zip"
対応プラットフォーム
| プラットフォーム | ビルドガイド | パッケージング |
|---|---|---|
| macOS | 17-build-guide-macos.md | ✅ .app バンドル + ZIP |
| Android | 18-build-guide-android.md | ✅ APK (CMake + Gradle) |
| iOS | 19-build-guide-ios.md | ✅ Xcode プロジェクト |
| Nintendo Switch | — | 🔲 Nintendo SDK で別途対応 |
Ad: stickyBottom (728x90)