DevTools
エディタホスト向けの`DevTools`静的ユーティリティクラス。アクションと要素の識別、ダイアログの状態、舞台への直接登録を扱う
DevToolsはエディタホスト向けの静的ユーティリティクラスです。ライブプレビュー、インスペクター、ステージエディタ、サムネイルレンダラーなどが該当します。こうしたホストが必要とし、通常のストーリーAPIでは提供されないエンジン内部の機能を公開します。アクションの識別、表示可能な要素の直接登録、現在のダイアログ行、レイヤー画像のレイヤーごとのソースなどです。
ゲームを書くためだけであれば、これは一切必要ありません。
import {DevTools} from "narraleaf-react/built-in";すべてのメンバーは静的で、構築が必要なものはありません。DevToolsはパッケージのルート(narraleaf-react)からも再エクスポートされていますが、narraleaf-react/built-inのエントリポイントを優先してください。
アクションの識別
ストーリーが構築されると、すべてのアクションにidが割り当てられます。静的idが割り当てられていない限り、生成されたid(a-0、a-1、...)が使われ、静的idがあればそちらが使われます。静的idは、ストーリーの再構築をまたいでアクションをアドレス可能にするもので、例えばfastForwardのターゲットとして使われます。重複した静的idは、ストーリー構築時に拒否されます。
getActionId
アクションの解決済みidを読み取ります。
action: LogicAction.Actions- 対象のアクション- 戻り値は
string- ストーリー構築時に割り当てられたid
const id = DevTools.getActionId(action);setActionId
アクションの解決済みidを上書きします。
action: LogicAction.Actions- 対象のアクションid: string- 新しいid- 戻り値は
LogicAction.Actions- 同じアクション
DevTools.setActionId(action, "chapter1:intro");getStaticId
アクションの静的idを読み取ります。静的idがなく、生成されたidを使っている場合はnullを返します。
action: LogicAction.Actions- 対象のアクション- 戻り値は
string | null
const staticId = DevTools.getStaticId(action);setStaticId
ストーリー構築をまたいでアクションが保持する静的idを割り当てます。nullを渡すと解除され、生成されたidへ戻ります。
action: LogicAction.Actions- 対象のアクションid: string | null- 静的id- 戻り値は
LogicAction.Actions- 同じアクション
DevTools.setStaticId(action, "chapter1:intro");チェーンとアクション
chainToActions
チェーン可能な式を展開し、それが生成するアクションのフラットなリストにします。
chain: Proxied<LogicAction.GameElement, Chained<LogicAction.Actions>>- チェーンされた式。ChainedActionsを参照- 戻り値は
LogicAction.Actions[]
const actions = DevTools.chainToActions(
image.char(["sad"]).darken(0.5, 300)
);wrapAction
複数のアクション、またはチェーンを、単一のControlアクションにラップし、ひとまとまりのシーケンスを1つの文として扱えるようにします。
action: LogicAction.Actions[] | Proxied<LogicAction.GameElement, Chained<LogicAction.Actions>>- ラップするアクションまたはチェーン- 戻り値は
ControlAction
const wrapped = DevTools.wrapAction(image.char(["sad"]).darken(0.5, 300));ステージの検査
getCurrentScene
getLayerSrcs
レイヤー画像の各レイヤーのsrcを、下から上への順で返します。nullのエントリは、指定されたタグに対して何も描画しないレイヤーです。レイヤー画像でない場合は空の配列を返します。
レイヤー画像は単一のsrcではなくスタックであるため、ステージ上の要素の独自サムネイルをレンダリングするホストは、これらを順番どおりに自分で合成する必要があります。
image: Image- Imageを参照tags?: string[]- 解決に使うタグ。デフォルトは画像の現在のタグ- 戻り値は
(string | null)[]
// 画像が今まさに表示している内容
const srcs = DevTools.getLayerSrcs(yuko);
// => ["yuko/body.png", "yuko/casual.png", "yuko/jacket.png", null, "yuko/mouth_sad.png"]
// 別のタグの組み合わせでは何が表示されるか、ステージには触れずに確認する
const preview = DevTools.getLayerSrcs(yuko, ["uniform", "happy"]);
for (const src of preview) {
if (src === null) continue; // このレイヤーは何も描画しない
// srcを順番どおりに自分のキャンバスへ描画する
}getDisplayableTransformProps
表示可能な要素の現在の変換状態のprops(位置、不透明度、ズーム、回転、拡大縮小、エフェクト)を読み取ります。返されるのは浅いコピーなので、それを変更してもステージ上の何かが変わることはありません。
ライブなポーズをキャプチャする用途を想定しています。例えば、要素の現在のステージ上の状態でモーションエディタを事前に埋めるような場合です。
displayable: LogicAction.DisplayableElements- Displayableを参照- 戻り値は
Record<string, unknown>
const pose = DevTools.getDisplayableTransformProps(image);setDisplayableTransformProps
表示可能な要素の変換状態のpropsを、アニメーションなしで上書きし、要素がマウント済みであれば結果を即座にDOMへ反映します。
デフォルトでは、指定したpropsは現在の状態にマージされます。merge: falseを指定すると、以前の状態は完全に破棄されます。
gameState: GameState- GameStateを参照displayable: LogicAction.DisplayableElements- 更新する要素props: Record<string, unknown>- 書き込む変換のpropsoptions?: { merge?: boolean }-mergeのデフォルトはtrue
DevTools.setDisplayableTransformProps(gameState, image, {opacity: 0.5});
// マージではなく、状態全体を置き換える
DevTools.setDisplayableTransformProps(gameState, image, pose, {merge: false});変換状態が進行中のトランスフォームによってロックされている場合は例外を投げます。ステージがアイドル状態のときにのみ注入してください。
この書き込みはセーブデータに伝えられます。 セーブフォーマットv3(0.26.0)以降、要素は何かがその状態の変化を通知したときにのみセーブへ反映されます。これはエンジンが、あるアクションがその要素に対して実行されるたびに行っていることです。この呼び出しはそのパスの外で状態を書き換えるため、代わりにその表示可能な要素に対してmarkDirty()を呼び出します。ホストが他の手段で要素の状態を書き込む場合は、自分でこれを呼び出す必要があります。呼び出さなければ、この書き込みはスキップされ、ロード時にはスクリプトが書いた状態に戻ります。app.debug: trueのとき、エンジンは定期的にすべての要素を巡回し、マークされていない状態を見つけると警告します。SavedGameを参照してください。
registerDisplayable
表示可能な要素を、displayable:initアクションを発行せずに、シーンのレンダーツリーへ登録します。要素はコンストラクタの設定にある変換状態で直ちにレンダリングされ、表示されるかどうかはopacity > 0によって決まります。
ステージを事前にポーズさせておきたいホスト向けです。計算済みの状態をコンストラクタの設定として要素を構築し(設定の状態はelement.reset()やnewGame()を経ても保持されます)、その後Scriptアクションから、あるいはマウント後に登録します。
この呼び出しは冪等です。シーンルートの自動初期化アクションがすでに登録済みの要素は、そのまま変更されません。
gameState: GameState- GameStateを参照displayable: LogicAction.DisplayableElements- 登録する要素scene?: Scene | null- デフォルトはnull。Sceneを参照layer?: Layer | null- デフォルトはnull。Layerを参照
const ghost = new Image({src: "yuko/body.png", opacity: 0.4});
DevTools.setElementId(ghost, "preview:ghost");
DevTools.registerDisplayable(gameState, ghost, scene, layer);setElementId
要素に明示的なidを割り当てます。
シーンのアクションツリーから到達可能な要素は、ストーリー構築時に生成されたid(e-0、e-1、...)を受け取ります。registerDisplayableで直接登録された要素はそのツリーの外にあるため、そのままではデフォルトのidを使い続け、Reactのkeyとして他の要素と衝突してしまいます。生成されるidと決して衝突しないよう、独自のプレフィックスを持つ一意なidを与えてください。
element: LogicAction.GameElement- 対象の要素id: string- 割り当てるid
DevTools.setElementId(ghost, "preview:ghost");setElementStaticId
ストーリー構築後も保持される名前を要素に与えます。
これを設定しない場合、要素のidは構築時に生成され、その周囲のストーリーが変わると変化します。セーブはidによって要素の状態を復元するため、スクリプトの編集後も状態を維持する必要があるすべての要素には名前を付けてください。
setElementIdはidを直接割り当てますが、シーンのアクションツリーから到達可能な要素については、構築のたびにそれが上書きされます。ここにnullを渡すと名前を解除し、生成されたidを使うようになります。
element: LogicAction.GameElement- 対象の要素id: string | null- 保持する名前。nullを渡すと生成されたidを使う
const classroom = new Image({src: "bg/classroom.png"});
DevTools.setElementStaticId(classroom, "bg:classroom");ダイアログ
getCurrentDialog
現在プレイヤーに提示されているダイアログ行を読み取ります。画面に何も表示されていない場合はnullを返します。ADVとNVLの両方の表示方式に対応します。
gameState: GameState- GameStateを参照- 戻り値は
DevToolsCurrentDialog | null- DevToolsCurrentDialogを参照
const dialog = DevTools.getCurrentDialog(gameState);
if (dialog?.ended) {
console.log("waiting for the player to advance", dialog.actionId);
}onDialogStateChange
現在提示されているダイアログ行の変化(作成、タイプの完了、進行、確定)を、ADVとNVLの両方のモードにわたって購読します。
リスナーはペイロードを持ちません。新しい状態を読み取るにはgetCurrentDialogを呼び出してください。
gameState: GameState- GameStateを参照listener: () => void- 変化のたびに呼び出される- 戻り値は
LiveGameEventToken- LiveGameEventTokenを参照
const token = DevTools.onDialogStateChange(gameState, () => {
const dialog = DevTools.getCurrentDialog(gameState);
highlightRow(dialog?.actionId ?? null);
});
return function cleanup() {
token.cancel();
};DevToolsCurrentDialog
現在プレイヤーに提示されているダイアログ行のスナップショットです。
type DevToolsCurrentDialog = {
/** Id of the say action that produced the line (static id when assigned). */
actionId: string | null;
/** True once the line finished displaying and awaits advance. */
ended: boolean;
mode: "adv" | "nvl";
};永続化
getNamespaceName
Persistentがストア内で登録される際の名前で、Storable.getNamespaceが受け取るのと同じ文字列です。
persistent: Persistent<any>- Persistentを参照- 戻り値は
string
const name = DevTools.getNamespaceName(playerPersistent);DynamicPersistent
DynamicPersistentクラスそのもので、これ以外にパッケージからエクスポートされることはありません。これはPersistentの内部バリアントで、名前空間にプレフィックスが付き、実行時に作成されます。シーンのローカル変数はその一例です。
const Dynamic = DevTools.DynamicPersistent;これは意図的に内部向けとされているものです。ここで参照できるのは、ホストがエンジン自身の名前空間をミラーする必要がある場合のためであり、ストーリーのコードのためではありません。