はじめに
例えばゲームを制作している際に、デモやイベントの開始などで、プレイヤーなど特定のアクターを瞬間移動(テレポート、ワープ)させたくなることがあると思います。
そんな場合に、以下のようにシンプルな記述していませんか?
TargetActor->SetActorLocationAndRotation(TargetLocation, TargetRotation);
これでも実現はできるのですが、UnrealEngine(以下UE)にはテレポートの際に気をつけたいパラメータや便利な処理が存在しています。
今回は見落としがちなこれらテレポート関係について、C++のコードから深堀りしつつまとめてみます。
なお、確認したUEのバージョンは5.6になります。
これ以降のバージョンでは、新しい機能の追加や変更があるかもしれません。
対象読者
この記事では主にC++を使って説明していきますが、Blueprint(以下BP)でも形は違えど同じ処理を呼び出しているため、BPを使っている方にも参考になる内容かと思います。
ちょっとしたワンポイントの説明なので、内容としては初心者向けです。
SetActorLocationAndRotationを詳しく見てみる
UEのSetActorLocationAndRotationの宣言は以下のようになっています。
/**
* Move the actor instantly to the specified location and rotation.
*
* @param NewLocation The new location to teleport the Actor to.
* @param NewRotation The new rotation for the Actor.
* @param bSweep Whether we sweep to the destination location, triggering overlaps along the way and stopping short of the target if blocked by something.
* Only the root component is swept and checked for blocking collision, child components move without sweeping. If collision is off, this has no effect.
* @param Teleport How we teleport the physics state (if physics collision is enabled for this object).
* If equal to ETeleportType::TeleportPhysics, physics velocity for this object is unchanged (so ragdoll parts are not affected by change in location).
* If equal to ETeleportType::None, physics velocity is updated based on the change in position (affecting ragdoll parts).
* If CCD is on and not teleporting, this will affect objects along the entire swept volume.
* Note that when teleporting, any child/attached components will be teleported too, maintaining their current offset even if they are being simulated.
* Setting the location without teleporting will not update the location of simulated child/attached components.
* @param OutSweepHitResult The hit result from the move if swept.
* @return Whether the rotation was successfully set.
*/
ENGINE_API bool SetActorLocationAndRotation(FVector NewLocation, FRotator NewRotation, bool bSweep=false, FHitResult* OutSweepHitResult=nullptr, ETeleportType Teleport = ETeleportType::None);
ここで注目したいのは、ETeleportType という型のTeleport引数のところです。
デフォルトではETeleportType::Noneになっており、指定しなくても動くようにはなっています。
ETeleportTypeの定義も見てみます。
/** Whether to teleport physics body or not */
UENUM()
enum class ETeleportType : uint8
{
/** Do not teleport physics body. This means velocity will reflect the movement between initial and final position, and collisions along the way will occur */
None,
/** Teleport physics body so that velocity remains the same and no collision occurs */
TeleportPhysics,
/** Teleport physics body and reset physics state completely */
ResetPhysics,
};
コメントを読むと、ETeleportType::TeleportPhysicsを指定することで、物理上は移動してないこと(この移動による速度を発生させない)にできるようです。
また、ETeleportType::ResetPhysicsを使うことで物理状態をリセットできるともあります。
つまり、物理を有効にしたオブジェクトをテレポートさせる際には、ここの値を気にする必要があります。
例えば、
「デモの開始やカット切替時などで、位置が遠くに一瞬で変わる場面転換のような状況」の場合にはリセットなどの使い方を想定しているようです。
その場にいたはずなのに布や髪などといった物理の揺れものが激しく動いている、というようなことが起きないよう制御をするための機能です。
BPコマンドにある「Teleport」にチェックを入れた場合、ETeleportType::TeleportPhysicsを設定したのと同じ動作をします。
ちなみに、SetActorLocationAndRotationに限らず他の移動処理にもこの指定があると思います。
TeleportTo関数
実はアクターにはTeleportToというテレポートのための関数があります。
/**
* Used for adding actors to levels or teleporting them to a new location.
* The result of this function is independent of the actor's current location and rotation.
* If the actor doesn't fit exactly at the location specified, tries to slightly move it out of walls and such if bNoCheck is false.
*
* @param DestLocation The target destination point
* @param DestRotation The target rotation at the destination
* @param bIsATest is true if this is a test movement, which shouldn't cause any notifications (used by AI pathfinding, for example)
* @param bNoCheck is true if we should skip checking for encroachment in the world or other actors
* @return true if the actor has been successfully moved, or false if it couldn't fit.
*/
ENGINE_API virtual bool TeleportTo( const FVector& DestLocation, const FRotator& DestRotation, bool bIsATest=false, bool bNoCheck=false );
この関数の内部では、 ETeleportType::TeleportPhysics を指定して移動を行います。
また、テレポートできた場合、AActor::TeleportSucceededという関数を呼び出してくれるようです。
この関数に、テレポート成功時の処理を記述できるようになっています。(ただしC++のみ)
位置向き指定の他に、bIsATestとbNoCheckの引数があります。
これらの機能について詳しく見てみます。
bIsATestの機能は?
このフラグはAIのパス移動などで使用する目的のようです。
Testという名前からして、移動はせずテレポートの可否のみを判定するのかと思いきや、実装上では位置向きを更新するようでした。
bNoCheckの機能は?
実際にテレポートさせるとすると、その位置が安全かどうかのチェックが必要になることがあります。
例えば地形や他のアクターに重ならないか、などです。
これがfalseの場合、テレポート先が埋まらない位置かどうかの安全かどうかのチェックをUWorld::FindTeleportSpot関数を使って行ってくれ、自分で実装の手間を省くことができそうです。
たとえば安全かどうかのチェックだけを自作したい、または無効化したい場合にはtrueにすると良さそうです。
ACharacterは対応している?
C++の実装を見ると対応しています。
CharacterMovementにはTeleportToが呼ばれた際の処理が記述されていて、動く足場(Based)に乗っている状態の切り替えや、移動先の地形に合わせたMovementModeの変更を行ってくれるようです。
Teleportコマンド

BPからもTeleportToを呼び出せるコマンドがあります。
こちらには位置と向き以外は特に指定がなく、bIsATest、bNoCheckともにfalseを指定してTeleportToを呼び出します。
まとめ
つい位置向き移動だけでサクッと済ませてしまいそうなテレポート(ワープ)処理ですが、ただ移動させるだけではダメな場合もあり、移動先が安全かどうかのコリジョンチェックや物理の速度リセットなど、テレポートならではの考慮の必要なポイントがあります。
UEでテレポートさせる際はシンプルにSetActorLocation系の関数を使わず、まずはTeleportTo関数を使ってみることが良いと思います。
使うことで、テレポートならではの処理を自作せずにすむかもしれません。
ただし、自作したい場合も出てくることがあると思います。
(コリジョンチェックを独自にやりたいなど)
その場合には、自作のテレポート専用の関数を用意しておくと良いでしょう。
テレポート時だけ実行したい処理が必要になる場合はよくあるので、追記できるように関数にまとめておくといいと思います。
この記事がお役に立てば幸いです。
参考資料
