Skip to content

ライフサイクルと検証

完全なライフサイクル

各 Block の実行順序

  1. 前の block のクリーンアップ前の block の handler が返したクリーンアップ関数が遷移時に実行されます(next() が呼ばれた時点)
  2. onValidateNextBlock — 実行前の検証
  3. onBeforeBlock — 前処理(続行するには resolve() を呼び出す必要あり)
  4. タイプ handler(Tier 2、次に Tier 1)

Scene イベント

ts
engine.onSceneEnter(({ scene, context }) => {
  // Called when handle.start() is executed
});

engine.onSceneExit(({ scene, context }) => {
  // Called when the scene ends (naturally or via cancel)
});
csharp
engine.OnSceneEnter(args => {
    // Called when handle.Start() is executed
});

engine.OnSceneExit(args => {
    // Called when the scene ends (naturally or via cancel)
});
cpp
engine.onSceneEnter([](auto* scene, auto*) {
    // Called when handle->start() is executed
});

engine.onSceneExit([](auto* scene, auto*) {
    // Called when the scene ends (naturally or via cancel)
});
gdscript
engine.on_scene_enter(func(args):
    pass # Called when handle.start() is executed
)

engine.on_scene_exit(func(args):
    pass # Called when the scene ends (naturally or via cancel)
)

onValidateNextBlock

各 block 遷移をインターセプトして検証します。handler は次の block (nextContext) と前の block (fromContext) の解決されたキャラクターを受け取ります:

ts
engine.onValidateNextBlock(({ nextBlock, fromBlock, nextContext, fromContext }) => {
  return { valid: true };
});

engine.onInvalidateBlock(({ scene, reason }) => {
  console.error('Invalid block:', reason);
  scene.cancel();
});
csharp
engine.OnValidateNextBlock(args => {
    // args.NextContext.Character, args.FromContext?.Character
    return new ValidationResult { Valid = true };
});

engine.OnInvalidateBlock(args => {
    Console.Error.WriteLine($"Invalid block: {args.Reason}");
    args.Scene.Cancel();
});
cpp
engine.onValidateNextBlock([](const auto& args) {
    // args.nextContext.character, args.fromContext.character (check args.hasFromContext)
    return ValidationResult{true};
});

engine.onInvalidateBlock([](auto* scene, const auto& reason) {
    std::cerr << "Invalid block: " << reason << "\n";
    scene->cancel();
});
gdscript
engine.on_validate_next_block(func(args):
    # args["nextContext"]["character"], args["fromContext"]["character"]
    return {"valid": true}
)

engine.on_invalidate_block(func(args):
    printerr("Invalid block: %s" % args["reason"])
    args["scene"].cancel()
)

Character Gating

nextContext.character を使用して、ゲームの状態に基づいて block の実行を制御します:

ts
// Block if the character is stunned
engine.onValidateNextBlock(({ nextContext }) => {
  const { character } = nextContext;
  if (!character) return { valid: false, reason: 'no_character' };
  if (game.characterHasStatus(character, 'stunned'))
    return { valid: false, reason: 'character_stunned' };
  return { valid: true };
});
csharp
engine.OnValidateNextBlock(args => {
    var character = args.NextContext.Character;
    if (character == null)
        return ValidationResult.Fail("no_character");
    if (game.CharacterHasStatus(character, "stunned"))
        return ValidationResult.Fail("character_stunned");
    return ValidationResult.Ok();
});
cpp
engine.onValidateNextBlock([&game](const auto& args) {
    auto* character = args.nextContext.character;
    if (!character) return ValidationResult{false, "no_character"};
    if (game.characterHasStatus(character, "stunned"))
        return ValidationResult{false, "character_stunned"};
    return ValidationResult{true};
});
gdscript
engine.on_validate_next_block(func(args):
    var character = args["nextContext"]["character"]
    if character == null:
        return {"valid": false, "reason": "no_character"}
    if game.character_has_status(character, "stunned"):
        return {"valid": false, "reason": "character_stunned"}
    return {"valid": true}
)

fromContext.character を使用してキャラクター間の遷移を検証できます(例:関係チェック、クールダウン)。fromContext はシーンの最初の block では null です。

onBeforeBlock

各 block の前に呼び出されます。続行するには必ず resolve() を呼び出す必要があります:

