UE4 10km以上遠くにActorを移動すると破壊される件


UE4のWorldの大きさ問題

最近作ってたコンテンツでおきた問題をメモしておきます。

そのコンテンツはWheeledVehicleを使って車を走らせていたのですが、最近走るエリアを大幅に広げてみました。

それでちゃんと動くかテストしたところ、10kmを超えたあたりで車が消滅してしまいました。。。。

エリアが広すぎるせいで処理が重くなったりするかなあという不安はもっていたのですが、急に車が消滅するのは想像していませんでした。

そこで色々調べたところ原因と解決策が分かりました。

WorldSettingsのEnable World Bounds Checks

まず簡単にこの件を解決する方法としては、WorldSettingsのEnable World Bounds ChecksをOFFにすることです。

(色々検索しててみつけたのですが、元のページが見つからなくなってしまった。。。)

/images/2019/07/03/221615/20190703215908.png

この項目をOFFにするとたしかに10kmを超えても車は消滅しません。

なぜなのか?この項目は何に効いているのか?と疑問がでたのでもう少し調べてみました。

Editorでこの項目にマウスを合わせると以下の画像のようにツールチップがでます。

/images/2019/07/03/221615/20190703220325.png

ん?CheckStillInWorldってなんだ?と思ってEngineのソースを検索してみると、AActor::CheckStillInWorld‘という関数が見つかりました。これは怪しい。

bool AActor::CheckStillInWorld()
{
    if (IsPendingKill())
    {
        return false;
    }
    UWorld* MyWorld = GetWorld();
    if (!MyWorld)
    {
        return false;
    }

    // Only authority or non-networked actors should be destroyed, otherwise misprediction can destroy something the server is intending to keep alive.
    if (!(HasAuthority() || Role == ROLE_None))
    {
        return true;
    }

    // check the variations of KillZ
    AWorldSettings* WorldSettings = MyWorld->GetWorldSettings( true );

    if (!WorldSettings->bEnableWorldBoundsChecks)
    {
        return true;
    }

    if( GetActorLocation().Z < WorldSettings->KillZ )
    {
        UDamageType const* const DmgType = WorldSettings->KillZDamageType ? WorldSettings->KillZDamageType->GetDefaultObject<UDamageType>() : GetDefault<UDamageType>();
        FellOutOfWorld(*DmgType);
        return false;
    }
    // Check if box has poked outside the world
    else if( ( RootComponent != nullptr ) && ( GetRootComponent()->IsRegistered() == true ) )
    {
        const FBox&    Box = GetRootComponent()->Bounds.GetBox();
        if(   Box.Min.X < -HALF_WORLD_MAX || Box.Max.X > HALF_WORLD_MAX ||
            Box.Min.Y < -HALF_WORLD_MAX || Box.Max.Y > HALF_WORLD_MAX ||
            Box.Min.Z < -HALF_WORLD_MAX || Box.Max.Z > HALF_WORLD_MAX )
        {
            UE_LOG(LogActor, Warning, TEXT("%s is outside the world bounds!"), *GetName());
            OutsideWorldBounds();
            // not safe to use physics or collision at this point
            SetActorEnableCollision(false);
            DisableComponentsSimulatePhysics();
            return false;
        }
    }

    return true;
}

関数の一番最後に答えが書いてありました。

const FBox& Box = GetRootComponent()->Bounds.GetBox();
if(   Box.Min.X < -HALF_WORLD_MAX || Box.Max.X > HALF_WORLD_MAX ||
    Box.Min.Y < -HALF_WORLD_MAX || Box.Max.Y > HALF_WORLD_MAX ||
    Box.Min.Z < -HALF_WORLD_MAX || Box.Max.Z > HALF_WORLD_MAX )
{
        UE_LOG(LogActor, Warning, TEXT("%s is outside the world bounds!"), *GetName());
    OutsideWorldBounds();

ここでActorの位置のチェックが行われており、HALF_WORLD_MAXを超えた座標の場合はOutsideWorldBounds()が呼ばれています。

OutsideWorldBounds()の中を見ると`Destroy();‘とだけ書かれていました。

これやん!!!

HALF_WORLD_MAXの定義は以下のようになっています。

#define WORLD_MAX                   2097152.0              /* Maximum size of the world */
#define HALF_WORLD_MAX             (WORLD_MAX * 0.5)      /* Half the maximum size of the world */

あー、だいたい10kmだな。これで確定。

なので、設定をいじらない状態だと、-10kmから+10kmまでの全長20kmがとりえる最大の距離だということが分かりました。(対角線使えばもうちょっと長くできるけど)

これ以上広い世界を作るにはどうするんだろう?Enable World Bounds ChecksをOFFにするのはイレギュラーな気もするのでWorldを分けるとかするんでしょうか?気になるのでまた調べてみます。

UE4 

See also