🐥
[UEFN][Verse]UIをマルチプレイヤー対応する
はじめに
UEFNでUIを作成しマルチプレイヤーに対応させるには、通常のプラットフォームとは少し違った考え方が必要になります
今回はUIをマルチプレイヤーに対応させる方法について紹介します
こちらの島は公開されているので動作を確認したい方は遊んでみてください
0596-9617-1560
コードだけ必要な方は完成品まで飛んでください
参考にしたコード
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
height_display := class(creative_device):
@editable
GroundLevelOffset: float = 0.0
@editable
Text: string = "Height: "
@editable
TextPosition: text_position = text_position.UpperCenter
var DisplayMap: [player]?height_widget = map{}
OnBegin<override>()<suspends>:void=
Sleep(0.0)
CheckForPlayers()
CheckForPlayers()<suspends>: void =
loop:
Sleep(1.0)
AllPlayers := GetPlayspace().GetPlayers()
for (Player : AllPlayers):
HeightWidgetInstance := height_widget{VerseDevice := Self, InstancePlayer := Player}
PlayerHeight := GetHeight(Player)
if (not DisplayMap[Player], set DisplayMap[Player] = option{HeightWidgetInstance}):
HeightWidgetInstance.CreateUIForPlayer(PlayerHeight)
if (Agent := agent[Player]):
HeightWidgetInstance.ShowUI(Agent)
GetHeight(Player: player): int =
if (FortCharacter := Player.GetFortCharacter[]):
PlayerLocation := FortCharacter.GetTransform().Translation
PlayerHeight := PlayerLocation.Z + GroundLevelOffset*100.0
if (HeightInt := Floor[PlayerHeight/100.00]):
return HeightInt
return 0
height_widget := class():
var VerseDevice : height_display = height_display{}
InstancePlayer: player
HeightText<localizes>(Text: string, Height: int) : message = "{Text} {Height}"
HeightWidget : button_quiet = button_quiet{}
var UIPerPlayer : [player]?canvas = map{}
var XMinPos: float = 0.4
var XMaxPos: float = 0.6
var YMinPos: float = 0.0
var YMaxPos: float = 0.0
AdjustTextPos(): void =
case (VerseDevice.TextPosition):
text_position.LowerLeft =>
set XMinPos = 0.0
set XMaxPos = 0.2
set YMinPos = 0.7
set YMaxPos = 0.0
text_position.LowerRight =>
set XMinPos = 0.6
set XMaxPos = 0.8
set YMinPos = 0.7
set YMaxPos = 0.0
text_position.BottomCenter =>
set XMinPos = 0.4
set XMaxPos = 0.6
set YMinPos = 0.7
set YMaxPos = 0.0
_ =>
CreateUIForPlayer(Height: int):canvas=
AdjustTextPos()
HeightWidget.SetText(HeightText(VerseDevice.Text, Height))
MyCanvas : canvas = canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := vector2{X := XMinPos, Y := YMinPos}, Maximum := vector2{X := XMaxPos, Y := YMaxPos}}
Offsets := margin{Top := 100.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := vector2{X := 0.5, Y := 0.5}
SizeToContent := false
Widget := HeightWidget
ShowUI(Agent: agent): void =
if (Player := player[Agent], PlayerUI := GetPlayerUI[Player]):
if (MyUI := UIPerPlayer[Player]?):
PlayerUI.RemoveWidget(MyUI)
if {set UIPerPlayer[Player] = false}
else:
NewUI := CreateUIForPlayer(0)
PlayerUI.AddWidget(NewUI, player_ui_slot{InputMode := ui_input_mode.None})
if (set UIPerPlayer[Player] = option{NewUI}) {}
spawn:
UpdateHeight()
UpdateHeight()<suspends>: void =
loop:
Sleep(0.1)
PlayerHeight := VerseDevice.GetHeight(InstancePlayer)
HeightWidget.SetText(HeightText(VerseDevice.Text, PlayerHeight))
text_position := enum:
UpperCenter
BottomCenter
LowerRight
LowerLeft
こちらのコードはPlayerの高さを画面上部に表示するVerseコードになります
このコードそのままでも動作に問題は無いのですが、creative_device
とheight_widget
が相互参照しておりコードとしてはイマイチです
リファクタしながらコードを読み解いていきます
完成品
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
height_display := class(creative_device):
@editable
GroundLevelOffset: float = 0.0
@editable
Text: string = "Height: "
#PlayerをKeyにして、HeightWidgetをValueにしたMap
var DisplayMap: [player]?height_widget = map{}
#Deviceの起動
OnBegin<override>()<suspends>:void=
Sleep(0.0)
CheckForPlayers()
#Player情報を1秒周期でチェックする
CheckForPlayers()<suspends>: void =
loop:
Sleep(1.0)
AllPlayers := GetPlayspace().GetPlayers()
for (Player : AllPlayers):
#各Playerに対して、HeightWidgetを作成する
HeightWidgetInstance := height_widget{InstancePlayer := Player, BaseText := Text, Offset := GroundLevelOffset}
#DisplayMapに該当のPlayerが存在しない場合、HeightWidgetを作成する
if (not DisplayMap[Player], set DisplayMap[Player] = option{HeightWidgetInstance}):
if (Agent := agent[Player]):
HeightWidgetInstance.ShowUI(Agent)
height_widget := class():
InstancePlayer: player
BaseText : string
Offset : float
HeightText<localizes>(Text: string, Height: int) : message = "{Text} {Height}"
HeightWidget : button_quiet = button_quiet{}
#PlayerをKeyにして、CanvasをValueにしたMap
var UIPerPlayer : [player]?canvas = map{}
#PlayerのUIを作成する.つまりCanvasとWidgetを作成する
CreateUIForPlayer(Height: int):canvas=
Print("CreateUIForPlayer{Height}")
HeightWidget.SetText(HeightText(BaseText, Height))
XMinPos: float = 0.4
XMaxPos: float = 0.6
YMinPos: float = 0.0
YMaxPos: float = 0.0
MyCanvas : canvas = canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := vector2{X := XMinPos, Y := YMinPos}, Maximum := vector2{X := XMaxPos, Y := YMaxPos}}
Offsets := margin{Top := 100.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := vector2{X := 0.5, Y := 0.5}
SizeToContent := false
Widget := HeightWidget
#UIの表示
ShowUI(Agent: agent): void =
Print("ShowUI")
#AgentからPlayerUIを取得する
if (Player := player[Agent], PlayerUI := GetPlayerUI[Player]):
if (MyUI := UIPerPlayer[Player]?):
Print("Removing UI")
PlayerUI.RemoveWidget(MyUI)
if {set UIPerPlayer[Player] = false}
else:
Print("Create UI")
NewUI := CreateUIForPlayer(0)
PlayerUI.AddWidget(NewUI, player_ui_slot{InputMode := ui_input_mode.None})
if (set UIPerPlayer[Player] = option{NewUI}) {}
spawn:
UpdateHeight()
#高さを監視する
UpdateHeight()<suspends>: void =
loop:
Sleep(0.1)
PlayerHeight := GetHeight(InstancePlayer)
HeightWidget.SetText(HeightText(BaseText, PlayerHeight))
#Playerの高さを取得する
GetHeight(Player: player): int =
if (FortCharacter := Player.GetFortCharacter[]):
PlayerLocation := FortCharacter.GetTransform().Translation
PlayerHeight := PlayerLocation.Z + Offset*100.0 #cm単位に変換
if (HeightInt := Floor[PlayerHeight/100.00]):
return HeightInt
return 0
UI作成時に必要な情報をcreative_device
から渡し、相互参照が起きないように改良しました
基本的なロジックとしてはCheckForPlayers()
を1秒に1回実行しまだUIを作っていないプレイヤーを探します
もしUIを作っていないプレイヤーが居たらそのプレイヤーにUIを作成します。
UIを作成した後にheight_widget
内で非同期なUpdateHeight()
を実行し0.1秒ごとにプレイヤーの位置を監視してUIを更新しています
終わりに
このコードを改造することでプレイヤー情報を監視しながらUIを更新することができます
是非役立ててください
参考
Height Meter for OnlyUp style maps
[UEFN][Verse] UIにImage/Textureを貼る実装。これからVerseやっていきな人へ
Discussion