ts
engine.onBeforeBlock(({ block, resolve }) => {
  const delay = block.nativeProperties?.delay;
  if (delay) {
    setTimeout(resolve, delay * 1000);
  } else {
    resolve();
  }
});
csharp
engine.OnBeforeBlock(args => {
    var delay = args.Block.NativeProperties?.Delay;
    if (delay.HasValue)
    {
        // use your engine's delay system (coroutine, DOTween, Invoke, etc.)
        DelayThenCall((float)delay.Value, args.Resolve);
    }
    else
    {
        args.Resolve();
    }
});
cpp
engine.onBeforeBlock([](const auto& args) {
    auto delay = args.block->nativeProperties
        ? args.block->nativeProperties->delay : std::nullopt;
    if (delay.has_value()) {
        // use your engine's timer system (FTimerManager, SDL_AddTimer, etc.)
        scheduleDelay(delay.value(), [&args]() { args.resolve(); });
    } else {
        args.resolve();
    }
});
gdscript
engine.on_before_block(func(args):
    var delay = args["block"].get("nativeProperties", {}).get("delay", 0)
    if delay > 0:
        await get_tree().create_timer(delay).timeout
    args["resolve"].call()
)

クリーンアップ関数

handler はクリーンアップ関数を返すことができ、block から離れる際に呼び出されます:

ts
engine.onDialog(({ block, next }) => {
  const element = showDialogUI(block);

  // next() is called later — by player input, timer, etc.

  return () => {
    element.remove(); // called when the engine moves to the next block
  };
});
csharp
engine.OnDialog(args => {
    var element = ShowDialogUI(args.Block);

    // next() is called later — by player input, timer, etc.

    return () => element.SetActive(false);
});
cpp
engine.onDialog([](auto*, auto* block, auto* ctx, auto next) -> CleanupFn {
    auto* element = showDialogUI(block);

    // next() is called later — by player input, timer, etc.

    return [element]() { element->remove(); };
});
gdscript
engine.on_dialog(func(args):
    var element = show_dialog_ui(args["block"])

    # next is called later — by player input, timer, etc.

    return func(): element.queue_free()
)

エラー境界

すべての handler 呼び出しは try/catch でラップされています。handler がスローした場合:

  • エラーはサイレントです — ログ出力や再スローは行われません。scene が予期せず終了した場合、handler を確認してください。
  • メイントラックの場合:scene はクリーンに終了します
  • async トラックの場合:影響を受けたトラックのみが終了し、他のトラックとメインフローは継続します

これはクロス言語互換です(TS、C#、C++、GDScript の try/catch)。

cancel()

scene.cancel() を呼び出すと、以下のシーケンスが実行されます:

  1. すべての async トラック がキャンセルされます
  2. 現在の block のクリーンアップ関数が実行されます
  3. onSceneExit handler が呼び出されます
  4. scene が完了としてマークされます
ts
engine.onInvalidateBlock(({ scene, reason }) => {
  console.error('Validation failed:', reason);
  scene.cancel();
});
csharp
engine.OnInvalidateBlock(args => {
    Console.Error.WriteLine($"Validation failed: {args.Reason}");
    args.Scene.Cancel();
});
cpp
engine.onInvalidateBlock([](auto* scene, const auto& reason) {
    std::cerr << "Validation failed: " << reason << "\n";
    scene->cancel();
});
gdscript
engine.on_invalidate_block(func(args):
    printerr("Validation failed: %s" % args["reason"])
    args["scene"].cancel()
)

NativeProperties

engine が block をディスパッチする方法を制御する実行プロパティ:

フィールド説明
isAsyncboolean?並列 async トラックで実行
delaynumber?実行前のディレイ(onBeforeBlock で消費)
timeoutnumber?実行タイムアウト
portPerCharacterboolean?metadata 内のキャラクターごとに出力ポートを作成
skipIfMissingActorboolean?参照されたアクターが不在の場合、block をスキップ
debugboolean?エディタ用デバッグフラグ
waitForBlocksstring[]?この block が進行する前に訪問済みでなければならない block の UUID
waitInputboolean?明示的なプレイヤー入力制御用のパッシブフラグ

Visual Reference

Block Execution Flow

Character Gating Flow