執筆バージョン: Unreal Engine 4.24 |
皆さん、今日も元気にナビメッシュを活用してますでしょうか? キャラクターを動かす時に重宝するナビメッシュですが、なかなかクセが強くて大変ですよね。ビルドしたのに昔のナビメッシュデータが残っていたり、うまく生成してくれなかったり…… 今日はそんなナビメッシュに関して、機能をちょっとだけ拡張してナビメッシュ関連のボリュームを配置するワークフローを改善してみたいと思います。 まずはNavModifierVolumeについて軽く説明します。このボリュームはナビメッシュに対して重み(優先度)を付ける事ができます。どういう事かというと、通常AIは目的地に向かって最短ルートを進もうとしますが、このボリュームによって指定した範囲を避けて通るような動きをつける事ができるというものです。 画像を使って説明します。通常時は画像のように最短ルートを通ります。

次に最短ルートの途中にNavModifierVolumeを配置して「AreaClass」を「NavArea_Obstacle」にしてここに障害物があることにします。

するとAIは障害物を避けて遠回りするルートを使用します。

ちなみに両方に障害物を配置すると、諦めて最短ルートを通ります。

さて、ここからが本題です。障害物を配置するたびにNavModifierVolumeを同じように配置するのは苦行ですよね。そこで活用できそうなのが、NavModifierComponentです。これを使用すれば、障害物用のアクターに対してNavModifierVolumeを追加できるようになります。なりますが、なかなかにクセが強くて扱いが難しいです。 色々試してみたり、UNavModifierComponentのソースを読んで分かった事が以下の通りです。 まず、アクターにコリジョンがついていると、それぞれのコンポーネントに対してNavModifierの範囲が適用されます。なので例えばStaticMeshが1つだけのアクターにNavModifierComponentを付けてもあまり意味がありません。もともとそのStaticMeshのサイズでナビメッシュは削除されるからです。

そこでStaticMesh以外にも影響範囲として別途CollisionComponentを追加すると、その範囲分も含めてNavModifierが適用されます。ここまでは非常に便利な機能だと思います。

次にアクター側のコリジョンをNoCollision(全てのComponentがNoCollision)にした場合は、NavModifierComponentのFailsafeExtentの範囲が適用されます。

そしてこれはバグだと思うのですが、このFailsafeExtentは回転に対応していません。対応していないというか、正確にいうと移動と回転の計算が逆になっているようです。アクターを回転すると全然違う座標にNavModifierが適用されてしまってます。FailsafeExtentが適用されてしまっている状態の場合は注意しましょう。(Failsafeとは…)

ここまでで分かったことは、UNavModifierComponentはコリジョンに深く依存しているということです。逆にいえばコリジョンに関係のない範囲でNavModifierを指定しようとすると実質無理だという事がわかります。 ですが、そうしたい場合は少なからずあるかと思います。例えば宝箱やギミックなどに敵を近づけさせたくない場合、レギュレーションとしてギミックの半径何m以内は敵が侵入できないようにしたいなどです。 今回はそれらのケースに対応した機能をUNavModifierComponentを派生させて別途作成する手法の紹介をしたいと思います。 まずはUNavModifierComponentを基底とした派生クラスをC++で新規作成します。
次にソースコードの中身です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
#pragma once #include "CoreMinimal.h" #include "NavModifierComponent.h" #include "TestNavModifierComponent.generated.h" /** * */ UCLASS(ClassGroup = (Navigation), meta = (BlueprintSpawnableComponent)) class NAVTEST_API UTestNavModifierComponent : public UNavModifierComponent { GENERATED_BODY() public: // 影響範囲 UPROPERTY(EditAnywhere) FVector NavExtent; // 中心座標の調整用 UPROPERTY(EditDefaultsOnly) FVector OffsetLocation; public: UTestNavModifierComponent(const FObjectInitializer& amp; ObjectInitializer); virtual void GetNavigationData(FNavigationRelevantData& amp; Data) const override; }; |
cppはこちらになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
#include "TestNavModifierComponent.h" UTestNavModifierComponent::UTestNavModifierComponent(const FObjectInitializer& amp; ObjectInitializer) : Super(ObjectInitializer) , NavExtent(100.0f, 100.0f, 100.0f) , OffsetLocation(0.0f, 0.0f, 0.0f) { } void UTestNavModifierComponent::GetNavigationData(FNavigationRelevantData& amp; Data) const { FTransform ActorTransform(GetOwner() - > GetActorTransform()); FTransform OffsetTransfrom(OffsetLocation); FTransform OutTransfrom; // 今回はアクターのスケール値は無視する ActorTransform.SetScale3D(FVector(1.0f, 1.0f, 1.0f)); FTransform::Multiply(& OutTransfrom, & OffsetTransfrom, & ActorTransform); Data.Modifiers.Add(FAreaNavModifier(NavExtent, OutTransfrom, AreaClass).SetIncludeAgentHeight(bIncludeAgentHeight)); } |
NavExtentを編集することで影響範囲を自由に変更する事が可能になりました。また、UNavModifierComponentはUActorComponentが基底クラスになっているため、コンポーネント単体での座標情報を持っていません。そのため中心座標を調整できるようにしています。 これをビルドするとアクターに追加するコンポーネントの一覧に「Test Nav Modifier」が追加されるので自作したアクターに追加してみます。 NavExtentでデフォルトのサイズ、OffsetLocationで中心点を指定して、あとはレベルに配置するだけで完成です。これでコリジョンとは無関係にNavModifierを配置できるようになりました。
いかがでしょうか? 既存のNavModifierComponentと今回作成したComponentとを使い分ける事で大抵のケースはカバーできそうな気がします。 ただ、現状だとNavModifierの範囲がナビメッシュを表示しないとわからないという問題を抱えていますが、こちらは視認用のprimitiveComponentを用意して、その値をNavModifierに範囲させるなどの対応を取ればなんとかなりそうです。 是非活用してみて下さい。