シナリオスクリプト
シナリオスクリプトは、拡張子が .nani のテキストドキュメントで、シーン内の出来事を制御するために使います。スクリプトアセットを作成するには、アセットのコンテキストメニューから Create -> Naninovel -> Scenario Script を選択します。作成したスクリプトは、組み込みの ストーリーエディター で開いて編集できます。Microsoft Word、Google Docs、VS Code など、お好みの外部テキストエディターやコードエディターも使用できます。
シナリオスクリプトの各行はステートメントを表し、コマンド、汎用テキスト、ナビゲーションラベル、またはコメントのいずれかになります。ステートメントのタイプは、行の先頭に配置された記号によって決定されます。
| 記号 | ステートメント |
|---|---|
| @ | コマンド |
| # | ラベル |
| ; | コメント |
行の先頭に上記の記号のいずれも存在しない場合、それは 汎用テキスト ステートメントと見なされます。
ヒント
コンパイラーローカライズ 機能を使用すると、コンパイラーで事前定義されているすべての要素を変更できます。記号、コマンド識別子、定数など、スクリプト作成時に入力する事実上すべての内容が対象です。
コマンド行
@ 記号で始まる行は、コマンドステートメントとして扱われます。コマンドは、シーン内の出来事を制御する1つの操作を表します。たとえば、背景を変更する、キャラクターを移動する、別のシナリオスクリプトをロードするといった操作に使用できます。
コマンド識別子
コマンド記号の直後には、コマンド識別子を記述します。識別子には、コマンドを実装するC#クラスの名前を使用できます。そのクラスに Alias 属性でエイリアスが設定されている場合は、エイリアスも使用できます。
たとえば、@save コマンド(ゲームのオートセーブに使用)は AutoSave C#クラスによって実装されています。実装クラスには [Alias("save")] 属性も適用されているため、スクリプト内で @save と @AutoSave の両方のステートメントを使用してこのコマンドを呼び出すことができます。
コマンド識別子は大文字と小文字を区別しません。以下のステートメントはすべて有効であり、同じ AutoSave コマンドを呼び出します。
@save
@Save
@AutoSave
@autosaveコマンドパラメーター
ほとんどのコマンドには、動作を指定するためのパラメーターがいくつかあります。パラメーターはコマンド識別子の後に記述し、キーと値をコロン(:)で区切ります。キーとなるパラメーター識別子には、コマンドを実装するクラスの対応するパラメーターフィールド名を使用できます。そのフィールドに Alias 属性でエイリアスが設定されている場合は、エイリアスも使用できます。
@commandId paramId:paramValue例として、シーン内で表示されているすべてのアクターを非表示にする @hideAll コマンドを見てみましょう。次のように使用できます。
@hideAlltime number パラメーターを使用して、アクターが完全に非表示になるまでのフェードアウト時間を制御できます。
@hideAll time:5.5これにより、アクターは5.5秒間フェードアウトしてから完全に見えなくなります。
パラメーター値の型
コマンドパラメーターは、それぞれ次のいずれかの型の値を受け取ります。
| 型 | 説明 |
|---|---|
| string | 単純な文字列値。例:LoremIpsum。スペースが含まれる場合は、必ず二重引用符で囲んでください。例:"Lorem ipsum dolor sit amet."。 |
| number | 整数または小数の数値。例:1、-25、1.0、-0.005。 |
| boolean | true または false の2つの値のいずれかを持つことができます。true や false と入力する代わりに ブールフラグ を使用できます。例:@hideAll wait:true の代わりに @hideAll wait!。 |
| named | ドットで区切られたキーと値のペア。キーは文字列で、値は上記の型のいずれかです。名前付き数値の例:foo.8、bar.-20。 |
| list | 上記の型のいずれかの値のカンマ区切りリスト。文字列リストの例:foo,bar,"Lorem ipsum."、数値リストの例:12,-8,0.105,2。 |
プライマリパラメーター
一部のコマンドには、識別子(名前)を指定せずに使用できるパラメーターがあります。これをプライマリパラメーターと呼びます。
たとえば、@bgm コマンドは、再生するオーディオリソースのパスを指定するプライマリパラメーターを受け取ります。
@bgm PianoThemeここでの「PianoTheme」は、path string パラメーターの値です。
1つのコマンドが持てるプライマリパラメーターは1つだけです。指定する際は、必ず他のパラメーターよりも前に記述してください。
オプションおよび必須パラメーター
多くのコマンドパラメーターはオプションです。あらかじめ値が定義されているか、値を指定しなくてもコマンドを実行できるため、省略できます。たとえば、@resetText コマンドでパラメーターを省略すると、デフォルトプリンターのテキストがリセットされます。特定のプリンターを対象にするには、@resetText Dialogue のようにプリンターIDを指定します。
ただし、一部のパラメーターはコマンドを実行するために必須であり、常に指定する必要があります。VS Code 拡張機能は、そのようなパラメーターの割り当てを忘れた場合に警告します。
標準コマンド
組み込みの標準コマンドの一覧と、各コマンドの概要、パラメーター、使用例は、APIリファレンス を参照してください。
コメント行
セミコロン(;)で始まる行は、コメントステートメントとして扱われます。エンジンは実行時にコメントを完全に無視します。自分用のメモや、同じシナリオスクリプトを扱うチームメンバーへの注釈を残す際に使用してください。
; 次のコマンドはゲームをオートセーブします。
@save
@save ; コマンド行の中にもコメントを入れることができます。
# Label ; ラベル行の中にも入れられます。
吾輩は[shake ; インラインコマンドの中にも入れられます]猫である。[; ...空のものも含めて。]このガイドの残りの部分では、サンプルのNaniScriptスニペットに注釈を付けるためにコメントを使用します。
汎用テキスト行
汎用テキスト行を使うと、大量のテキストを含むスクリプトも簡単に記述できます。どのステートメント記号でも始まらない行は、汎用テキストとして扱われます。
吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。汎用テキスト行の先頭に話者IDを記述し、その後にコロンとスペース(: )を置くと、表示するテキストを キャラクターアクター に関連付けられます。
Felix: 吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。テキストに関連付けられたキャラクターの外観を頻繁に変更する場合は、話者IDの後に外観を指定することで、入力の手間を省けます。
Felix.Happy: 吾輩は猫である。名前はまだ無い。上記の行は、次の2行と同じです。
@char Felix.Happy
Felix: 吾輩は猫である。名前はまだ無い。コマンドのインライン化
テキストメッセージを表示している最中に、特定の文字の直後や直前にコマンドを実行したい場合があります。たとえば、特定の単語が表示されたときにアクターが外観(表情)を変えたり、メッセージの途中で記述されたイベントに反応して特定の効果音が再生されたりする場合です。コマンドのインライン化機能により、このようなケースを処理できます。
すべてのコマンド(標準 と カスタム の両方)は、角括弧([ ])を使用して汎用テキスト行にインライン化(埋め込み)できます。
Felix: 吾輩は[char Felix.Happy pos:50]猫である![sfx Explosion]名前はまだ無い。インラインコマンドの構文は、@ 記号を省略してコマンド本体を角括弧で囲む点を除き、通常のコマンドとまったく同じです。どのコマンド行も汎用テキストにインライン化できます。コマンドの効果は変わりませんが、実行のタイミングはテキストメッセージ内の挿入位置によって決まります。
エンジン内部では、汎用テキスト行を解析して個々のコマンドに分け、それぞれをインラインインデックスで識別します。テキストの表示には @print コマンドを使用します。
たとえば、次の汎用テキスト行:
吾輩は猫である。[char Felix.Happy pos:75 wait!]名前はまだ無い。— は、実際にはエンジンによって個々のコマンドのシーケンスとして処理されます。
@print "吾輩は猫である。" !waitInput
@char Felix.Happy pos:75 wait!
@print "名前はまだ無い。" !reset汎用テキスト行内に実際に角括弧を表示するには、バックスラッシュでエスケープします。例:
何らかのテキスト \[ 角括弧内のテキスト \]— は、ゲーム内で 何らかのテキスト [ 角括弧内のテキスト ] と表示されます。
テキスト行が表示された後の入力待ちをスキップするには、行末に [>] を追加します。
; 次の行を表示した後、入力待ちはアクティブになりません
; (プレイヤーは読み進めるために確認の入力を行う必要がありません)。
吾輩は猫である。名前はまだ無い。[>]汎用パラメーター
汎用テキスト行の一部または全体に対して @print パラメーターを指定・変更するには、特別な < コマンドを使用します。このコマンドは汎用テキスト行でのみ使用できます。
; この行の話者はKohakuとYukoアクターになりますが、
; プリンターの表示名は「全員」と表示されます。
Kohaku,Yuko: こんにちは![< as:"全員"]
; 最初の部分は50%の速度で表示され、
; 2番目の部分は250%の速度で待機なしで表示されます。
こんにちは[< speed:0.5]、世界![< speed:2.5 nowait!]このコマンドは、指定されたパラメーターを、自身より前にある最後のテキスト部分に適用します。< とテキストの間に他のコマンドがインライン化されている場合でも同様です。
; 速度は依然として「こんにちは」部分に適用されます。
; パラメーターがインラインコマンドの後にある場合でも同様です。
こんにちは[-][< speed:0.5]、世界!空白区切り文字
汎用テキスト行の先頭や末尾に空白(スペースやタブなど)がある場合は、表示する内容の始まりと終わりを区切り文字で明示すると便利です。特に、ネストを使用する場合は、この区切りが重要になります。
汎用テキスト行の境界を示す区切り文字には、[](空のインラインコマンド)を使用します。
; 「Some text continuation.」を表示(間にスペース2つ)
@group
; 最初の部分の末尾の空白を保持します。
Some text []
; 2番目の部分の先頭の空白を保持します。
[] continuation.[< join!]ラベル行
ラベルは、@goto コマンドでシナリオスクリプト内を移動するための「アンカー」として使用されます。ラベルを定義するには、行の先頭に # 記号を付け、その後にラベル名を続けます。
# Epilogueその後、@goto コマンドを使用してその行に移動できます。
@goto ScriptPath#Epilogue@goto コマンドとターゲットラベルが同じスクリプト内にある場合は、スクリプトパスを省略できます。
@goto #Epilogue# 区切り文字の前後の空白は任意で、スクリプト名とラベル名の両方に空白を含めることができます。以下のエンドポイント形式はすべて有効です。
@goto Script#Label
@goto Script #Label
@goto Script# Label
@goto Script # Label
@goto Multi Word Script # Multi Word Labelシナリオルート
ナビゲーションコマンドで指定する「アンカー」はエンドポイントと呼ばれます。エンドポイントは、スクリプトパスとラベルの2つの部分で構成されます。ラベルは省略可能で、省略するとスクリプトの先頭を指します。スクリプトパスは、シナリオルートを基準としたシナリオファイルへの相対パスで、.nani 拡張子は含めません。
シナリオルートは、プロジェクト内のすべてのシナリオファイルが保存される最上位ディレクトリです。たとえば、Unityプロジェクト内の次のディレクトリ構造を考えてみましょう。
Assets
└── Scenario/
├── Prologue.nani
├── CommonRoute/
│ ├── Day1/
│ │ ├── Scene1.nani
│ │ └── Scene2.nani
│ └── Day2/
│ └── Scene1.nani
└── RouteX/
└── SceneX.naniこの場合、シナリオルートは Assets/Scenario ディレクトリです。Assets/Scenario/RouteX/SceneX.nani スクリプトファイルに移動するには、次のエンドポイントを使用します:RouteX/SceneX。
シナリオファイルを作成または移動すると、シナリオルートは自動的に検出されます。現在のルートはスクリプト構成メニューで確認できます。
エンドポイント構文
Naninovelは4種類のエンドポイント構文をサポートしており、場合によってはより簡潔なパスを記述できます。
正規エンドポイント
これはデフォルトの構文で、シナリオルート から始まるスクリプトへのフルパスを含みます。常にサポートされており、現在のスクリプトの場所に依存しませんが、ターゲットスクリプトまでのすべてのディレクトリを含める必要があります。
; 「Assets/Scenario/Prologue.nani」スクリプトの先頭に移動します。
@goto Prologue
; 「Assets/Scenario/CommonRoute/Day1/Scene1.nani」スクリプトの
; 「Action」ラベルに移動します。
@goto CommonRoute/Day1/Scene1#Actionローカルエンドポイント
この構文は、現在のスクリプト内のラベルに移動する場合にのみサポートされます。ラベルのみが含まれます。
; 現在のスクリプト内の「Action」に移動します。
@goto #Action相対エンドポイント
相対パスでは、現在のスクリプトの位置を基準に移動先を記述するため、エンドポイントを簡潔に書けます。
; 「Assets/Scenario/CommonRoute/Day1/Scene1.nani」内にいるとして、
; 同じディレクトリ内の「Scene2.nani」ファイルに移動します。
@goto ./Scene2
; 現在のディレクトリの1つ上の「Day2」ディレクトリ内の「Scene1.nani」ファイルに移動します。
@goto ../Day2/Scene1
; 現在のディレクトリの2つ上の「RouteX」ディレクトリ内の
; 「SceneX.nani」ファイルに移動します。
@goto ../../RouteX/SceneXワイルドカードエンドポイント
パスにディレクトリを含めたくない場合は、スクリプト名のみを指定してワイルドカードパスを使用できます。これは、スクリプト名がプロジェクト全体で一意である場合にのみ機能します。
; 「Prologue.nani」スクリプトがどこにあっても移動します。
@goto */Prologue
; 「Scene1.nani」ファイルが複数あるため、エラーになります。
@goto */Scene1
; 「Day1」の下には「Scene1.nani」ファイルが1つしかないため機能します。
@goto */Day1/Scene1ブールフラグ
ブールフラグを使うと、ブールパラメーターの値を短く記述できます。例:
; Kohakuキャラクターを表示します。
@char Kohaku visible!
; 次と同等です:
@char Kohaku visible:true
; Kohakuキャラクターを非表示にします。
@char Kohaku !visible
; 次と同等です:
@char Kohaku visible:false
; インラインコマンドもフラグをサポートしています。
吾輩は猫である。[shake Camera ver! !wait]名前はまだ無い。
; 次と同等です:
吾輩は猫である。[shake Camera ver:true wait:false]名前はまだ無い。完全なブール形式が必要になるのは、シナリオ式 で値を動的に評価する場合です。例:
; 「score」変数が10より大きい場合、Kohakuを表示します。
@char Kohaku visible:{score>10}— または、ブールパラメーターがプライマリパラメーターの場合。例:
; プライマリパラメーターでカメラルックモードを無効にします。
@look false後者の場合でも、プライマリパラメーターのIDを指定すれば、フラグを使用できます。
; ブールフラグでカメラルックモードを無効にします。
@look !enable条件付き実行
デフォルトでは、スクリプトは順番に実行されます。分岐を設けるには、すべてのコマンドで使える if または unless パラメーターを使用します。
; 「level」が9000より大きい場合、選択肢を追加します。
@choice "9000を超えている!" if: level above 9000
; 「dead」がfalseの場合、printコマンドを実行します。
@print "まだ生きています。" if: not dead
; 同じですが、より簡潔です。
@print "まだ生きています。" unless:dead
; 「insane」がtrueの場合、または1〜10の範囲のrandom関数が
; 5以上を返す場合、「@glitch」コマンドを実行します。
@glitch if: insane or random(1, 10) is at least 5
; 「score」が7〜13の間の場合、または「lucky」がtrueの場合、
; 「LuckyEnd」スクリプトに移動します。
@goto LuckyEnd if: (score is at least 7 and score is at most 13) or lucky
; 代わりにブール演算子を使用することもできます(結果は上記と同じです)。
@goto LuckyEnd if: (score >= 7 & score <= 13) | lucky
; インラインコマンド内の条件。
吾輩は猫である。[sfx Applause if:score>=10]名前はまだ無い。
; 式内の二重引用符をエスケープします。
@print {remark} if: remark = "Saying \"Stop the car\" was a mistake."条件ブロック
@if と @else を使用して、複数行の条件ブロックを ネスト できます。
; 「score」変数に応じてテキスト行を表示します:
; 「不合格です。もう一度挑戦してください!」 - scoreが6以下の場合。
; 「テストに合格しました。」と「素晴らしい!」 - scoreが8より大きい場合。
; 「テストに合格しました。」と「お見事!」 - scoreが7より大きい場合。
; 「テストに合格しました。」と「よくできました!」 - それ以外の場合。
@if score is above 6
テストに合格しました。
@if score is above 8
素晴らしい!
@or score is above 7
お見事!
@else
よくできました!
@else
不合格です。もう一度挑戦してください!条件ブロックをテキスト行にインラインで記述することもできます。その場合は、@endif でブロックの終わりを示します。
; 「score」変数に応じてテキスト行を表示します:
; 「テスト結果:不合格。」 - scoreが6以下の場合。
; 「テスト結果:満点!」 - scoreが8より大きい場合。
; 「テスト結果:合格。」 - それ以外の場合。
テスト結果:[if score>8]満点![or score>6]合格。[else]不合格。[endif]逆の条件を指定するには、@unless を使用します。
; deadがfalseの場合は「まだ生きています!」を表示し、それ以外の場合は「もうおしまいです。」を表示します。
@unless dead
まだ生きています!
@else
もうおしまいです。
; 「score」変数に応じてテキスト行を表示します:
; 「テスト結果:合格。」 - scoreが10以上の場合。
; 「テスト結果:不合格。」 - scoreが10未満の場合。
テスト結果:[unless score<10]合格。[else]不合格。[endif]注意
条件式と使用可能な演算子の詳細については、シナリオ式 ガイドを参照してください。
ネスト
@if、@choice、@while などのコマンドでは、インデントを使って、そのコマンドに他のコマンドや汎用テキスト行を関連付けられます。
@if score > 10
@bgm Victory
よくできました、テストに合格です!ここで、@bgm コマンドとそれに続く汎用テキスト行は、@if コマンドに関連付けられています。
この機能をサポートするコマンドはネストホストと呼ばれます。C#では、これらのコマンドは Command.INestedHost インターフェースを実装しています。ホストコマンドは、ネストされたコマンドを実行するかどうか、どれを実行するか、どの順序で実行するかを制御します。
各ホストコマンドには、ネストされたコマンドを実行するときの独自の動作があります。たとえば、@if は条件が満たされない場合にネストされたコマンドをスキップしますが、@choice はプレイヤーが関連する選択肢を選択した場合にのみネストされたコマンドを実行します。
@if score > 10
よくできました、テストに合格です!
@bgm Victory
@spawn Fireworks
@or attempts > 100
どうしようもないですね... 助けが必要ですか?
@choice "はい、お願いします!"
@set score+=10
@goto #BeginTest
@choice "もう少し頑張ってみます。"
@goto #BeginTest
@else
不合格です。もう一度挑戦してください!
@goto #BeginTestネストされたブロックのインデント方法に注目してください。各レベルには正確に4スペースを使用します。または、1つのタブを使用することもできます。それ以外のスペース数や空白文字はインデントとして認識されません。ネストされたブロックは任意の深さにできます。レベルごとにインデントを4スペースまたはタブ1つずつ増やすだけです。
複数のコマンドを単一のホストの下にグループ化するには、@group コマンドを使用します。
; randomコマンドはネストされた行の1つを選択しますが、ネストされた行の子は
; 無視します。ここではgroupコマンドを使用して複数の行を結合し、
; randomコマンドがそれらを一緒に実行するようにしています。
@random
@group
@back tint:red
赤く塗れ。
@group
@back tint:black
黒く塗れ。非同期実行
一部のコマンドは、時間をかけて実行される場合があります。たとえば、@hide コマンドは、設定された時間(time パラメーターで変更可能)をかけて、指定されたアクターをフェードアウトします。次の例を考えてみましょう。
@hide Kohaku
@show Yuko— 再生すると、Kohakuがフェードアウトしている間に、Yukoアクターもフェードインし始めます。これは、デフォルトでは非同期コマンドの完了を待たずに次へ進むためです。@show は、@hide がKohakuのフェードアウトを開始した直後にYukoのフェードインを開始します。
再生を続行する前に非同期コマンドが完了するのを待ちたい場合は、wait パラメーターを使用します。
@hide Kohaku wait!
@show Yuko— これで、YukoはKohakuが完全にフェードアウトした後にのみフェードインを開始します。
シーンを設定するために複数の非同期コマンドを使用し、それらすべてが完了するまで待つのが一般的です。プロセスを簡素化するには、@await コマンドを使用します。
; ネストされた行を並行して実行し、すべて完了するまで待ちます。
@await
@back RainyScene
@bgm RainAmbient
@camera zoom:0.5 time:3
; 次の行は、上記がすべて完了した後に実行されます。
雨が降り始めた...並行再生
個々のコマンドはデフォルトで非同期に実行されます。それに加えて、一連のコマンドを独立した制御フローと再生状態で動かし、メインシナリオと並行して実行したい場合もあります。
@async コマンドを使うと、ネストされた行を専用のスクリプトトラック上で、メインの再生処理と並行して実行できます。たとえば、シナリオを通常どおり進めながら、バックグラウンドで複合アニメーションを実行する場合によく使われます。
; 音楽をフェードしながら、カメラを3つのポイントにゆっくりとパンします。
@async
@bgm volume:0.7 fade:10
@camera offset:4,1 zoom:0.5 time:3 wait!
@bgm volume:0.3 fade:5
@camera offset:,-2 zoom:0.4 time:2 wait!
@stopBgm fade:10
@camera offset:0,0 zoom:0 time:3 wait!
; 上記のアニメーションが独立して実行されている間、以下のテキストが表示されます。
...— また、一連のコマンドをループで実行することもできます。
@async loop!
@spawn Pebbles
@shake Camera
@wait { random(3, 10) }
; 下のテキストが表示されている間、上記のアニメーションはループで実行されます。
気をつけて!アニメーションの進行中にゲームがセーブおよびロードされたとしても、現在の再生状態が復元され、セーブ時のポイントからアニメーションが続行されます。ロールバックも同様に機能します。
非同期タスク
上記のループを停止したい場合や、ループしない非同期シナリオブロックの完了を待ってから先に進みたい場合には、非同期タスクが役立ちます。@async コマンドのオプションのプライマリパラメーターで、そのコマンドが実行する非同期タスクに名前を付けます。後で @stop または @await コマンドにその名前を指定すれば、タスクを停止(キャンセル)したり、完了を待ったりできます。
; 「Quake」非同期タスクを開始します。
@async Quake loop!
@spawn Pebbles
@shake Camera
@wait { random(3, 10) }
...
; ある時点でタスクを停止します。
@stop Quake同様に、非同期タスクの完了を待つこともできます。
@async CameraPan
@camera offset:4,1 zoom:0.5 time:3 wait!
@camera offset:,-2 zoom:0.4 time:2 wait!
...
; カメラをリセットする前に、パンアニメーションが終了していることを確認します。
@await CameraPan
@camera offset:0,0 zoom:0タスクの残り時間を待ちたくない場合は、complete! フラグを使用してタスクを即座に完了させることもできます。
; カメラアニメーションを完了し、即座にリセットします。
@await CameraPan complete!
@camera offset:0,0 zoom:0 time:0ヒント
よく使うアニメーションやその他の非同期タスクを別のスクリプトにカプセル化し、@gosub コマンドを使用して他のスクリプトから再利用することを検討してください。
@gosub FX#Quake
...
@stop Quake
@gosub FX#CameraPan
...
@await CameraPan# Quake
@async Quake loop!
@spawn Pebbles
@shake Camera
@wait { random(3, 10) }
@return
# CameraPan
@async CameraPan
@bgm volume:0.7 fade:10
@camera offset:4,1 zoom:0.5 time:3 wait!
@bgm volume:0.3 fade:5
@camera offset:,-2 zoom:0.4 time:2 wait!
@stopBgm fade:10
@camera offset:0,0 zoom:0 time:3 wait!
@returnトラックの同期
より高度な使い方として、並行して実行中のトラック同士を結合(同期)したり、メイントラックと結合したりする場合があります。このような同期には @sync コマンドを使用します。
60秒以内に爆弾を解除してください!
@async Boom
@wait 60
; 60秒後、「Boom」タスクが停止されていない場合、
; 下の @sync コマンドはメイントラックを強制的にここに移動させ、
; その後「BadEnd」スクリプトに移動します。
@sync
@goto BadEnd
; 一連の爆弾解除パズルをシミュレートします。
解除パズル1。
解除パズル2。
解除パズル3。
; 「Boom」非同期タスクが停止されたため、メイントラックは
; 中断することなく実行を継続します。
@stop Boom
爆弾は解除されました!— Boom 非同期タスクで @sync コマンドを使用しなかった場合、@goto コマンドは非同期トラックで実行され、メイントラックはさらに実行を続けるため、BadEnd とメインシナリオの両方が並行して実行されることになります。@sync が行うのは、ターゲットトラック(デフォルトではメイントラック)をこのコマンドが使用されている行に強制的に移動させ、ホストトラックを破棄することです。実質的には、ホストトラックをターゲットトラックと入れ替えることになります。
テキスト識別
スクリプトのローカライズ や オートボイス などの機能では、シナリオスクリプトに記述されたテキストを他のリソースに関連付ける必要があります。たとえば、オリジナルの代わりに表示する翻訳テキストや、テキストが表示されるときに再生するボイスクリップなどです。これが機能するには、そのような各テキストに一意の識別子を割り当てる必要があります。
デフォルトでは、Naninovelはスクリプトアセットのインポート時に、コンテンツハッシュによってすべてのローカライズ可能なテキストを自動的に識別します。テキストを変更しない限り、これは正常に機能します。変更すると、関連付けが壊れます。オートボイスクリップを再マッピングするか、変更されたテキストステートメントを再翻訳する必要があります。
テキストの編集後も関連付けを維持するには、Naninovel -> Tools -> Text Identifier エディターメニューからテキスト識別ユーティリティを開いて使用します。このユーティリティは、シナリオスクリプト内のローカライズ可能な各テキストに一意のIDを自動生成して書き込みます。次の例のように、ローカライズ可能な各パラメーターの末尾に識別子が追加されます。
Kohaku: やあ!|#1|[-]元気?|#2|
@choice "選択肢1|#3|"
@choice "選択肢2|#4|"IDを削除または変更しない限り、関連付けは壊れません。テキストIDが目立ちすぎないよう、IDE拡張機能とストーリーエディターでは薄い色で表示します。
このユーティリティは、生成するテキストIDがすべて一意で、そのスクリプト内で過去に使われていないことを保証します。そのために、リビジョン番号を NaninovelData/ScriptRevisions エディターアセットに保存して管理しています。テキストIDが割り当てられた行を削除しても、そのIDが別の場所で再利用されることはありません(手動で追加した場合を除きます)。
識別済みテキスト参照
ごくまれに、ローカライズ可能なテキスト識別子を意図的に複製したい場合があります。たとえば、C#でコマンドのインスタンスを作成し、そこでスクリプト内に指定されたローカライズ済みのパラメーターを再利用する場合です。
単に LocalizableTextParameter 値を割り当てると、Naninovelは重複するテキストIDについて警告します。代わりに、静的メソッド CommandParameter.Ref() を使用してください。
var print = new PrintText();
print.AuthorLabel = CommandParameter.Ref(otherPrint.AuthorLabel);シナリオスクリプト内の既存のローカライズされたテキストを参照するには、識別子の前に & を付けます。
; 「テキスト」を含む選択肢を表示し、その後同じテキストを表示します。
@choice "テキスト|#SOMEID|"
@print |#&SOMEID|タイトルスクリプト
タイトルスクリプトは、スクリプト構成メニューで割り当てられる特別なシナリオスクリプトです。割り当てられると、エンジンの初期化後、または @title コマンドやさまざまなゲーム内メニューの「Title」ボタンでタイトルメニューに戻るときに自動的に再生されます。タイトルスクリプトは、背景、音楽、効果、タイトルUIの表示など、タイトル画面のシーンをセットアップするために使用できます。
このスクリプトは、プレイヤーがタイトルUI内で「NEW GAME」や「EXIT」をクリックしたとき、またはゲームをロードするためにいずれかのセーブスロットをクリックしたときにコマンドを呼び出すためにも使用できます。以下はタイトルスクリプトの例です。
; タイトルメニューの外観を設定します。
@back MainMenuBackground
@bgm MainMenuMusic
@spawn Rain
@showUI TitleUI
@stop
# OnNewGame
; 以下のコマンドは、プレイヤーが「NEW GAME」をクリックしたときに実行されます。
; 「stopBgm」コマンドが待機されているため、
; 新しいゲームのロードが始まる前に音楽が完全に停止することに注目してください。
@sfx NewGameSoundEffect
@stopBgm wait!
@stop
# OnLoad
; 以下のコマンドは、プレイヤーがセーブしたゲームをロードしたときに実行されます。
@sfx LoadGameEffect
@wait 0.5
@stop
# OnExit
; 以下のコマンドは、プレイヤーが「EXIT」をクリックしたときに実行されます。
@sfx ExitGameEffect
@wait 1.5
@stopFountain
Fountain は、人がそのまま読めるテキスト形式で脚本を書き、共有するためのマークアップ構文です。Highland、Final Draft、Scrivener などの脚本執筆ソフトウェアでサポートされています。
Naninovelは、.fountain ドキュメントを .nani スクリプトに変換するツールを提供しているため、Fountain互換ソフトウェアでプロジェクトの初期シナリオを作成し、それをNaninovelに移行できます。
エディターメニューからツールを開きます:Naninovel -> Tools -> Fountain Screenplay。ソース .fountain ドキュメントと生成される .nani ファイルの出力フォルダーを選択し、「Convert Screenplay」をクリックします。
Fountainの Action および Dialogue 段落は 汎用テキスト行 に変換されます。その他の構文構造は コメント行 として表されます。脚本を複数の .nani スクリプトに分割したい場合は、Fountainの Section マークアップを使用します。たとえば、次の脚本を考えてみましょう。
# Episode 1
## Scene 1
...
## Scene 2
...
# Episode 2
## Scene 1
...これは、フォルダーに整理された次のシナリオスクリプトに変換されます。
Episode 1/Scene 1.naniEpisode 1/Scene 2.naniEpisode 2/Scene 1.nani