Help us understand the problem. What is going on with this article?

UE4 でプレイヤー以外の Tick と Physics を止める方法の検討 (第8回ぷちコン)

More than 1 year has passed since last update.

はじめに

第8回ぷちコンにて実装した、 UE4 で自分のプレイヤー以外の時間(Tick,Physics)を止める方法を検討したときの覚書き。

参考までに応募したやつ ↓


http://www.youtube.com/watch?v=TMK2cYWaAyw

検討1: SetGamePaused → 不採用

SetGamePaused.png

これでは自分を含めて全部止まってしまう。
そもそもポーズ画面でこれを使うつもりだったので、不採用。

検討2: CustomTimeDilation + SetSimulatePhysics → 不採用

CustomTimeDilation.png

確かに、これで個別に Tick と物理を止めることは可能。
が、メッシュに対しては、個別に SetSimulatePhysics を呼ぶ必要がある。めんどくさい。
(事前に MeshComponent 検索して設定する機能をつけた BP を継承すれば良い話ではあるが)

あと、時間を止めるトリガーが来た瞬間に、ステージ内の自分以外の全アクターに対してこの処理が走ることになる。
うーん、もっとスマートな方法はないものか・・・。

検討3: UWorld::bPlayersOnly, UWorld::bShouldSimulatePhysics → 今回採用

実は、UWorld 内のプロパティをいじると、特定のアクター以外の時間を止めることができる

UWorld::bPlayersOnly

これを true にすると、ローカルでコントローラーに Possess されている APawn(もしくはその継承クラス, ACharactorとか) のみ Tick を走らせる ようになる。
もっと厳密にいうと、ワールドの TickType が強制的に ELevelTick::LEVELTICK_ViewportsOnly になる。
Actor が Tick による更新指示を受ける部分のコードを読めばわかるのだが、

Actor.cpp
void AActor::TickActor( float DeltaSeconds, ELevelTick TickType, FActorTickFunction& ThisTickFunction )
{
    //root of tick hierarchy

    // Non-player update.
    const bool bShouldTick = ((TickType!=LEVELTICK_ViewportsOnly) || ShouldTickIfViewportsOnly());
    if(bShouldTick)
    {
        // If an Actor has been Destroyed or its level has been unloaded don't execute any queued ticks
        if (!IsPendingKill() && GetWorld())
        {
            Tick(DeltaSeconds); // perform any tick functions unique to an actor subclass
        }
    }
}

// ~省略~

/** If true, actor is ticked even if TickType==LEVELTICK_ViewportsOnly */
bool AActor::ShouldTickIfViewportsOnly() const
{
    return false;
}

となっている。
つまり、アクターは基本的に ELevelTick::LEVELTICK_ViewportsOnly 時は Tick が走らない
しかし、AActor の継承クラスの一部において ShouldTickIfViewportsOnly() を override しているものがある。

  • ACameraRig_Crane
  • ACameraRig_Rail
  • ACineCameraActor
    • これらの3つはシーケンサ用で必ず true を返すようになっている。
  • APlayerCameraManager
    • APlayerController の参照を持っているときだけ true を返す。
    • APlayerController がスポーンされるときには必ず渡されるものなので、これも基本的には true を返すと見てよい
  • APawn
    • 「ローカルでコントロールされている」かつ 「APlayerController がセットされている」という条件で true を返す

ということで、自分で継承クラスを作って ShouldTickIfViewportsOnly() を override しない限りは、
カメラ関係以外ではローカルでコントローラーに Possess されている APawn かその継承クラスのみ Tick が走ることになる。

なので、これを true にすれば、プレイヤー以外の Tick を止めることができる。

UWorld::bShouldSimulatePhysics

これを false にすると、ワールドのすべての Physics による更新をしなくなる。
ただし、これはあくまで Physics の更新を止めるだけであり、オブジェクト同士の作用(ぶつかったときの力など)はそのまま受け付けている状態になる。
そのため、解除した瞬間に、蓄積された作用が一気に適用される挙動となる。

この挙動を起きないようにするためには、やはり SetSimulatePhyshics を false にしないといけない。
が、今回作ったゲームでは、むしろその挙動の方が面白かったので、あえてそのままにしてある。

実装

UWorld も上記のプロパティもブループリント公開はされてないので C++ で組む。
FunctionLibrary で共用の関数として、以下のようにして実装した。

CommonBlueprintFunctionLibrary.h
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CommonBlueprintFunctionLibrary.generated.h"

UCLASS()
class ONEMINUTE_API UCommonBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()
public:

    UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject") , Category="TimeStop")
    static void StartTimeStop(UObject* WorldContextObject);

    UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject") , Category="TimeStop")
    static void FinishTimeStop(UObject* WorldContextObject);
};
CommonBlueprintFunctionLibrary.cpp
void UCommonBlueprintFunctionLibrary::StartTimeStop(UObject* WorldContextObject)
{
    UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject);
    if(World) {
        World->bPlayersOnly = true;
        World->bShouldSimulatePhysics = false;
    }
}

void UCommonBlueprintFunctionLibrary::FinishTimeStop(UObject* WorldContextObject)
{
    UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject);
    if(World) {
        World->bPlayersOnly = false;
        World->bShouldSimulatePhysics = true;
    }
}

あとは、ブループリントから必要なタイミングで呼び出せばよい。
やってみたときの実験動画 → https://ue4-mstdn.tokyo/@negimochi/1098

negimochi
ゲームのプログラマしてます。
http://negimochi.work/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした