[Verse]切り替えの仕掛けを使った扉の開閉システム
はじめに
前回、扉を開く仕掛けをVerseで作成しました。
今回は、切り替えの仕掛けを使って開閉できる扉にしました。
以降、本記事はバージョンv29.40 で執筆しています。
機能概要
切り替えの仕掛けと連動して、対応する扉の開閉を行います。切り替えの仕掛けと扉のペアをUEFN上で追加することで、複数の扉の開閉が可能です。
前提:
扉は建築小道具で、ゲーム開始時は閉じた状態です。
切り替えの仕掛けの状態は全体共有とし、持続データを考慮しません。
入力:
切り替えの仕掛け。トリガー
処理:
- 扉の開閉:切り替えの仕掛けがオンになると扉を開く。オフになると扉を閉じる
- 扉のリセット:トリガーが実行されると全ての扉を閉じる
コード & UEFN上の設定
コード
全文
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/Devices/CreativeAnimation }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/Devices/CreativeAnimation/InterpolationTypes }
opening_closing_door := class<concrete>:
@editable
MainId:type{ _X:int where 0 <= _X, _X <= 5 } = 0 # 部屋番号を設定。最小最大は任意
@editable
SubId:?int = false # 同一部屋内で複数の扉がある場合に設定。扉の番号
@editable
DoorProp:creative_prop = creative_prop{} # 扉のプロップ
@editable
InputSwitch:switch_device = switch_device{} # 入力用スイッチ。オンオフで開閉を制御する
var DoorId:string = array{} # 扉のID。MainIdとSubIdを組み合わせて設定。一意
var InitTransform:transform = transform{} # 扉の初期位置
opening_closing_door_manager := class(creative_device):
@editable
RoomExits:[]opening_closing_door = array{} # 各部屋の出口用の扉の配列
@editable
InputResetTrigger:trigger_device = trigger_device{} # 入力用トリガー。全ての扉を閉じる
RotAngle:float = 90.0 # 開く角度
OpeningTime:float = 3.0 # 開くのにかける時間
OnBegin<override>()<suspends>:void=
InputResetTrigger.TriggeredEvent.Subscribe(OnResetTriggered)
for (RoomExit:RoomExits):
set RoomExit.DoorId =
if (SubId := RoomExit.SubId?) then "{RoomExit.MainId}_{SubId}"
else "{RoomExit.MainId}"
set RoomExit.InitTransform = RoomExit.DoorProp.GetTransform()
spawn{ OpenCloseLoop(RoomExit) }
OnResetTriggered(AgentOpt:?agent):void=
if(Agent := AgentOpt?):
spawn{ Reset(Agent) }
# 扉の開閉
# 対象の扉について、Switchのオンオフで開閉を制御する
OpenCloseLoop(Door:opening_closing_door)<suspends>:void=
loop:
# Switchがオンになると扉を開く
Door.InputSwitch.TurnedOnEvent.Await()
if (AController := Door.DoorProp.GetAnimationController[]):
OpeningKeyFrames:[]keyframe_delta = array:
MakeKeyFrameDelta(
MakeRotationFromYawPitchRollDegrees(RotAngle, 0.0, 0.0),
OpeningTime)
AController.SetAnimation(
OpeningKeyFrames, ?Mode := animation_mode.OneShot)
AController.Play()
# Switchがオフになると、瞬時に扉を初期位置に戻す(閉じる)
Door.InputSwitch.TurnedOffEvent.Await()
Door.DoorProp.MoveTo(Door.InitTransform, 0.1)
else:
Print("OpenCloseLoop() AController Error. Door={Door.DoorId}")
# 扉のリセット。初期位置に戻す(閉じる)
Reset(Agent:agent)<suspends>:void=
# 開いている扉を閉じる
ResetDoors : []string = for:
Door:RoomExits
Door.InputSwitch.GetCurrentState[]
do:
Door.InputSwitch.Enable()
Door.InputSwitch.TurnOff(Agent)
Door.DoorId
var DoorIds : string = "["
for (Index -> DoorId : ResetDoors):
if (Index < ResetDoors.Length - 1) then set DoorIds += DoorId + ", "
else set DoorIds += DoorId
set DoorIds += + "]"
Print("Reset() ResetDoors={ResetDoors.Length} {DoorIds}")
# 扉を開くアニメーションのキーフレームを作成
MakeKeyFrameDelta(DeltaRotation : rotation, OverTime:float):keyframe_delta=
keyframe_delta:
DeltaLocation := vector3 { X:= 0.0, Y:= 0.0, Z:= 0.0 }
DeltaRotation := DeltaRotation
DeltaScale := vector3 { X:=1.0, Y:=1.0, Z:=1.0 }
Time := OverTime
Interpolation := EaseOut
UEFN上の設定
opening closing door manager を配置して、RoomExits, InputResetTrigger を設定します。
切り替えの仕掛けは、インタラクトしてオンしても、ほかのデバイスからオンにしてもよいです。動画では各デバイスのイベント時にオンにしています。
解説
OnBegin<override>()<suspends>:void=
InputResetTrigger.TriggeredEvent.Subscribe(OnResetTriggered)
for (RoomExit:RoomExits):
set RoomExit.DoorId =
if (SubId := RoomExit.SubId?) then "{RoomExit.MainId}_{SubId}"
else "{RoomExit.MainId}"
set RoomExit.InitTransform = RoomExit.DoorProp.GetTransform()
spawn{ OpenCloseLoop(RoomExit) }
InputResetTrigger.TriggeredEvent.Subscribe(OnResetTriggered)
InputResetTriggerがトリガーされた時にOnResetTriggeredを実行します。
for (RoomExit:RoomExits):
RoomExits配列の各要素に対して次の処理を行います。
DoorId に MainIdとSubIdから成る文字列をセットします。SubIdがfalseの場合は、MainIdのみ文字列にします。こちらは扉のリセット処理の際、ログ出力に使用しています。
InitTransform に DoorPropの初期位置(transform)をセットします。
開閉処理(OpenCloseLoop)を実行します。開閉処理は、非同期関数なのでspawnしています。
OnResetTriggered(AgentOpt:?agent):void=
if(Agent := AgentOpt?):
spawn{ Reset(Agent) }
Reset(Agent:agent)<suspends>:void=
# 開いている扉を閉じる
ResetDoors : []string = for:
Door:RoomExits
Door.InputSwitch.GetCurrentState[]
do:
Door.InputSwitch.Enable()
Door.InputSwitch.TurnOff(Agent)
Door.DoorId
var DoorIds : string = "["
for (Index -> DoorId : ResetDoors):
if (Index < ResetDoors.Length - 1) then set DoorIds += DoorId + ", "
else set DoorIds += DoorId
set DoorIds += + "]"
Print("Reset() ResetDoors={ResetDoors.Length} {DoorIds}")
OnResetTriggeredは、トリガーされたときに呼び出されるので、引数は?agent型です。agent情報がある場合に、リセット処理(Reset)を実行します。
for:
Door:RoomExits
Door.InputSwitch.GetCurrentState[]
RoomExits配列の各要素に対して、InputSwitchがオンの場合に次の処理を行います。
InputSwitchを有効化し、InputSwitchをオフにします。
ResetDoors に DoorId をセットします。
(補足)InputSwitchは、ほかのデバイスからの入力を検知して扉を閉じるのがこのVerseデバイスでの役割としています。ただし、ResetTriggerがトリガーされたときは、特別にオフにする操作をしています。扉を閉じることとInputSwitchをオフにすることはペアなので、UEFN上で各InputTriggerの「オフにする」にResetTriggerのトリガーイベントを紐づけるよりも、ここで一緒に行ったほうがよさそうです。
Print("Reset() ResetDoors={ResetDoors.Length} {DoorIds}")
閉じた扉を確認するため、ResetDoors の数と DoorId をログ出力します。
(例)
0件の場合、「Reset() ResetDoors=0 []」
DoorId="1" と DoorId="2_1" の2件の場合、「Reset() ResetDoors=2 [1, 2_1]」
OpenCloseLoop(Door:opening_closing_door)<suspends>:void=
loop:
# Switchがオンになると扉を開く
Door.InputSwitch.TurnedOnEvent.Await()
if (AController := Door.DoorProp.GetAnimationController[]):
OpeningKeyFrames:[]keyframe_delta = array:
MakeKeyFrameDelta(
MakeRotationFromYawPitchRollDegrees(RotAngle, 0.0, 0.0),
OpeningTime)
AController.SetAnimation(
OpeningKeyFrames, ?Mode := animation_mode.OneShot)
AController.Play()
# Switchがオフになると、瞬時に扉を初期位置に戻す(閉じる)
Door.InputSwitch.TurnedOffEvent.Await()
Door.DoorProp.MoveTo(Door.InitTransform, 0.1)
else:
Print("OpenCloseLoop() AController Error. Door={Door.DoorId}")
loop:
何度でも繰り返し開閉できるようにloop式に処理を入れます。
if (AController := Door.DoorProp.GetAnimationController[]):
else:
Print("OpenCloseLoop() AController Error. Door={Door.DoorId}")
InputSwitchがオンになると、DoorPropからアニメーションコントローラーを取得し、扉を開きます。取得失敗した場合、 DoorId をログに出力します。
扉を開く処理については、以下の記事をご参照ください。
Door.DoorProp.MoveTo(Door.InitTransform, 0.1)
InputSwitchがオンになったあと、InputSwitchがオフになると0.1秒かけて扉を初期位置に戻します(扉を閉じます)。
アニメーションコントローラーではなくMoveToを使用しているのは、初期位置に戻せればよかったことと、扉を閉じる動作は素早く行いたかったのでアニメーションコントローラーである必要がなかったことが理由です。
以上、完成です。
お読みいただきありがとうございました!
Discussion