【UEFN】 DiabloライクのTop-DownViewカメラを作る Part.2(Verseの学習④)
カメラ方向と操作方向が一致したTop-Down Viewカメラを作る
前回紹介したTop-Down View カメラはカメラ方向と操作方向が一致していない場合に、非常に操作が行いづらいという欠点がありました。
そこで、前回のものを改修して、プレイヤーの向きにカメラが追従するように変更します。
イメージとしてはこのような形になります。

改修方針
- 親となっているオブジェクト(図のNullProp)の位置をプレイヤーの位置と一致させるように動かします。(A)
- カメラのオフセットは親からの相対座標として与えておきます。(B)
- プレイヤーの視点移動に合わせて、親となっているオブジェクトのYaw角を動かします。(A)
- ジャンプした際に、カメラ方向と操作方向を一致させるようにします。(常に一致させることも可能ですが、ゲーム性としてジャンプした際のみに一致させる方針で進めます)

概念図
シーン準備
前回と同一のものを使用し、カメラオフセット(上記図のB)は変更しておきます。
Verse Code
改修したコードは以下のようになります。
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/Characters }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/Characters }
# A Verse-authored creative device that can be placed in a level
track_player_manager := class(creative_device):
@editable
_CameraRoot: creative_prop = creative_prop{}
@editable
_OtherRoot: creative_prop = creative_prop{}
@editable
_OffsetX : float = 0.0
@editable
_OffsetY : float = 0.0
@editable
_OffsetZ : float = 0.0
var _CurrentRotation : rotation = rotation{}
### OnBegin
OnBegin<override>()<suspends>:void=
Offset:vector3 = vector3{X:=_OffsetX, Y:=_OffsetY, Z:=_OffsetZ}
## add jumped event
Players : []player = GetPlayspace().GetPlayers()
if(Player : player = Players[0]):
if(FortniteCharacter : fort_character = Player.GetFortCharacter[]):
FortniteCharacter.JumpedEvent().Subscribe(OnPlayerJumped)
UpdateRotationValue()
## start update loop
spawn{UpdateTargetPosition(Offset)}
### process for jump
OnPlayerJumped(the_player:fort_character):true=
UpdateRotationValue()
### update rotation value
UpdateRotationValue():void=
Players : []player = GetPlayspace().GetPlayers()
if(Player : player = Players[0]):
if(FortniteCharacter : fort_character = Player.GetFortCharacter[]):
### Not Good
#var playerRotation : rotation = FortniteCharacter.GetTransform().Rotation
### Good
var viewRotation : rotation = FortniteCharacter.GetViewRotation()
var angles :[]float = viewRotation.GetYawPitchRollDegrees()
if(angleYaw := angles[0]):
anglePitch := 0.0
angleRoll := 0.0
targetRotation := MakeRotationFromYawPitchRollDegrees(angleYaw, anglePitch, angleRoll)
set _CurrentRotation = _CurrentRotation.RotateBy(MakeShortestRotationBetween(_CurrentRotation, targetRotation))
### Loop Process
UpdateTargetPosition(Offset:vector3)<suspends>:void=
loop:
## get player and player position
Players : []player = GetPlayspace().GetPlayers()
if(Player : player = Players[0]):
if(FortniteCharacter : fort_character = Player.GetFortCharacter[]):
PlayerPosition : vector3 = FortniteCharacter.GetTransform().Translation
## add rotation
PlayerRotation : rotation = FortniteCharacter.GetTransform().Rotation
## first rotation
TargetPositionX : float = PlayerPosition.X
TargetPositionY : float = PlayerPosition.Y
TargetPositionZ : float = PlayerPosition.Z
TargetPosition:vector3 = vector3{X:=TargetPositionX, Y:=TargetPositionY, Z:=TargetPositionZ} + Offset
## move target to player position
_CameraRoot.MoveTo(TargetPosition, _CurrentRotation, 0.5)
_OtherRoot.MoveTo(TargetPosition, IdentityRotation(), 0.5)
# Not Good
# if(_CameraRoot.TeleportTo[TargetPosition, IdentityRotation()]):
# void
Sleep(0.5)
# Not Good
# Sleep(0.0)
コード解説します。 CameraRootは上記図で言うNullPropをセットする場所です。 OtherRootを今回追加しています。これは、回転を適用せずにプレイヤーに追従してほしいものを入れるために用意しました。 自分の作例では、ライトを入れています。(ライトは回転させたくないため)
@editable _CameraRoot: creative_prop = creative_prop{} @editable _OtherRoot: creative_prop = creative_prop{}
_CurrentRotationに計算した回転量を保持していきます。
var _CurrentRotation : rotation = rotation{}
プレイヤーがジャンプした際に、回転量計算が発生するようにしています。
FortniteCharacter.JumpedEvent().Subscribe(OnPlayerJumped)
...
OnPlayerJumped(the_player:fort_character):true=
UpdateRotationValue()
UpdateRotationValueが回転量計算の部分になります。
今回の一番のポイントはここです。
### Not Good #var playerRotation : rotation = FortniteCharacter.GetTransform().Rotation ### Good var viewRotation : rotation = FortniteCharacter.GetViewRotation()
プレイヤーに追従させたいのでプレイヤーの回転をとってしまいそうなところですが、それではうまくいきません。実際の操作は視点の方向と一致する必要があるため、視点の回転情報を取得する必要があります。
Fortniteは3人称ゲームのため、キャラクターの方向と視点の方向が一致していません。
Yaw角だけを取り出して回転情報を作ります。現在の回転との差分を出し、時計回りと半時計回りで近い方向に回るようにしています。
var angles :[]float = viewRotation.GetYawPitchRollDegrees() if(angleYaw := angles[0]): anglePitch := 0.0 angleRoll := 0.0 targetRotation := MakeRotationFromYawPitchRollDegrees(angleYaw, anglePitch, angleRoll) set _CurrentRotation = _CurrentRotation.RotateBy(MakeShortestRotationBetween(_CurrentRotation, targetRotation))
ここで出来た回転情報を使用してMoveTo関数を利用します。
_CameraRoot.MoveTo(TargetPosition, _CurrentRotation, 0.5)
_OtherRootは回転はせず位置だけ追従させるので前回と同様です。
デバイス設定
デバイス設定は以下のようになります。
_OtherRootはここではセットしていませんが、実際はライトのためのRootオブジェクトをセットしています。

まとめ
今回の結果はこのような形になります。

視点方向とカメラ方向が一致したため、感覚的に操作が行えるようになりました。
また、ジャンプで回転というゲーム性も出たため、なかなかいいように思います。
EpicのDevCommunityで今回のコードをSnippetとして投稿しておきました。
https://dev.epicgames.com/community/snippets/xve/fortnite-top-down-view-support-device
この情報が皆さんの参考になれば幸いです。
今後もさまざまな情報を発信していきますので、Twitterフォローしていただけると嬉しいです。 twitter.com
UEFN楽しい:)