🎮

「UnrealEngine」最寄りのナビメッシュ上の座標を取得する

2023/12/31に公開

必要になった背景

  • ナビメッシュで経路探索をする際に
  UNavigationSystemV1::FindPathToLocationSynchronously
  ( UObject* WorldContextObject,
    const FVector& PathStart,
    const FVector& PathEnd,
    AActor* PathfindingContext,
    TSubclassOf<UNavigationQueryFilter> FilterClass
   )
  • この関数のみでパス検索すると、終点がナビメッシュの外に出たとき(キャラが空中に浮いたりとかした場合)に、パスの作成に失敗してしまう。そのためこの関数で指定した終点がメッシュ内になるようにしたかった。

解決方法

  UNavigationSystemV1::ProjectPointToNavigation
    ( const FVector& Point,
      FNavLocation& OutLocation,
      const FVector& Extent = INVALID_NAVEXTENT,
      const FNavAgentProperties* AgentProperties = NULL,
      FSharedConstNavQueryFilter QueryFilter = NULL
    )
  • この関数を使用すると Extent で指定した範囲内の最寄りの終点位置が FNavLocation& で取れる。結果を取り出す際は
FNavLocation::Location
  • で取得できるので、この値を FindPathToLocationSynchronously()PathEnd に指定する。
#include "NavigationSystem.h"
#include "NavigationPath.h"

//~~~~

void ASample::FindPath(const FVector& endPoint)
{
    const auto owner = GetOwner();
    if (!IsValid(owner))
    {
        return;
    }

    UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld());

    // endPointから最寄りのMeshの検索範囲
    const FVector Extent(500, 500, 10000);

    // 巨大なアクター等だと開始地点がうまく取得できないのでExtentで位置を取得する
    FNavLocation StartPathLocation;

    // メッシュからはみ出た位置を指定すると終了地点がうまく取得できないのでExtentで位置を取得する
    FNavLocation EndPathLocation;

    // 最寄りのナビメッシュ上の座標が取れる
    NavSys->ProjectPointToNavigation(owner->GetActorLocation(), StartPathLocation, Extent);
    NavSys->ProjectPointToNavigation(endPoint, EndPathLocation, Extent);

    NavPath = NavSys->FindPathToLocationSynchronously(GetWorld(), StartPathLocation.Location, EndPathLocation.Location);

    #if WITH_EDITOR
    // 以下の処理はデバッグ用の表示のみ
    if (IsValid(NavPath))
    {
        float green_hue = 1.0f / 3.0f; // Normalized hue for green
        float red_hue = 0.0f; // Normalized hue for red
        float saturation = 1.0f; // Full saturation
        float brightness = 1.0f; // Full brightness

        for (int point = 0; point < NavPath->PathPoints.Num(); ++point)
        {
            // Calculate the hue based on the point index, interpolating between green and red
            float Hue = FMath::Lerp(green_hue, red_hue, static_cast<float>(point) / (NavPath->PathPoints.Num() - 1));

            // Create an FLinearColor from HSV values
            FLinearColor PointColorHSV = FLinearColor::MakeFromHSV8(Hue * 255, saturation * 255, brightness * 255);

            // Convert to FColor
            FColor PointColor = PointColorHSV.ToFColor(true);

            // Use the color for the current point
            DrawDebugSphere(
                GetWorld(),
                NavPath->PathPoints[point],
                10.0f,
                12,
                PointColor,
                false,
                0.01f
            );

            // If this is not the first point, draw a line from the previous point to the current one
            if (point > 0)
            {
                // Interpolate the hue for the line color based on the previous point
                float LineHue = FMath::Lerp(green_hue, red_hue, static_cast<float>(point - 1) / (NavPath->PathPoints.Num() - 1));
                FLinearColor LineColorHSV = FLinearColor::MakeFromHSV8(LineHue * 255, saturation * 255, brightness * 255);
                FColor LineColor = LineColorHSV.ToFColor(true);

                // Draw the line
                DrawDebugLine(
                    GetWorld(),
                    NavPath->PathPoints[point - 1],
                    NavPath->PathPoints[point],
                    LineColor,
                    false, 0.01f, 0,
                    5.0f // Thickness
                );
            }
        }
    }
    #endif
}

Discussion