[UE4] CrowdManagerによる群衆の制御


# 1. はじめに
[検証バージョン：4.22.0]
　CrowdManagerはEngineが提供するAI/Navigation機能の1つです。この機能についてはドキュメント等で説明されていないため、ここで少し内容を掘り下げています。この機能は多くのエージェント(AIで動作するキャラクター)を管理する際に有用です。
　以下のGifはCrowd機能のイントロダクションとして利用していないケースと利用したケースを比較したものです。各エージェントはNavmesh上を無作為に移動するような振る舞いをしています。Crowd機能なしの場合はエージェント同士が衝突し互いの移動を阻害しています。Crowd機能ありの場合はエージェント同士が自律して巡回を行い、回避するようなパスを計算して動作します。


**Crowd機能なし**
![CrowdOff.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/e0d6e69e-ba78-e025-25e4-9e435a8ab28c.gif)

**Crowd機能あり**
![CrowdOn.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/3e54d674-ea14-9e2c-baab-6563816a3339.gif)

# 2. 概要
![2019-04-12_20h02_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/d31256e3-f499-3cc2-1e6a-5978b4f2e860.png)
　CrowdManagerの概要は最初に説明した通りですが、多くのエージェントを制御するのに適した機能です。エージェント同士の回避動作を制御するため、Playerに対しては適用されません。また、CharacterMovementComponentには衝突回避機能としてRVO(Reciprocal Velocity Obstacles)を備えていますが、**精度や負荷においてはCrowdを利用した方が若干優れています**(設定にも依存します)。

# 3. 構成
![2019-04-14_01h17_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/7972d63c-4161-a3cf-fd71-00477d385237.png)
　CrowdManagerとその関連クラスについて示したものが上記となります。それぞれのクラスの役割については以下の通りです。

**DetourCrowdAIController**
Crowd機能をサポートするCrowdFollowingComponentを所有したAIControllerの継承クラスです。このControllerを所有するPawnはCrowdManagerによる迂回機能を利用することができます。

**CrowdFollowingComponent**
Crowd機能をサポートするComponentです。このComponentをControllerとして持つPawnはCrowd対象のエージェントとして登録され、CrowdManagerによって迂回機能を管理されます。

**CrowdManager**
Engine側でCrowdを管理するクラスです。NavigationSystemやDetorと連動して迂回パスを提供したり、エージェント同士のやりとりを管理します。

**Detor(dtCrowd)**
実際にCrowdを制御するための巡回パスなどを計算する実装クラスです。このモジュールはRecast機能を使用することを前提とした機能です。

**NavigationSystem**
CrowdManagerクラスを管理するためのNavigation機能を提供するクラスです。CrowdManagerの生成、破棄や、パス情報の提供を担います。

# 4. 使い方
![2019-04-12_20h02_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/7f721e53-9c23-d564-aed5-853f1b4b9772.png)
　Crowd機能を利用するためのシンプルな設定方法は、PawnのAIControllerに`DetourCrowdAIController`を設定するだけです。もし独自のAIControllerにCrowd機能を持たせる場合は、`CrowdFollowingComponent`を継承したComponentをAIControllerのConstructerでDefaultSubObjectとして設定するだけです。また、CrowdFollowingComponentにはMovementComponentと連動するため、PawnにはCharacterMovementComponentを設定しておきます。

```cpp:MyAIController.cpp
#include "MyAIController.h"
#include "Navigation/CrowdFollowingComponent.h"
#include "MyCrowdFollowingComponent.h"

AMyAIController::AMyAIController(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer.SetDefaultSubobjectClass<UMyCrowdFollowingComponent>(TEXT("PathFollowingComponent")))
{

}
```
　この設定によりCrowdManagerに対象のエージェントが登録されて、Crowd機能で動作するようになります。
もしカスタムMovmentClassを作成する場合は以下のようになります。

```cpp:MyCrowdFollowingComponent.h
#pragma once

#include "CoreMinimal.h"
#include "Navigation/CrowdFollowingComponent.h"
#include "MyCrowdFollowingComponent.generated.h"

UCLASS()
class MYPROJECT_API UMyCrowdFollowingComponent : public UCrowdFollowingComponent
{
	GENERATED_BODY()
	
};

```

# 5. 設定
![2019-04-13_23h23_57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/76a33637-3428-c61d-6608-919c0a646d06.png)

　Crowd機能に関してはProjectSettingsから設定を行うことができます。独自のCrowdManagerクラスを設定したり、Crowd機能が提供する迂回機能のパラメータを調整する場合はここから設定します。

**Project Settings詳細**

**Engine - Navigation System**

| 項目     		|説明    |
|:-----------------	|:------------------|
| Crowd Manager Class	|独自のCrowdManagerクラスを設定|

**Engine - Crowd Manager**

| 項目     		| 				|説明    |
|:-----------------	|:------------------		|:------------------|
| Avoidance Config	| 				| 障害回避パラメータ<br>(ECrowdAvoidanceQuality毎)|
|			|Velocity Bias			| 速度バイアス|
|			|Desired Velocity Weight	| 希望の速度へのスケール|
|			|Current Velocity Weight	| 現在の速度へのスケール|
|			|Side Bias Weight		| サイドバイアスへのスケール|
|			|Impact Time Weight		| サイドペナルティへのスケール|
|			|Impact Time Range		| 衝突時間ペナルティへのスケール|
|			|Custom Pattern Idx		| サンプリングパターン配列のインデックス|
|			|Adaptive Divisions		| サンプリングにおけるリングあたりの分割数|
|			|Adaptive Rings			| サンプリングにおけるリング数|
|			|Adaptive Depth			| サンプリングにおける最高速度での反復回数|
| Sampling Patterns	| 				| 障害回避サンプリング設定|
|			|Angle				| サンプリングパターン角度(Deg)|
|			|Radii				| サンプリングパターン半径|
| Max Agents			| 				| 管理可能なエージェント数の上限|
| Max Agent Radius		| 				| 管理可能なエージェントの半径上限|
| Max Avoided Agents		| 				| 速度回避のための隣接エージェントの最大数|
| Max Avoided Walls		| 				| 速度回避のための壁セグメントの最大数|
| Navmesh Check Interval	| 				| NavMeshから離れた時のポーリング間隔(s)|
| Path Optimization Interval	| 				| エージェントパス最適化間隔(s)|
| Separation Dir Clamp		| 				| 隣接エージェントとの分離力強度|
| Path Offset Radius Multiplier	| 				| コーナーパス補正用エージェント半径乗数|
| Resolve Collisions		| 				| エージェント間コリジョンをCrowdで解決するか|

これらのパラメータは以下のリンクでも説明されています。
[[Answer Hub] Crowd Manager Avoidance Config](https://answers.unrealengine.com/questions/212408/crowd-manager-avoidance-config.html)

# 6. デバッグ
![2019-04-12_20h03_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/b0e2d794-789b-f031-dfa5-87e3ecdc8ce6.png)
　CrowdManagerを始めとしたAI関連の機能はVisual Loggerからデバッグすることができます。Visual Loggerはメニューの[Window]->[Developer Tools]->[Visual Logger]から開きます。Visual LoggerからPIEなどで実行した動作をレコードし、その時のCrowdのパス検索情報などをデバッグすることができます。

# 7. まとめ
　CrowdManagerに関するAI機能について簡単に紹介してきましたが、ドキュメントで紹介されていない機能が他にもたくさん存在していきます。これらの機能を適切な箇所で利用することで、より効率的にコンテンツを制作できることに繋がるため、今後も紹介してきたいと思います。