UE5 トレース系の処理についてのメモ

# 概要
UnrealEngine5 のトレースについてのメモ。
似たようなものでオーバーラップ（`UWorld::OverlapMultiByObjectType`など）がありますが、こちらはオーバーラップするとコリジョンイベントが発生します。


## 更新履歴
| 日付 | 内容 |
|:-----|:----|
|2024/03/07| 初版 |
|2024/11/05| サンプルコード修正 |


## 参考
以下の記事を参考にいたしました、ありがとうございます。
[UE5公式：トレースの概要](https://docs.unrealengine.com/5.3/ja/traces-in-unreal-engine---overview/)
[UE4レイキャスト(ライントレース)を使う](https://unrealengine.hatenablog.com/entry/2014/10/11/003032)
[Sweep Multi Line Trace](https://unrealcpp.com/sweep-multi-line-trace/)
[非同期シーンを利用したRaycast](https://www.docswell.com/s/EpicGamesJapan/ZVPELZ-cedec2018-ue4-CharacterPerformance#p116)
[UE5公式：UWorld](https://docs.unrealengine.com/5.3/en-US/API/Runtime/Engine/Engine/UWorld/)
[UE5公式：UKismetSystemLibrary](https://docs.unrealengine.com/5.3/en-US/API/Runtime/Engine/Kismet/UKismetSystemLibrary/)
[UE5公式：FRraceHandle](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/FTraceHandle?application_version=5.3)
[【UE5】コリジョン管理・処理に関する講演メモ・補足 : Collision Data in UE5: Practical Tips for Managing Collision Settings & Queries | Unreal Fest 2023](https://pafuhana1213.hatenablog.com/entry/2023/12/13/034613)
[コリジョンについて](https://gamedevmemoir.com/archives/1201)


## 環境
Windows10
Visual Studio 2022
UnrealEngine 5.3.2

## 関連ソース
"Engine\Source\Runtime\Engine\Classes\Engine\World.h"
"Engine\Source\Runtime\Engine\Private\World.cpp"
"Engine\Source\Runtime\Engine\Classes\Kismet\KismetSystemLibrary.h"
"Engine\Source\Runtime\Engine\Public\CollisionQueryParams.h"


# KismetSystemLibrary 
トレース系メソッドはUWorldクラスにありますが、BPで扱えるように KismetSystemLibraryクラスにてラップされています。

## ライントレース
ライントレースはブロックヒットまでに返すヒット数が1つか複数かで`Single`と`Multi` が用意されています。

```c++:KismetSystemLibrary.h
// 指定されたラインに沿って衝突トレースを実行し、最初に発生したブロックヒットを返します。
// このトレースは、指定された TraceChannel に応答するオブジェクトを検索します。
UFUNCTION(BlueprintCallable, Category="Collision", meta=(bIgnoreSelf="true", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore", DisplayName="Line Trace By Channel", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="raycast"))
static ENGINE_API bool LineTraceSingle(const UObject* WorldContextObject, const FVector Start, const FVector End, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);

// 指定されたラインに沿って衝突トレースを実行し、最初のブロックヒットまでに発生したすべてのヒットを返します。
// このトレースは、指定された TraceChannel に応答するオブジェクトを見つけます。
UFUNCTION(BlueprintCallable, Category="Collision", meta=(bIgnoreSelf="true", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore", DisplayName = "Multi Line Trace By Channel", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="raycast"))
static ENGINE_API bool LineTraceMulti(const UObject* WorldContextObject, const FVector Start, const FVector End, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, TArray<FHitResult>& OutHits, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);

```

このほか、`???ForObjects`, `???ByProfile` のような引数違いのメソッドが用意されています。`???ByProfile` での引数 `FName ProfileName`は `BlockAll`や`OverlapAll`などのプロジェクト設定の[コリジョン]で定義されている名前です。通常はゲーム独自の追加プロファイルで使うと思います。

```C++:KismetSystemLibrary.h
// 指定されたラインに沿って衝突トレースを実行し、最初に発生したヒットを返します。
// これは、ObjectTypes で指定されたタイプのオブジェクトのみを検索します。
UFUNCTION(BlueprintCallable, Category="Collision", meta=(bIgnoreSelf="true", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore", DisplayName = "Line Trace For Objects", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="raycast"))
static ENGINE_API bool LineTraceSingleForObjects(const UObject* WorldContextObject, const FVector Start, const FVector End, const TArray<TEnumAsByte<EObjectTypeQuery> > & ObjectTypes, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f );

// 指定されたラインに沿って衝突トレースを実行し、発生したすべてのヒットを返します。
// これは、ObjectTypes で指定されたタイプのオブジェクトのみを検索します。
UFUNCTION(BlueprintCallable, Category="Collision", meta=(bIgnoreSelf="true", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore", DisplayName = "Multi Line Trace For Objects", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="raycast"))
static ENGINE_API bool LineTraceMultiForObjects(const UObject* WorldContextObject, const FVector Start, const FVector End, const TArray<TEnumAsByte<EObjectTypeQuery> > & ObjectTypes, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, TArray<FHitResult>& OutHits, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);

// 特定のプロファイルを使用してワールドに対してレイをトレースし、最初のブロック ヒットを返します。
UFUNCTION(BlueprintCallable, Category = "Collision", meta = (bIgnoreSelf = "true", WorldContext = "WorldContextObject", AutoCreateRefTerm = "ActorsToIgnore", DisplayName = "Line Trace By Profile", AdvancedDisplay = "TraceColor,TraceHitColor,DrawTime", Keywords = "raycast"))
static ENGINE_API bool LineTraceSingleByProfile(const UObject* WorldContextObject, const FVector Start, const FVector End, FName ProfileName, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);

```


トレースは床の高さを調べる等で使うケースが多いと思います。
以下コード例。

```C++:床の高さを調べる
	// 元の位置
	FVector _Loc = GetActorLocation();

	// 床を調べる
	FVector _St = _Loc + GetActorUpVector() * +500.0f;
	FVector _Ed = _Loc + GetActorUpVector() * -500.0f;
	FHitResult _HitResult;
	TArray<AActor*> ActorsToIgnore;

	UKismetSystemLibrary::LineTraceSingle(this, _St, _Ed,
			UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_WorldStatic),
			false, ActorsToIgnore, EDrawDebugTrace::None, _HitResult, true);

	if (_HitResult.bBlockingHit) {
		// 床の高さに補正
		_Loc = _HitResult.ImpactPoint;
	}
	else {
		// 床が見当たらない？
	}
```



### LineTraceByChannel 
BPでのノードは以下のようになっています。
![LineTraceByChannel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/a74656fb-53dd-3cff-b5c0-41a66c2a43d3.png)


### LineTraceForObjects
引数が少し違います。`ForObjects` では調べるオブジェクトタイプを直接指定する必要があります。
![LineTraceByObjects.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/1c342973-c52b-003e-4d0a-48194db3fd52.png)


### LineTraceByProfile
引数にコリジョンプロファイル名を指定する必要があります。
![LineTraceByProfileBP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/4ccc05a6-0893-eb77-ac6f-82f1554d9d15.png)



## ライン以外のトレース
ライン以外にスフィア、ボックス、カプセルがあり、それぞれ Single/Multi、Channel/ForObjects/ByProfile 版の各メソッドが用意されています。

```C++:KismetSystemLibrary.h
// スフィア
UFUNCTION(BlueprintCallable, Category="Collision", meta=(bIgnoreSelf="true", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore", DisplayName = "Sphere Trace By Channel", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="sweep"))
static ENGINE_API bool SphereTraceSingle(const UObject* WorldContextObject, const FVector Start, const FVector End, float Radius, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);

// ボックス
UFUNCTION(BlueprintCallable, Category = "Collision", meta = (bIgnoreSelf = "true", WorldContext="WorldContextObject", AutoCreateRefTerm = "ActorsToIgnore", DisplayName = "Box Trace By Channel", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="sweep"))
static ENGINE_API bool BoxTraceSingle(const UObject* WorldContextObject, const FVector Start, const FVector End, const FVector HalfSize, const FRotator Orientation, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);

// カプセル
UFUNCTION(BlueprintCallable, Category="Collision", meta=(bIgnoreSelf="true", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore", DisplayName = "Capsule Trace By Channel", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="sweep"))
static ENGINE_API bool CapsuleTraceSingle(const UObject* WorldContextObject, const FVector Start, const FVector End, float Radius, float HalfHeight, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);

```

BPでは以下のようになっています。
![TraceBP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/7d8b2276-dba3-619f-2176-077943611bfb.png)



# UWorld
トレース系メソッドはこちらに実装されています、BPでは呼び出せないのでC++で処理をつくることが必要です。

## 非同期ライントレース
非同期処理のトレース系メソッドが用意されています。非同期なので即時結果を取得することはできませんが、うまく使うとゲームスレッドの処理負荷軽減にもなります。

```C++:World.h
// 非同期用のインターフェース。 パラメータセットはほぼ同じですが、オプションで実行完了時に呼び出されるデリゲートを設定でき、必要に応じて UserData を設定できます。
// デリゲートがない場合は、QueryTraceData または QueryOverlapData を使用してトレース データをクエリできます。データは、リクエストが行われた後の次のフレームでのみ利用可能です。つまり、フレーム X でリクエストが行われた場合、フレーム (X+1) で結果を取得できます。 )
FTraceHandle	AsyncLineTraceByChannel(EAsyncTraceType InTraceType, const FVector& Start,const FVector& End, ECollisionChannel TraceChannel, const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam, const FCollisionResponseParams& ResponseParam = FCollisionResponseParams::DefaultResponseParam, const FTraceDelegate* InDelegate = nullptr, uint32 UserData = 0 );

```

　
同期版トレースとの違いはリクエスト時にトレースハンドル(`FTraceHandle`) が返ってくることと、完了時のデリゲートに処理を登録する必要があることです。
また複数のトレースを実行する場合はハンドルを保持してデリゲートで判別分岐処理等が必要になると思います。
以下サンプルコード。

```C++:TestActor.h
UCLASS()
class SAMPLE_API ATestActor : public AActor
{
	GENERATED_BODY()

// ..省略..

	// トレースデリゲート
	FTraceDelegate	TraceDelegate;
	
	// 完了時イベント
	void OnTraceCompleted(const FTraceHandle& _Handle, FTraceDatum& _Data);
	// トレースリクエスト
	FTraceHandle RequestTrace();

}; 
```

```C++:TestActor.cpp
#include "Engine/World.h"

void ATestActor::BeginPlay()
{
	// トレース完了時処理をバインド
	TraceDelegate.BindUObject(this, &ThisClass::OnTraceCompleted);
 	// トレースリクエスト
	RequestTrace();
}

// トレース完了時処理
void ATestActor::OnTraceCompleted(const FTraceHandle& _Handle, FTraceDatum& _Data)
{ 
	// 結果表示
	for (auto& _It : _Data.OutHits) {
		UE_LOG(LogTemp, Log, TEXT("OutHit : %s"), *_It.GetActor()->GetFName().ToString())
	}
}

// トレースリクエスト
FTraceHandle ATestActor::RequestTrace()
{
	UWorld* _World = GetWorld();
	if (_World == nullptr){	return(FTraceHandle()); }

	FCollisionObjectQueryParams _ObjectQueryParams;
	_ObjectQueryParams.AddObjectTypesToQuery(ECollisionChannel::ECC_Pawn);

	TArray<AActor*> _ActorsToIgnore;
	FCollisionQueryParams _Params(TEXT("ASyncTest"), false);
	_Params.AddIgnoredActors(_ActorsToIgnore);

	auto _St = GetOwner()->GetActorLocation();
	auto _Ed = _St + (GetOwner()->GetActorForwardVector() * 1000.0f);

	// マルチでライントレース
	auto _Handle = _World->AsyncLineTraceByObjectType(EAsyncTraceType::Multi, _St, _Ed, _ObjectQueryParams, _Params, &TraceDelegate);
	return(_Handle);
}

```

トレースデリゲートはラムダ式でバインドもできます。
以下サンプルコード。

```C++:トレースデリゲートをラムダ式でバインドする
	FVector _St = /* 開始位置 */
	FVector _Ed = /* 終了位置 */

	FCollisionObjectQueryParams _ObjectQueryParams;
	_ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldDynamic);
	FCollisionQueryParams _TraceParams;
	_TraceParams.bTraceComplex = false;

	// トレースデリゲート
	FTraceDelegate _TraceDelegate;
	_TraceDelegate.BindLambda([](const FTraceHandle& _Handle, FTraceDatum& _Data)
	{
		if (_Data.OutHits.Num() > 0){
			// ヒットした場合の処理
			const FHitResult& _Hit = _Data.OutHits[0];
			UE_LOG(LogTemp, Log, TEXT("Hit Actor: %s"), *_Hit.GetActor()->GetName());
		}
	});

	// 非同期トレースを行う
	auto _Trace = GetWorld()->AsyncLineTraceByObjectType(EAsyncTraceType::Single, _St, _Ed, _ObjectQueryParams, _TraceParams, &_TraceDelegate);
	
```





## 非同期スフィアトレース
ライン以外のスフィア、ボックス、カプセルのトレースは `AsyncSweep???`メソッドで行います。形状設定は `FCollisionShape`で指定します。

```C++:非同期スフィアトレース

FCollisionShape _MySphere = FCollisionShape::MakeSphere(100.0f);
ECollisionChannel _TraceChannel = ECollisionChannel::ECC_Pawn;
FCollisionQueryParams _Params(TEXT("ASyncTest"), false);

auto _Handle = GetWorld()->AsyncSweepByChannel(EAsyncTraceType::Multi, _St, _Ed, FQuat::Identity, _TraceChannel, _MySphere, _Params, FCollisionResponseParams::DefaultResponseParam, &TraceDelegate);

```


## コリジョンオーバーラップテスト
特定形状のコリジョンをオーバーラップテストをするメソッドもあります。トレースと違って移動はなしで衝突したコンポーネントでコリジョンイベントが発生します。


```C++:World.h
// 特定のチャネルを使用して、指定された位置でシェイプの衝突をテストし、ブロックまたは重複するシェイプが見つかった場合は返します。
bool OverlapAnyTestByChannel(const FVector& Pos, const FQuat& Rot, ECollisionChannel TraceChannel, const FCollisionShape& CollisionShape, const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam, const FCollisionResponseParams& ResponseParam = FCollisionResponseParams::DefaultResponseParam) const;

// 特定のチャネルを使用して、指定された位置でシェイプの衝突をテストし、オーバーラップするコンポーネントのセットを決定します。
bool OverlapMultiByChannel(TArray<struct FOverlapResult>& OutOverlaps, const FVector& Pos, const FQuat& Rot, ECollisionChannel TraceChannel, const FCollisionShape& CollisionShape, const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam, const FCollisionResponseParams& ResponseParam = FCollisionResponseParams::DefaultResponseParam) const;

```


# まとめ
ライントレース以外はあまり使わないのでまとめ。非同期系はゲームスレッドの処理負荷軽減のためにも使っていきたいところです。