「タグ付きライト パズル」チュートリアルのこのステップを完了すると、プレイヤーが操作するボタンに応じてライトのグループのオン/オフを切り替える方法を習得できます。
ライトのオン/オフを切り替える
プレイヤーがボタンを操作したときにオン/オフを切り替えるライトのグループとボタンのマッピングを作成する必要があります。そのためには、各ボタンを Lights
配列内でのライトのインデックスにマッピングします。
このサンプルではボタンとライトに次のマッピングを使用します。
- ボタン 1 をインデックス 0 のライトとインデックス 3 のライトにマッピング
- ボタン 2 をインデックス 0 のライト、インデックス 1 のライト、インデックス 2 のライトにマッピング
- ボタン 3 をインデックス 0 のライトとインデックス 1 のライトにマッピング
- ボタン 4 をインデックス 1 のライトにマッピング
このマッピングは ButtonsToLights
という配列で表現できます。ここで、ButtonsToLights
の各要素はライトのインデックスを保持している別の配列です。ButtonsToLights
の型は [][]int
であり、ButtonsToLights
が整数配列の配列であることを指定しています。
インデックス | 0 | 1 | 2 | 3 |
要素 | array{0, 3} | array{0, 1, 2} | array{0, 1} | array{1} |
ライトのオン/オフを切り替えるには、次の手順に従います。
ButtonsToLights
という名前の整数配列の配列を作成し、前の表で示しているボタンからライトへのインデックス マッピングで初期化します。ButtonsToLights : [][]int = array{array{0, 3}, array{0, 1, 2}, array{0, 1}, array{1}}
配列のインデックスは 0 で始まり、要素数 -1 まであります。したがって、
ButtonsToLights
の最初の要素はインデックス 0 にあり、最後の要素はインデックス 3 にあります。ToggleLights()
という名前の新しい メソッド をtagged_lights_puzzle
のクラス定義に追加します。このメソッドは、整数配列として (配列ButtonsToLights
の要素に一致するように) 関数に渡されたインデックスに基づいてLights
配列内のライトのオン/オフを切り替え、LightsState
内で同じインデックスにある要素を更新します。ToggleLights()
メソッドにパラメータLightIndices : []int
を追加し、for
式を使用して各インデックスを出力ログに出力します。ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices do: Logger.Print("Toggling light at {LightIndex}")
OnBegin()
内でToggleLights()
を呼び出して、メソッドが期待どおりに動作することをテストします。ButtonsToLights
の 1 つ目の要素をテストに使用します。配列に対するインデックス指定は 失敗する可能性がある式 であるため、その配列へのアクセスは 失敗コンテキスト から行う必要があります。この例では、失敗コンテキストにif
式を使用しています。
OnBegin<override>()<suspends> : void = SetupPuzzleLights() # ButtonsToLights の 1 つ目の要素を使用して ToggleLights メソッドをテストします。 if (LightIndices : []int = ButtonsToLights[0]): ToggleLights(LightIndices)
LightIndex
の準備が整ったので、そのインデックスでLights
配列とLightsState
配列にアクセスし、カスタマイズ可能なライトの仕掛けの参照とその現在のステートを取得します。ライトのオンとオフを切り替えるとは、ライトがオンの場合は消灯し、ライトがオフの場合は点灯するということです。print 文を更新して、ライトの新しいステートが何になるかを出力します。ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices Light := Lights[LightIndex] IsLightOn := LightsState[LightIndex] do: Logger.Print("Turning light at {LightIndex} {if (IsLightOn?) then "Off" else "On"}")
- ここで、インゲームと
LightsState
配列の両方で、カスタマイズ可能なライトの仕掛けのステートを更新します。IsLightOn
がfalse
であれば、そのライトに対してTurnOn()
を呼び出し、IsLightOn
がtrue
であれば、そのライトに対してTurnOff()
を呼び出します。コード ブロック内の最後の式は結果であるため、最後の式としてfalse
またはtrue
を設定することで、その値がNewLightState
に格納されます。ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices Light := Lights[LightIndex] IsLightOn := LightsState[LightIndex] do: Logger.Print("Turning light at {LightIndex} {if (IsLightOn?) then "Off" else "On"}") NewLightState := if (IsLightOn?): Light.TurnOff() false else: Light.TurnOn() true
LightIndex
にあるLighsState
の要素を、NewLightState
の値で更新します。配列のインデックス指定は失敗する可能性がある式であるため、LightsState
を設定する部分を失敗コンテキストでラップする必要があります。この例では、失敗コンテキストはif
式です。ステートが更新されたことを出力ログに出力します。ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices IsLightOn := LightsState[LightIndex] Light := Lights[LightIndex] do: Logger.Print("Turning light at {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}")
失敗コンテキストを使用したときに、出力ログを生成することをお勧めします。 失敗コンテキストで式が失敗すると、失敗コンテキストでの変更はすべてロールバックされ、何も起きなかったかのようになります。 出力ログに出力すると、出力ログにテキストが表示されるかを確かめることで、変更が行われたことをダブルチェックできます。これにより、変更が正常に行われたことをインゲームのステートだけに頼らずに検証できます。
ボタンの押下をライトの切り替えにつなぐ
ライトの切り替え方法を定義したので、次のステップは、ボタンを押す処理とライトのオンとオフの切り替え処理をつなげることです。
これを行うには、ボタンの InteractedWithEvent
をサブスクライブします。イベント サブスクリプションの詳細については、「仕掛けのインタラクションをコード化する」を参照してください。
InteractedWithEvent
では、イベント ハンドラに 1 つのパラメータ InPlayer : agent
があり、戻り値の型が void
であることが想定されています。またそのイベント ハンドラは、イベントを送出したボタンがつながれているライトはどれであるかを把握している必要があり、ToggleLights()
メソッドを呼び出すことができるように、Verse の仕掛け tagged_lights_puzzle
への参照を保持している必要もあります。
これらすべての情報をカスタム仕様のオブジェクトにまとめることができます。これはインデックスとサブスクライブする関数を含む新しいクラスを作成することで実現します。 このようにして、この新しいクラスで表されるとおり、各ボタンには個別ステートとイベント ハンドラが備わります。
イベント ハンドラの処理にカスタム仕様のオブジェクトを作成するには次の手順に従います。
button_event_handler
という名前の新しいクラスを作成します。そのクラス定義で次の宣言を行います。Indices
という名前の整数配列。- 使用している Verse の仕掛けへの参照である
PuzzleDevice
という名前のtagged_lights_puzzle
フィールド。これを使用してToggleLights()
メソッドを呼び出すことができます。 - パラメータ
InPlayer : agent
があり戻り値の型がvoid
である、OnButtonPressed()
という名前のメソッド。これは、PuzzleDevice
に対してToggleLights()
を呼び出します。button_event_handler := class(): # このボタンで制御するライトにアクセスするために使用される位置。 Indices : []int # この button_event_handler を作成した tagged_lights_puzzle。これに対して関数を呼び出すことができます。 PuzzleDevice : tagged_lights_puzzle OnButtonPressed(InPlayer : agent) : void = # このボタンで制御している位置にあるライトのオン/オフを切り替えるように PuzzleDevice に指示します。 PuzzleDevice.ToggleLights(Indices)
- プレイヤーが操作できるボタンを参照するための、編集可能な
button_device
配列フィールドをtagged_lights_puzzle
クラス内に作成します。@editable Buttons : []button_device = array{}
- 次に、
tagged_lights_puzzle
クラスのOnBegin()
を更新して、各ボタン用のbutton_event_handler
インスタンスを作成します。button_event_handler では次の情報が必要です。- ボタンと関連付けられているライトのインデックス。このインデックスは、
for
式で与えられるButtonIndex
を使用して、ButtonsToLights
配列から取得できます。すべてのButtons
をイテレートするために使用するfor
式のフィルタ条件として、これを取得できます。これは、無効なデータをインデックス指定しないようコードを保護するためのセーフガードとしても機能します。インデックス指定が失敗した場合、失敗したイテレーションはスキップされるため、プログラムは有効な状態のままです (ButtonsToLights
の数とButtons
の数を一致させていない場合に、失敗が発生する可能性があります)。 tagged_lights_puzzle
のインスタンスである Verse の仕掛けへの参照。クラス定義内から現在のオブジェクトへの参照を取得するには、Self
を使用できます。- このデータを使用した
button_event_handler
オブジェクトの作成は、次のようになります。OnBegin<override>()<suspends> : void = SetupPuzzleLights() for: ButtonIndex -> Button : Buttons LightIndices := ButtonsToLights[ButtonIndex] do: button_event_handler{Indices := LightIndices, PuzzleDevice := Self}
- ボタンと関連付けられているライトのインデックス。このインデックスは、
- 新しく作成したハンドラの
OnButtonPressed()
関数を使用して、ボタンの仕掛けのInteractedWithEvent
にサブスクライブできます。OnButtonPressed()
関数が呼び出されると、ライトのインデックス、およびプレイヤーが操作するボタンに関連付けられているtagged_lights_puzzle
仕掛けへの参照にアクセスできます。OnBegin<override>()<suspends> : void = SetupPuzzleLights() for: ButtonIndex -> Button : Buttons LightIndices := ButtonsToLights[ButtonIndex] do: Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed)
- Visual Studio Code にスクリプトを保存します。
- UEFN ツールバーの [Build Verse Scripts (Verse スクリプトをビルド)] をクリックし、レベルにある Verse の仕掛けを新しいコードで更新します。
- アウトライナー で tagged_lights_puzzle 仕掛けを選択すると、[Details] パネルが開きます。
- [Details] パネルで、4 個の要素を
Buttons
配列に追加し、それぞれに異なるボタンを割り当てます。他のプロパティにはスクリプトによって値が入力されるため、それらを変更する必要はありません。 - UEFN ツールバーで [Play (プレイ)] をクリックして、レベルのプレイテストを行います。
レベルをプレイテストする際に、ボタンを操作できる必要があり、各ボタンでは ButtonsLightsIndices
の設定に基づいて異なるセットのライトが切り替わる必要があります。GetCreativeObjectsWithTag()
では特定の順序が決まっていないため、スクリプト内での順序に基づいて切り替えられるライトは、レベルで表示される順序と一致しないことがあることに注意してください。

次のステップ
このチュートリアルの 次のステップ では、プレイヤーがパズルを解いたタイミングを検出して、アイテムをスポーンし、そのパズルをさらに操作できないようにする方法について学びます。