動画ではメッシュを購入している。
私はMixamoのスケルタルメッシュとアニメを使うことにする。
どうやらダウンロード設定がWithSkinだとアニメが動かない。
Withoutにすると使用できるアニメになる。
Idle、Walk、ゾンビアタックをインポートした。
In Placeにチェックを入れると位置が動かない。
動画では動いていないのでこのアニメを使う。
敵を使うためにBuilding PropのBPを作成する。
SkeltalMeshを追加。
まだスケルタルメッシュにはコリジョンがない。
スケルタルメッシュにそのままコリジョンは追加できないので、スタティックメッシュを作成する。
スタティックメッシュを開きコリジョンを作る。
BPで作ったスタティックメッシュをセット。
ヒットイベントにチェックを入れ、Visibleを無効にする。
スタティックメッシュは消えないことがあるようで、透明のマテリアルを作る。
スタティックメッシュにマテリアルをセット。
Can be Damagedにチェックが入っているか確認。
Deviceのアイテムグランダーを設置。
アイテムグランダーのアイテムリストにショットガンをセット。
付与されたアイテムの装備にチェックを入れる。
Deviceのトリガーを設置。
アイテムグランダーの「アイテムを付与する」でトリガーを選択し、On Triggeredにする。
トリガーを踏むとショットガンを装備するようになった。
BPのクラスデフォルトでinitと検索し、初期化カテゴリをそれぞれSoldier、初期化サブカテゴリをSoldier_Ability_R_T1にしている。
これで敵にHPが設定された。0になれば敵は消える。
どうやらシールドも設定されているようで3回攻撃しないとダメージは与えられなかった。
ナイアガラエフェクトをUEに追加して、それをUEFNのプロジェクトに移行している。
今回はフォートナイトのVFXを使うことにした。
敵BPのデスパーティクルにNS_GasPump_Explosionをセットした。
動画ではサウンドをインポートしたようだが、UEFNにあるサウンドを使う。
デスサウンドとヒットサウンドを適当にセット。
デスパーティクルソケット名にFX_Headと入力。
スタティックメッシュのソケットマネージャーでFX_Headというソケットを作成。
頭の位置に合わせる。
デスパーティクルなどが発生するようになった。
game_managerというverseファイルを作成。
敵を動かすらしい。
game_manager := class(creative_device):
# エディターで敵を指定できるようにする
@editable var Enemies : []creative_prop = array{}
# プレイヤーの配列
var Players : []player = array{}
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
InitPlayers()
# Players配列にプレイヤーをセットする関数
InitPlayers():void=
set Players = GetPlayspace().GetPlayers()
enemy_ai.verseというファイルをエクスプローラーで作成。
敵を動かすコードを書く。
# enemy_ai.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /Fortnite.com/Characters }
# 敵がプレイヤーを探す関数。
# <suspends>と入れることで単独で動く。ループする関数に使う。
FollowPlayer(Enemy : creative_prop, Player : player)<suspends>:void=
MoveSpeed := 0.01
# loopする
loop:
# 0.1秒停止
Sleep(0.1)
# Playerをゲットできるか。またEnemyが有効かどうか。
if(PlayerCharacter := Player.GetFortCharacter[], Enemy.IsValid[]):
# 敵の位置を取得。
PropLocation := Enemy.GetTransform().Translation
# プレイヤーの位置を取得。
PlayerLocation := PlayerCharacter.GetTransform().Translation
# 敵からプレイヤーの方向を計算する。Z軸のみを使用。
if(LookDirection := (PlayerLocation - PropLocation).MakeUnitVector[]):
Yaw := RadiansToDegrees(ArcTan(LookDirection.Y, LookDirection.X)) - 90.0
Pitch := 0.0 # RadiansToDegrees(ArcTan(LookDirection.Z, Sqrt((LookDirection.X * LookDirection.X) + (LookDirection.Y * LookDirection.Y))))
Roll := 0.0
NewRotation := MakeRotationFromYawPitchRollDegrees(Yaw, Pitch, Roll)
# Movement
LerpLocation := Lerp(PropLocation, PlayerLocation, MoveSpeed)
FinalLocation := vector3{X := LerpLocation.X, Y := LerpLocation.Y, Z := 0.0}
if:
Enemy.TeleportTo[FinalLocation, NewRotation]
else:
# 敵が死んだらループを停止。
break
# game_manager.verseに追加
OnBegin<override>()<suspends>:void=
InitPlayers()
InitEnemies()
InitEnemies():void=
# 最初のプレイヤー。
if(Player := Players[0]):
# 敵の数だけ繰り返す。
for(X := 0..Enemies.Length - 1):
if(Enemy := Enemies[X]):
# ループする関数にはspawnが必要。
spawn:
FollowPlayer(Enemy, Player)
作ったgame_managerを設置して、Enemiesを指定する必要がある。
敵がプレイヤーに向かって回転と移動をするようになった。
敵がプレイヤーにダメージを与えるためにDeviceのダメージボリュームを設置する。
敵の子にしてゾーンの位置や大きさを調整する。
子にすることで、敵を動かすとダメージボリュームも同じように動く。
アイテムグランダーの付与を全アイテムに変更して、ミニガンとロケットランチャーを追加。
敵に近づくと、プレイヤーがダメージを受けるようになった。
敵を破壊したことを確認するために、Deviceのプロップマニピュレーターを設置する。
プロップマニピュレーターの「ゾーン内の全てのオブジェクトに影響を与える」にチェックして、ゾーンを大きくする。
ゲームを終了させるためにエンドゲームデバイスを設置する。
エンドゲームデバイスの勝っているチームを、チームを起動中に設定。
敵を倒した時の関数を作る。
# game_manager.verseに追加するコード
# エディターでプロップマニピュレーターを指定できるようにする
@editable var PropManipulator : prop_manipulator_device = prop_manipulator_device{}
# エディターでエンドゲームデバイスを指定できるようにする
@editable var EndDevice : end_game_device = end_game_device{}
# 現在の倒した敵の数
var CurrentKillCount : int = 0
# ゲームを終了する敵の数
TotalKillsRequred : int = 3
InitEnemies():void=
# PropManipulator.DestroyedEventをOnPropDestroiyedに関連付ける。
PropManipulator.DestroyedEvent.Subscribe(OnPropDestroiyed)
# 敵を倒した時のイベント
OnPropDestroiyed(Agent : agent):void=
# CurrentKillCountを1増やす
set CurrentKillCount += 1
# CurrentKillCountがTotalKillsRequred以上ならゲームをエンド
if(CurrentKillCount >= TotalKillsRequred){EndDevice.Activate(Agent)}
game_ui.verseを作成。ユーザーインターフェースを実装する。
# game_ui.verse
using{ /UnrealEngine.com/Temporary/UI }
using{ /Fortnite.com/UI }
using{ /UnrealEngine.com/Temporary/SpatialMath }
using{ /Verse.org/Simulation }
game_ui := class():
# uiにテキストを表示するために必要。
KillsText<localizes>(Kills : int) : message = "Enemy Killed : {Kills}"
# キルカウントを表示するウィジェット。
KillCountWidget : button_loud = button_loud{}
# KillCountWidgetにKillsTextをSetTextする関数。
UpdateKillCount(TotalKills :int):void=
KillCountWidget.SetText(KillsText(TotalKills))
# game_manager.verseに追加するコード
# UIの変数。
var GameUI : game_ui = game_ui{}
# 敵を倒した時のイベント
OnPropDestroiyed(Agent : agent):void=
省略
# game_uiのUpdateKillCount関数を呼び出す。
GameUI.UpdateKillCount(CurrentKillCount)
uiを作成する関数を作る。
# game_ui.verseに追加するコード
# uiを作成する関数。
CreateUIForPlayer(Player : player):void=
# PlayerUIがあれば
if(PlayerUI := GetPlayerUI[Player]):
# 0とSetText
KillCountWidget.SetText(KillsText(0))
# uiの表示位置を指定する。
MyCanvas : canvas = canvas:
Slots := array:
canvas_slot:
# WidgetにKillCountWidgetを指定。
Widget := KillCountWidget
# アンカーを真ん中上部に指定。
Anchors := anchors{Minimum := vector2{X := 0.5, Y := 0.0}, Maximum := vector2{X := 0.5, Y := 0.0}}
# 上だけ100のマージンを設定。
Offsets := margin{Top := 100.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
# アライメントを真ん中に指定。
Alignment := vector2{X := 0.5, Y := 0.5}
# canvas_slotのサイズをコンテンツに合わせる。
SizeToContent := true
# PlayerUIにMycanvasを追加する。
PlayerUI.AddWidget(MyCanvas)
# game_manager.verseに追加するコード
# Players配列にプレイヤーをセットする関数
InitPlayers():void=
set Players = GetPlayspace().GetPlayers()
if(Player := Players[0]):
GameUI.CreateUIForPlayer(Player)
game_managerにPropManipulatorとEndDeviceをセット。
UIの表示と更新ができるようになり、指定した数の敵を倒すとゲームが終わるようになった。
BGMのためにDeviceのオーディオプレイヤーを設置。
オーディオプレイヤーの「ヒットで再生」を外す。
オートプレイを全てチェック。
音楽がなるようになって、チュートリアル終わり。
# game_manager.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# See https://dev.epicgames.com/documentation/en-us/uefn/create-your-own-device-in-verse for how to create a verse device.
# A Verse-authored creative device that can be placed in a level
game_manager := class(creative_device):
# エディターで敵を指定できるようにする
@editable var Enemies : []creative_prop = array{}
# エディターでプロップマニピュレーターを指定できるようにする
@editable var PropManipulator : prop_manipulator_device = prop_manipulator_device{}
# エディターでエンドゲームデバイスを指定できるようにする
@editable var EndDevice : end_game_device = end_game_device{}
# プレイヤーの配列
var Players : []player = array{}
# 現在の倒した敵の数
var CurrentKillCount : int = 0
# ゲームを終了する敵の数
TotalKillsRequred : int = 3
# UIの変数。
var GameUI : game_ui = game_ui{}
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
InitPlayers()
InitEnemies()
# Players配列にプレイヤーをセットする関数
InitPlayers():void=
set Players = GetPlayspace().GetPlayers()
if(Player := Players[0]):
GameUI.CreateUIForPlayer(Player)
InitEnemies():void=
# PropManipulator.DestroyedEventをOnPropDestroiyedに関連付ける。
PropManipulator.DestroyedEvent.Subscribe(OnPropDestroiyed)
# 最初のプレイヤー。
if(Player := Players[0]):
# 敵の数だけ繰り返す。
for(X := 0..Enemies.Length - 1):
if(Enemy := Enemies[X]):
# ループする関数にはspawnが必要。
spawn:
FollowPlayer(Enemy, Player)
# 敵を倒した時のイベント
OnPropDestroiyed(Agent : agent):void=
# CurrentKillCountを1増やす
set CurrentKillCount += 1
# CurrentKillCountがTotalKillsRequred以上ならゲームをエンド
if(CurrentKillCount >= TotalKillsRequred){EndDevice.Activate(Agent)}
# game_uiのUpdateKillCount関数を呼び出す。
GameUI.UpdateKillCount(CurrentKillCount)
# enemy_ai.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /Fortnite.com/Characters }
# 敵がプレイヤーを探す関数。
# <suspends>と入れることで単独で動く。ループする関数に使う。
FollowPlayer(Enemy : creative_prop, Player : player)<suspends>:void=
MoveSpeed := 0.01
# loopする
loop:
# 0.1秒停止
Sleep(0.1)
# Playerをゲットできるか。またEnemyが有効かどうか。
if(PlayerCharacter := Player.GetFortCharacter[], Enemy.IsValid[]):
# 敵の位置を取得。
PropLocation := Enemy.GetTransform().Translation
# プレイヤーの位置を取得。
PlayerLocation := PlayerCharacter.GetTransform().Translation
# 敵からプレイヤーの方向を計算する。Z軸のみを使用。
if(LookDirection := (PlayerLocation - PropLocation).MakeUnitVector[]):
Yaw := RadiansToDegrees(ArcTan(LookDirection.Y, LookDirection.X)) - 90.0
Pitch := 0.0 # RadiansToDegrees(ArcTan(LookDirection.Z, Sqrt((LookDirection.X * LookDirection.X) + (LookDirection.Y * LookDirection.Y))))
Roll := 0.0
NewRotation := MakeRotationFromYawPitchRollDegrees(Yaw, Pitch, Roll)
# Movement
LerpLocation := Lerp(PropLocation, PlayerLocation, MoveSpeed)
FinalLocation := vector3{X := LerpLocation.X, Y := LerpLocation.Y, Z := 0.0}
if:
Enemy.TeleportTo[FinalLocation, NewRotation]
else:
# 敵が死んだらループを停止。
break
# game_ui.verse
using{ /UnrealEngine.com/Temporary/UI }
using{ /Fortnite.com/UI }
using{ /UnrealEngine.com/Temporary/SpatialMath }
using{ /Verse.org/Simulation }
game_ui := class():
# uiにテキストを表示するために必要。
KillsText<localizes>(Kills : int) : message = "Enemy Killed : {Kills}"
# キルカウントを表示するウィジェット。
KillCountWidget : button_loud = button_loud{}
# KillCountWidgetにKillsTextをSetTextする関数。
UpdateKillCount(TotalKills :int):void=
KillCountWidget.SetText(KillsText(TotalKills))
# uiを作成する関数。
CreateUIForPlayer(Player : player):void=
# PlayerUIがあれば
if(PlayerUI := GetPlayerUI[Player]):
# 0とSetText
KillCountWidget.SetText(KillsText(0))
# uiの表示位置を指定する。
MyCanvas : canvas = canvas:
Slots := array:
canvas_slot:
# WidgetにKillCountWidgetを指定。
Widget := KillCountWidget
# アンカーを真ん中上部に指定。
Anchors := anchors{Minimum := vector2{X := 0.5, Y := 0.0}, Maximum := vector2{X := 0.5, Y := 0.0}}
# 上だけ100のマージンを設定。
Offsets := margin{Top := 100.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
# アライメントを真ん中に指定。
Alignment := vector2{X := 0.5, Y := 0.5}
# canvas_slotのサイズをコンテンツに合わせる。
SizeToContent := true
# PlayerUIにMycanvasを追加する。
PlayerUI.AddWidget(MyCanvas)