「タグ付きライト パズル」チュートリアルのこのステップを完了すると、プレイヤーがパズルを解いたタイミングを検出して、アイテムをスポーンし、そのパズルをさらに操作できないようにする方法を習得できます。
解かれたパズルを検出する
このセクションでは、ライトの適切な組み合わせを見つけることで、プレイヤーがパズルを解いたタイミングを検出する方法を示します。 パズルが解かれたとき、アイテムをスポーンできるように、アイテム スポナーの仕掛けを有効にする必要があります。
プレイヤーがパズルを解いたタイミングを検出するには次のステップに従います。
- アイテム スポナーの仕掛けを表すための、
ItemSpawner
という名前の編集可能なitem_spawner_device
フィールドをtagged_lights_puzzle
クラスに追加します。@editable ItemSpawner:item_spawner_device = item_spawner_device{}
- 次に、パズルを解くにはライトがどのステートになっている必要があるかを定義します。ライトのステートの表現は
logic
配列であるため、容易に比較できるように、解かれたライトのステートもlogic
配列にします。SolvedLightsState
という名前の編集可能なlogic
配列フィールドをtagged_lights_puzzle
クラス内に作成します。この例では、プレイヤーはパズルを解くにはすべてのライトを点灯させる必要があるため、各要素を値true
で初期化して、ライトが点灯していることを表します。@editable SolvedLightsState : []logic = array{true, true, true, true}
- Visual Studio Code でファイルを保存します。
- UEFN ツールバーの [Build Verse Scripts (Verse スクリプトをビルド)] をクリックし、レベルにある Verse の仕掛けを更新します。
- アウトライナー で tagged_lights_puzzle を選択すると、[Details] パネルが開きます。
- [Details] パネルで、Item Spawner プロパティをレベルにあるアイテム スポナーの仕掛けに設定します。
- 次に、現在の
LightsState
がSolvedLightsState
と一致しているかどうかをチェックするコードを作成します。IsPuzzleSolved()
という名前の新しいメソッドをtagged_lights_puzzle
クラスに追加します。このメソッドはパズルが解かれていれば成功し、解かれていなければ失敗し、そのチェックの結果を呼び出し元に知らせます。つまりこのメソッドはdecides
指定子で失敗可能とマークする必要があります。このメソッドにはdecides
指定子が付いているため、transacts
指定子も付いている必要があります。そのため、このメソッドで実行されるアクションは、メソッド内のどこかで失敗が発生した場合に (そのアクションは実行されなかったかのように) ロールバックできます。IsPuzzleSolved()<decides><transacts> : void = Logger.Print("Checking if puzzle is solved")
Verse では成功/失敗を使用して意思決定を行います。Verse で失敗を使用する方法の詳細については、「失敗」を参照してください。
- パズルが解かれたことを、いつ仕掛けでチェックすればよいでしょうか。最適なタイミングは
LightsState
配列が更新されたときです。これはToggleLights()
メソッドの内部で発生します。for
式でLightsState
配列の更新が終わったら、IsPuzzleSolved[]
メソッドを呼び出してパズルが解かれているかどうかを判断し、解かれていればアイテムをスポーンします (ItemSpawner.Enable()を呼び出す)。
IsPuzzleSolved[]は失敗する可能性がある式である (そのため、このメソッドの呼び出し時に、
()ではなく
[]を使用する) ため、失敗コンテキストで呼び出す必要があります。この例では、失敗コンテキストは [
if`](if-in-verse) 式であり、パズルが解かれているかどうかという条件に応じて式を実行できます。ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices IsLightOn := LightsState[LightIndex] Light := Lights[LightIndex] do: Logger.Print("Turning light at index {LightIndex} {if (IsLightOn?) then "Off" else "On"}") NewLightState := if (IsLightOn?): Light.TurnOff() false else: Light.TurnOn() true if (set LightsState[LightIndex] = NewLightState): Logger.Print("Updated the state for light at {LightIndex}") if (IsPuzzleSolved[]): Logger.Print("Puzzle solved!") ItemSpawner.Enable()
- 次に、パズルが解かれているかどうかを検出する
IsPuzzleSolved()
メソッドを作成します。IsPuzzleSolved()
は失敗コンテキストであるため、別の失敗コンテキスト (if
式など) を使用することなく、失敗する可能性がある式をメソッド本体内で使用できます。この場合、すべてのライトのステートが、解決したパズルのステートと同じであるかをチェックする必要があります。2 つのlogic
値が同じであるかどうかをテストするには、失敗する可能性がある式である等号演算子=
を使用できます。チェックした 2 つの値が初めて同じでなかったときにこの式は失敗するため、このメソッドは失敗し、呼び出し元のコンテキスト (この場合はToggleLights()
メソッド内のif
式) に戻ります。IsPuzzleSolved()<decides><transacts> : void = Logger.Print(“Checking if puzzle is solved”) for: LightIndex -> IsLightOn : LightsState IsLightOnInSolution := SolvedLightsState[LightIndex] do: IsLightOn = IsLightOnInSolution
- Visual Studio Code で変更内容を保存します。
- UEFN ツールバーの [Build Verse Scripts (Verse スクリプトをビルド)] をクリックして、作成したコードをコンパイルします。
- UEFN ツールバーの [Play (プレイ)] をクリックして、レベルをプレイテストします。
この例で示した設定でレベルをプレイテストするとき、すべてのボタンを 1 回操作するとパズルが解けて、アイテムがスポーンされます。

パズルが解かれたときにボタンのインタラクションを除去する
プレイヤーがパズルを完了した後、ボタンを操作できない、つまりライトのオンとオフを切り替えられないようにします。 このセクションで示すのはイベントのサブスクライブを解除する方法で、プレイヤーがボタンを操作したときにイベント ハンドラが呼び出されなくなります。
仕掛けのイベントに対して Subscribe()
を呼び出すと、その関数呼び出しに cancelable
結果があります。Cancel()
を cancelable
変数に対して呼び出すと、そのイベントを処理する関数のサブスクライブが解除されるため、その関数はそのイベントがディスパッチされたときに呼び出されなくなります。
パズルが解かれたときに、ボタンのインタラクションを除去するために次のステップに従います。
- 各ボタンのイベントをサブスクライブした結果を格納するための、
ButtonSubscriptions
という名前のcancelable
配列変数フィールドをtagged_lights_puzzle
クラスに追加し、空の配列でフィールドを初期化します。var ButtonSubscriptions : []cancelable = array{}
- これは
for
式の最後の文であるため、成功したすべてのイテレーションの戻り値がその式の戻り値の型の配列に集められます。前に説明したとおり、イベントSubscribe
呼び出しの戻り値の型はcancelable
です。これはcancelable
の配列 ([]cancellable
) になるfor
の ToggleLights を参照します。これはButtonsSubscription
型と一致しているため、for
式の結果をButtonsSubscription
配列に割り当てることができます。このコードをOnBegin
関数内のSetupPuzzleLights
の後に追加します。OnBegin<override>()<suspends> : void = SetupPuzzleLights() set ButtonSubscriptions = for: ButtonIndex -> Button : Buttons LightIndices := ButtonsToLights[ButtonIndex] do: Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed)
- 最後に、プレイヤーがこれ以上
LightsState
を変更できないように、忘れずにボタンを無効にします。これを実行するには、cancelable
サブスクリプションの呼び出しで、各ボタンのInteractedWithEvent
ハンドラのサブスクライブを解除します。これらのButtonSubscriptions
はイベント ハンドラがOnBegin
で作成されたときに保存されました。残っているのは、この配列でイテレートして、各ButtonSubcription
に対してキャンセルを呼び出すことだけです。ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices IsLightOn := LightsState[LightIndex] Light := Lights[LightIndex] do: Logger.Print("Turning light at index {LightIndex} {if (IsLightOn?) then "Off" else "On"}") NewLightState := if (IsLightOn?): Light.TurnOff() false else: Light.TurnOn() true if (set LightsState[LightIndex] = NewLightState): Logger.Print("Updated the state for light at {LightIndex}") if (IsPuzzleSolved[]): Logger.Print("Puzzle solved!") ItemSpawner.Enable() for (ButtonSubscription : ButtonSubscriptions): ButtonSubscription.Cancel()
- Visual Studio Code で変更内容を保存します。
- UEFN ツールバーの [Build Verse Scripts (Verse スクリプトをビルド)] をクリックして、作成したコードをコンパイルします。
- UEFN ツールバーの [Play (プレイ)] をクリックして、レベルをプレイテストします。
このデフォルト プロパティでは、1 回すべてのボタンを操作すると、パズルが解けて、アイテムがスポーンされ、それからボタンの操作が無効になります。
次のステップ
このチュートリアルの 最終ステップ では、このチュートリアルの完全なスクリプトを示し、この例をさらに変更するアイデアを紹介します。