← ドキュメント一覧

KSC(コード記法)仕様書

Kaede Scriptコード記法(KSC、.ksc の詳細仕様書です。TypeScript に近い構文で、複雑なゲームロジックを手続き的に記述できます。

Kaede Script 全体像

kaedevn のスクリプト言語は 1 つ(Kaede Script)で、2 つの記法を提供します:

  • タグ記法(KS、.ksKS 仕様書。宣言的でシーン描写向き
  • コード記法(KSC、.ksc — 本仕様書。手続き的で複雑なロジック向き

概要は Kaede Script 概要 を参照してください。

処理方式: インタプリタ

KSC は インタプリタ方式 で動作します。スクリプトを1行ずつ逐次解釈して直接実行します。TypeScript に近い構文でゲームのロジックを記述でき、フラグによる複雑な分岐、数値計算、関数の再利用などが可能です。

.ksc スクリプト
    |
    v
インタプリタ ── スクリプトを逐次解釈して直接実行
    |              TypeScript 風の構文を1行ずつ処理
    v
HostAPI ────── 描画エンジンに命令を委譲
                Web なら PixiJS、Switch なら SDL2

インタプリタ自体は画面描画や音声再生を一切行わず、HostAPI 経由で各プラットフォームの実装に委譲します。デバッグモード(ブレークポイント、変数ウォッチ、ステップ実行)やエラー候補提示(Levenshtein 距離による類似コマンドの提案)も搭載しています。

KS との比較

  • 構文 — KS: TyranoScript 風(@command) / KSC: TypeScript 風
  • 処理方式 — KS: コンパイラ → Op[] / KSC: インタプリタ(逐次実行)
  • 型システム — KS: なし / KSC: あり(型チェック)
  • 制御フロー — KS: @if / @choice / KSC: if/else, for, while
  • 関数 — KS: なし / KSC: ユーザー定義関数(def / sub)
  • 用途 — KS: シンプルなシナリオ / KSC: 高度なロジック

型システム

KSC は以下の型をサポートします。型の不一致はエラーとして検出されます。

  • number — 数値(例: affection = 5
  • string — 文字列(例: playerName = "太郎"
  • boolean — 真偽値(例: isClear = false
  • null — null 値(例: result = null
  • object — オブジェクト(例: {hp: 100, mp: 50}
  • array — 配列(例: [1, 2, 3]
  • union — ユニオン型(例: number | string
  • 関数 — 関数型(例: def get_rank(score) { ... }
// 実行時にエラーを検出
affection = "高い"  // Error: number型の変数にstringを代入できません
jump(123)           // Error: jump()の引数はstringである必要があります

1. ラベルの定義

KSC におけるラベルは、ジャンプ先やサブルーチンの開始地点として機能します。行頭に * を付けて記述します。

// ラベルの定義
*start_scene

bg("room_day")
#hero
「おはよう!」
#

jump("next_part") // ラベルへジャンプ

*next_part
#sakura
「いい天気だね」
#

2. ダイアローグ(セリフ)ブロック

KSC では、セリフを #名前# で囲んで記述します。このブロックが終了すると、自動的にクリック待ちが発生します。

#hero
「今日はいい天気だね」
「どこかへ出かけようか」
#

#sakura
「賛成!図書館に行きたいな」
#

変数展開

セリフ内で {変数名} を使うと、変数の値が自動的に埋め込まれます。

#sakura
「{playerName}君、今の好感度は {affection} だよ!」
#

3. 変数と演算

数値、文字列、真偽値を自由に扱えます。

// 代入と演算
affection = 5
affection += 2  // 7になる
playerName = "太郎"
isClear = false

4. フロー制御

条件分岐 (if)

条件式により、物語の展開を変化させます。ジャンプ先には対応するラベルが必要です。

if (affection >= 10) {
    jump("true_end")
} else if (affection >= 5) {
    #sakura
    「もっと仲良くなりたいな」
    #
} else {
    jump("bad_end")
}

*true_end
#sakura
「ずっと一緒にいようね!」
#
jump("story_end") // ← これがないと *bad_end も表示されてしまいます

*bad_end
#sakura
「さようなら...」
#

*story_end
#system
おわり
#

選択肢 (choice)

プレイヤーに選択を促し、結果を即座に反映します。

注意: ラベルは進行を止めません

ラベル自体にプログラムの実行を止める機能はありません。特定のラベル以降の処理だけを行いたい場合は、ブロックの最後に別のラベルへの jump() を記述して、意図しない実行を防いでください。

choice {
    "一緒に帰る" {
        jump("go_home")
    }
    "デートに誘う" if (affection >= 10) {
        jump("date_event")
    }
}

*go_home
#system
二人で夕暮れの道を歩いた。
#
jump("day_end") // ← ここで飛ばさないと、下の *date_event も実行されてしまう

*date_event
#sakura
「えっ、デート!? 喜んで!」
#
jump("day_end")

*day_end
#system
こうして一日が終わった。
#

ループ (for / while)

KSC では KS にはないループ構文が使えます。

// for ループ
for (i = 0; i < 5; i += 1) {
    #system
    「{i}回目のループ」
    #
}

// while ループ
count = 10
while (count > 0) {
    count -= 1
}

5. 関数とサブルーチン

処理を共通化して再利用できます。

サブルーチン (sub)

戻り値のない処理のまとまりです。

sub show_status() {
    #system
    現在の好感度: {affection}
    #
}

// 呼び出し
show_status()

関数 (def)

計算結果などを返すことができます。

def get_rank(score) {
    if (score > 80) return "S"
    return "A"
}

rank = get_rank(score)

call / ret

ラベルをサブルーチンとして呼び出すこともできます。

call("common_effect")  // サブルーチン呼び出し
// ... 処理後ここに戻ってくる

*common_effect
se("sparkle")
wait(500)
ret()  // 呼び出し元に戻る

6. 組み込みコマンド・リファレンス

KSC で利用可能な組み込みコマンド/関数の全一覧です。Interpreter.tsisBuiltinFunction() で定義される 28 個です。各コマンドは IEngineAPI のメソッドを経由してプラットフォーム(Web = PixiJS、Switch = SDL2)に委譲されます。

コマンドシグネチャ非同期役割
表示・演出
bg(id, effect?)async背景切替(IEngineAPI.setBg
ch(id, pose, pos?, fadeMs?)asyncキャラ表示(showChar、pos: L / C / R
ch_anim(id, pose, pos)asyncアニメ付きキャラ表示
ch_hide(id, fadeMs?)async特定キャラ消去
ch_clear(fadeMs?)async全キャラ消去
filter(name, strength?)syncスクリーンフィルタ適用(46 種)
filter_clear()syncフィルタ解除
color_adjust(b, c, s, t)async明度・コントラスト・彩度・色温度
shake(intensity?, ms?)async画面揺れ
オーディオ
bgm(id, vol?, fadeMs?)syncBGM 再生(ループ)
bgm_stop(fadeMs?)条件付BGM 停止(fadeMs 指定時は async)
se(id, vol?)sync効果音(1 回)
voice(id)syncボイス再生
制御・待機
wait(ms)async指定 ms 待機
waitclick()asyncクリック待機
jump(label)ラベルへ移動(スタック非消費)
call(label)サブルーチン呼び出し(復帰位置を push)
ret()サブルーチンから復帰
特殊演出
timeline / timeline_play(id)asyncタイムライン演出実行
battle(id)asyncバトル開始、'win' / 'lose' を返す
map_load(id, x?, y?, dir?)asyncマップ転送
map_exit()asyncマップ終了
player_move(dir, steps)asyncプレイヤー移動
event_move(id, dx, dy, speed?)asyncイベント移動
item_add(id, count)syncアイテム追加
item_remove(id, count)syncアイテム削除
layout_set(id)asyncレイアウト切替

組み込みユーティリティ関数

関数戻り値役割
print(value)voidデバッグ出力(コンソール)
random(min, max)number[min, max] の整数乱数(シード付き乱数で決定論的)
toString(value)string任意値を文字列化
len(array)number配列/文字列/オブジェクトの要素数

詳細なパラメータ・エラー条件は packages/interpreter/docs/spec-builtin-commands.md を参照。


7. 式評価モデル

KSC の式評価は Evaluator.ts再帰下降パーサーで実装されています。

7.1 リテラル

種類
Number423.14-0.5
String"hello"'world'(エスケープ: \n / \t / \\ / \"
Booleantrue / false
nullnull
Array[1, 2, 3](trailing comma 可)
Object{hp: 100, name: "hero"} / {"key": val}

7.2 演算子と優先順位

高い → 低い順で以下の 9 段階:

演算子結合備考
1( expr ) / リテラル / 識別子 / 関数呼び出しPrimary
2.prop / [index]プロパティ・インデックスアクセス(連鎖可)
3!-(単項)前置
4* / / / %Multiplication
5+ / -Addition(+ は文字列結合も)
6> / >= / < / <=Comparison
7== / !=Equality
8&&Logical AND(短絡評価)
9||Logical OR(短絡評価)

代入は文レベルで = / += / -= / *= / /= が利用可能(プロパティ代入 obj.x = 5 も可)。

7.3 暗黙型変換

  • + 演算子 — 片方が string なら文字列結合、そうでなければ数値加算
  • 他の算術・比較 — オペランドを数値に強制変換
  • truthiness(if / while / && / ||)
    • false / 0 / ""(空文字列)/ null / undefinedfalsy
    • それ以外 → truthy

7.4 プロパティアクセスと永続変数

  • ドット記法:player.hp / flags.firstDate
  • ブラケット記法:arr[0] / obj["key"]
  • 永続変数プレフィクスp. で始まる変数(例:p.ended_good)は自動で永続化マップに保存される。セーブ/ロード対象

8. エラー型

ランタイムエラーは [KSC ErrorType] Line N: メッセージ 形式で表示されます。

ErrorType発生シーン典型例
SyntaxErrorトークナイズ・パース失敗#キャラ名 が閉じていない、関数定義の構文ミス
ReferenceError未定義変数/関数/ラベル参照undefined_var の読み取り、存在しないラベルへの jump
TypeError型不一致choice の条件式が boolean でない、組み込み関数の引数型が合わない
RuntimeError実行時の論理エラーwhile の反復回数が上限(100,000)を超過、配列範囲外アクセス
StackOverflow関数/call 再帰が深度上限(16)超過call ループ、再帰関数の非終端
FileNotFoundスクリプト・リソース未検出map_load で存在しないマップ

Levenshtein 距離による類似候補提示

ReferenceError 発生時、編集距離 3 以下(かつ変数名長の半分以下)で最大 3 件の候補が自動提示されます。

[KSC ReferenceError] Line 15: 未定義の変数: afection
候補: affection, addition, actor

9. コメント構文

形式構文ネスト
単行コメント// …
ブロックコメント/* … */不可(開始の /* と最初の */ が対応)

セリフブロック(#キャラ名 ... #)の内部にコメントを書くと、そのままテキストとして表示されます(エスケープ扱いなし)。

インタプリタの役割

KSC インタプリタはスクリプトの「頭脳」です。以下を担当します:

  • 逐次実行: スクリプトを1行ずつ解釈して直接実行
  • 変数の保持: フラグや好感度などのゲーム状態を管理(グローバル/ローカルスコープ対応)
  • フロー制御: if 分岐、ジャンプ、サブルーチン呼び出し(call / ret)、for / while ループ
  • 選択肢の処理: プレイヤーの選択を受け取り、対応するラベルへ移動
  • デバッグモード: ブレークポイント、変数ウォッチ、トレースログ、ステップ実行
  • エラー候補提示: Levenshtein 距離による類似コマンドの提案(「bgg と書きましたが、bg のことですか?」)

インタプリタ自体は画面描画や音声再生を一切行いません。「背景を room に変えて」「BGM を再生して」といった命令を HostAPI 経由で発行するだけです。実際の描画は、各プラットフォームの実装(Web なら PixiJS、Switch なら SDL2)が担当します。

サンプルシナリオ

// === 学園シーン(KSC版) ===
*start

bg("classroom")
ch("sakura", "smile", "C")

#sakura
「おはよう!今日もいい天気だね」
#

affection = 5

ch("sakura", "thinking", "C")
#sakura
「放課後、どうする?」
#

choice {
    "一緒に帰る" {
        affection += 5
        jump("go_home")
    }
    "図書館で勉強" {
        jump("library")
    }
    "デートに誘う" if (affection >= 10) {
        jump("date")
    }
}

*go_home
ch("sakura", "smile", "C")
#sakura
「やったー!一緒に帰ろう」
#
jump("day_end")

*library
ch("sakura", "sad", "C")
#sakura
「そっか...じゃあまたね」
#
jump("day_end")

*date
ch("sakura", "smile", "C")
#sakura
「えっ、デート!? 喜んで!」
#
affection += 10
jump("day_end")

*day_end
def get_rank(score) {
    if (score > 15) return "S"
    if (score > 10) return "A"
    return "B"
}

rank = get_rank(affection)

ch_hide("sakura", 500)
bg("sunset")
#system
好感度ランク: {rank}
こうして放課後が過ぎていった。
#

関連ドキュメント

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