3. Ink/Inky の使い方
はじめに
Inkというスクリプト言語では、複雑な分岐を持つインタラクティブな物語を制作できます。
1. Inkの基本用語
| 用語 | 読み | 説明 |
|---|---|---|
| Knot | ノット | 物語の「章」や「シーン」にあたる大きな単位。=== knot_name === のように定義します。 |
| Stitch | スティッチ | Knot内の「場面」や「段落」のような小さな単位。整理のために使います。= stitch_name のように定義します。 |
| Divert | ダイバート | 物語の流れを別のKnotやStitchにジャンプさせる命令。-> で示します。いわゆる「goto文」です。 |
| Choice | チョイス | プレイヤーに提示する選択肢。* で示します。 |
| Variable | バリアブル | プレイヤーの名前、スコア、フラグなどを保存する「変数」です。VAR で定義します。 |
| Content | コンテンツ | プレイヤーに表示されるテキスト部分です。 |
2. 早見表(チートシート)
| 記法 | 機能 | 例 |
|---|---|---|
// コメント |
コメント(ゲームに表示されないメモ) | // この行はゲームに表示されない |
=== KnotName === |
Knot(ノット)の定義 | === chapter1 === |
= StitchName |
Stitch(スティッチ)の定義 | = forest_enter |
-> Target |
Divert(ダイバート) | -> chapter2 or -> forest.exit |
* Text |
選択肢 | * ドアを開ける |
+ Text |
表示され続ける選択肢(Sticky Choice) | + 地図を見る |
VAR x = 10 |
変数の宣言 | VAR score = 0 |
~ x = 11 |
変数の変更 | ~ score = score + 10 |
{x} |
変数の値を表示 | 現在のスコアは {score} 点です。 |
| `{ A | B | C }` |
{ condition: ... } |
条件分岐 | * {has_key} 鍵を使う |
EXTERNAL func() |
外部関数の宣言 | EXTERNAL PlaySound(soundName) |
3. よく使う基本機能
ここからは、具体的な機能と書き方を例文を交えて解説します。
3.1. KnotとDivert
物語は最初のKnotから始まり、-> を使って別のKnotへジャンプすることで進行します。
// === 森の入口 ===
// 物語はここから始まる
=== forest_entrance ===
森の入口に着いた。
目の前には二つの道がある。
-> path_choice // path_choiceというKnotに移動する
=== path_choice ===
どちらの道へ進みますか?
// この先は選択肢で分岐(後述)
* 左の道へ進む -> left_path
* 右の道へ進む -> right_path
=== left_path ===
左の道を進むと、美しい泉を見つけた。
-> END // 物語を終了する
=== right_path ===
右の道を進むと、恐ろしいオオカミに遭遇した!
-> END3.2. 選択肢 (Choice)
プレイヤーの行動を決めさせるには * を使います。
- 基本的な選択肢
*: 一度選ぶと消えます。 - 表示され続ける選択肢
+(Sticky Choice): 選んだ後も、再びその場面に戻ってきたときに表示され続けます。インベントリや地図の確認などによく使います。
=== crossroad ===
十字路だ。どうしよう?
* まっすぐ進む
まっすぐ進むと、城が見えてきた。
-> castle_gate
+ 地図を見る
地図を開くと、この先が城だとわかった。
// 同じ場所に戻ることで、他の選択肢を選び直せる
-> crossroad
* 引き返す
来た道を引き返した。
-> END3.3. 変数
変数を使うと、プレイヤーの行動や状態を記憶し、物語をダイナミックに変化させられます。
VARで変数を宣言します。(物語の最初にまとめて書くのがおすすめです)~で変数の値を変更します。{}で囲むと、テキスト内に変数の値を表示できます。
VAR score = 0
VAR player_name = "勇者"
=== cave_entrance ===
洞窟の入口で宝箱を見つけた。
* 開ける
~ score = score + 100
中から100ゴールド見つけた!
現在のスコア: {score}
* 無視する
今は先を急ごう。
-> cave_exit
=== cave_exit ===
{player_name}は洞窟を抜けた。
最終スコアは {score} 点だった。
-> END一時変数 (temp)
tempで一時変数を宣言できます。
=== classroom ===
~ temp status = getStatus("quest_id")
{ status:
- 0: ステータスは0です
- 1: ステータスは1です
- else: ステータスはその他です
}3.4. 分岐
変数と条件分岐を組み合わせることで、物語は格段に面白くなります。
if
{ x > 0:
~
}if/else
{ x > 0:
~
- else:
~
}if/elseはこちらでもよい
{
- x > 0:
~
- else:
~
}switch
{ x: ~
- 0: ~
- 1: ~
- else: ~
}ネスト
条件分岐の中に、さらに条件分岐を入れることもできます。
VAR has_key = true
VAR time = "night"
=== gate ===
城の門の前に来た。
{ has_key:
// まず鍵を持っているかチェック
鍵を使って門を開けよう。
{ time == "night":
// 次に夜かどうかチェック
夜だったため、衛兵は居眠りしていた。
あなたは静かに中へ入った。
-> inside
- else:
衛兵に見つかってしまった!
-> jail
}
- else:
門には鍵がかかっている。
-> END
}3.5. ゲームエンジンと連携 (外部関数)
Inkはテキストを管理する言語ですが、音を鳴らしたり、画面を揺らしたりといった演出はできません。そこで使うのが外部関数 (External Function) です。
EXTERNAL で「こういう名前の関数をゲームエンジン側で呼び出しますよ」とInkに宣言しておけば、物語の途中でその関数を呼び出せます。
主な用途
- BGMや効果音の再生
- キャラクターの立ち絵や表情の変更
- 画面のシェイクやフラッシュといった演出
例:効果音を鳴らす
Ink側の記述
// ゲームエンジン側で実装する関数を宣言
EXTERNAL PlaySound(soundName)
=== treasure_chest ===
宝箱を開けると、まばゆい光が溢れ出した!
// "Fanfare"という名前の効果音を再生するよう指示
~ PlaySound("Fanfare")
中から伝説の剣を手に入れた!
-> ENDゲームエンジン側
この後、Unityなどのゲームエンジン側でPlaySoundという名前の関数を実装し、「“Fanfare"という文字列を受け取ったら、ファンファーレの音源を再生する」という処理を書くことで、連携が完成します。
3.6. 繰り返しとランダム表示
同じ場面でも、来るたびに違うテキストを表示させると、物語が生き生きします。
| 記法 | 機能 |
|---|---|
{once: ...} |
一度しか表示されないテキスト |
{ A | B | C } |
呼び出すたびにA, B, Cの順で表示 |
~ { A | B | C } |
A, B, C, A, B, C … と循環して表示 |
{shuffle: ...} |
毎回ランダムな順で表示 |
=== town_gate ===
町の門番が立っている。
{once:
- 「ようこそ、旅人さん。」 // 最初に訪れた時だけ表示
- else:
// 2回目以降はこちらが表示される
- 門番はこちらを一瞥しただけだ。
}
彼は言った。「{今日はいい天気だな。|何か用かい?|異常なし。}」
// 訪れるたびにセリフが順番に変わる
-> town_square4. 応用機能
| 機能 | 記法 | 説明 |
|---|---|---|
| 関数 | === my_func(arg) === |
引数を受け取り、値を返すことができる再利用可能なコードブロック。計算などで使います。 |
| トンネル | -> my_tunnel -> |
関数のように呼び出せますが、処理後に自動で呼び出し元に戻ります。物語の「寄り道」に便利です。 |
| スレッド | ->> my_thread |
メインの物語とは別に、裏側で並行して処理を動かします。時間経過の管理などに使います。 |
| リスト | LIST fruits = apple, orange |
複数の値をまとめて管理します。インベントリ管理などに強力です。 |
| 定数 | CONST MAX_HP = 100 |
変更できない値を定義します。 |
まとめ
Inkについて解説しました。