UE4 パーティクルシステムについてのメモ

# 概要
UnrealEngine の ParticleSystem についてのメモです。


## 環境
Windows10
Visual Studio 2017
UnrealEngine 4.25

## 参考
以下を参考にさせて頂きました、ありがとうございます。

[UE公式 : パーティクル システムのリファレンス](https://docs.unrealengine.com/ja/Engine/Rendering/ParticleSystems/Reference/index.html)
[【UE4】Particle Instance Parameter について（解説）](https://qiita.com/Dv7Pavilion/items/0d867d260a5b92ea3f5a)



# 関連ソース
"Engine\Source\Runtime\Engine\Classes\Particles\ParticleSystem.h"
"Engine\Source\Runtime\Engine\Classes\Particles\ParticleSystemComponent.h"



# パーティクルを発生させる
## コンポーネントを作成してパーティクルを指定する
パーティクルアセットの指定やアタッチ座標等を全て指定する必要があります。
`AttachToComponent` で指定したコンポーネントにアタッチされます。

![SpawnEffect0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/b2ac1433-c144-b811-7a24-7af7504b19e6.jpeg)


## 作成済のパーティクルシステムコンポーネントに対して発生させる
パーティクルを指定してから明示的に発生させます。コンポーネントがアクティベートしていない場合は、`Acticateノード`も繋ぐ必要があります。
発生位置などは一緒で、条件によってパーティクルアセットが違う場合にはこの方法がよいと思われます。

![SpawnEffect1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/2916063e-8f46-afea-bb16-c4d309c74590.jpeg)


## パーティクルアセットをドロップする
ブループリントのイベントグラフにパーティクルアセットをドロップするとそのままコンポーネント追加ノードが作成されます。ルートコンポーネント以下にアタッチされるようです。
パーティクル1個で、アセットも決まっているような生成などはこれが一番楽です。

![DirectAsset.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/06fcc5d2-f1db-a720-577b-6ac46c212cd9.jpeg)



# C++での制御について
## パーティクルシステムコンポーネントを生成する

```cpp:ATestActor.h
UCLASS()
class TEST_API ATestActor : public AActor
{
	GENERATED_BODY()
	
	// コンストラクタ
	ATestActor(const FObjectInitializer& ObjectInitializer);

	// ..省略..

	// シーンコンポーネント
	UPROPERTY(Category = "Test", VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
	class USceneComponent*		SceneComponent;

	// パーティクルシステムコンポーネント
	UPROPERTY(Category = "Test", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
	class UParticleSystemComponent*	PSC;
};

```

```cpp:ATestActor.cpp

// コンストラクタ
ATestActor::ATestActor(const FObjectInitializer& ObjectInitializer)
{
	// シーンコンポーネント作成
	SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent0"));
	if( SceneComponent ){
		RootComponent = SceneComponent;
	}

	// パーティクルシステムコンポーネント作成
	PSC = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ParticleSystemComponent0"));
	if(PSC){
		PSC->SetupAttachment(RootComponent);
		PSC->SecondsBeforeInactive = 1.0f;
		PSC->bAutoActivate = false;
	}
}

```


## エフェクトを発生させる
コンストラクタでパーティクルアセットを取得後、任意のタイミングでパーティクルを発生させます。パーティクルシステムコンポーネントを作成する場合と、予め作成済のパーティクルシステムコンポーネントを使う場合です。
以下コード例。

```cpp:ATestActor.h
UCLASS()
class TEST_API ATestActor : public AActor
{
	GENERATED_BODY()

	// 省略..

	// パーティクル
	class UParticleSystem* Particle;
};
```

```cpp:ATestActor.cpp
#include "ATestActor.h"
#include "Engine.h"
#include "ParticleHelper.h"
#include "Particles/ParticleSystem.h"
#include "Particles/ParticleSystemComponent.h"	
#include "Runtime/CoreUObject/Public/UObject/ConstructorHelpers.h"

// コンストラクタ
ATestActor::ATestActor(const FObjectInitializer& ObjectInitializer)
{
	// パーティクルアセットを探す
	ConstructorHelpers::FObjectFinder< UParticleSystem > find_eff(TEXT("ParticleSystem'/Game/Particles/P_Fire.P_Fire'"));
	if (find_eff.Succeeded()) {
		Particle = find_eff.Object;
	}
}

void ATestActor::BeginPlay()
{
	Super::BeginPlay();

	// 作成例１
	// コンポーネントを作成してパーティクルを設定し発生させる
	UParticleSystemComponent* _PSC = UGameplayStatics::SpawnEmitterAttached(Particle, RootComponent, NAME_None, FVector(0.0f, 0.0f, 0.0f), FRotator(0.0f, 0.0f, 0.0f));


	// 作成例２
	// 作成済パーティクルシステムコンポーネント(PSC)に対して発生させる
	if(PSC){
		// アクティベート
		PSC->Activate();
		// パーティクルを設定
		PSC->SetTemplate(Particle);
		// エミッタを有効に
		PSC->SetEmitterEnable(NAME_None, true);
	}
	
}
```

### 備考
GameplayStaticsに用意されているメソッドは以下の様になっています。

```cpp:GameplayStatics.h

// スケール指定なしのC++用SpawnEmitterAtLocation下位互換バージョン
static UParticleSystemComponent* SpawnEmitterAtLocation(const UObject* WorldContextObject, UParticleSystem* EmitterTemplate, FVector Location, FRotator Rotation, bool bAutoDestroy, EPSCPoolMethod PoolingMethod = EPSCPoolMethod::None, bool bAutoActivateSystem =true);

// 指定された位置と回転で指定されたエフェクトを再生します。 エフェクトが完了するとシステムは消えます。 複製しません。
static UParticleSystemComponent* SpawnEmitterAtLocation(UWorld* World, UParticleSystem* EmitterTemplate, const FTransform& SpawnTransform, bool bAutoDestroy = true, EPSCPoolMethod PoolingMethod = EPSCPoolMethod::None, bool bAutoActivate = true);

// スケール指定なしのC++用SpawnEmitterAttached 下位互換バージョン
static UParticleSystemComponent* SpawnEmitterAttached(class UParticleSystem* EmitterTemplate, class USceneComponent* AttachToComponent, FName AttachPointName, FVector Location, FRotator Rotation, EAttachLocation::Type LocationType, bool bAutoDestroy = true, EPSCPoolMethod PoolingMethod = EPSCPoolMethod::None, bool bAutoActivate=true);

```


# Particle Instance Parameter でパーティクルカラー変更テスト

## 準備
パーティクルエミッタにモジュールを追加する
[右クリック] -> [Color] -> [Scalar Color / Life]

![Add_Module.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/4af2c513-2e3c-975b-5382-32cd93084955.jpeg)

以下の様に追加されます。
![Add_ScaleColorLife.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/7fe4f463-0563-5c66-2bc2-6b835c6906b3.jpeg)


詳細設定を変更する
`Distribution` を `Distribution Vector Particle Parameter` に変更する
`Parameter Name` にパラメータ名前を入れる
`Param Modes` を各 `DPM Direct` に変更する

![Detail.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/4c381c5b-bd65-6546-2b1b-9718b197e383.jpeg)


パーティクルシステムコンポーネントの詳細でパラメータを公開する
エミッタアクションの `パラメータを公開` でパーティクルのパラメータを反映させる
`ParamType` を `Color` にする

![PSC_Detail.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/94caa9bd-6278-da38-3738-2024f35beb87.jpeg)

## 実行テスト
以下のようなBPで実行テストを行います。
キー[1]押下でパーティクルを発生させて、キー[2]でそのパーティクルのパラメータ `TestParam` のカラーパラメータを変更することができます。

![sample_BP.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/b455ce1c-d8c6-c619-86b7-34a09734b882.jpeg)

発生時は青のパーティクルですが、パラメータを適用すると黄色に変化します。

## 備考
C++で同様にパラメータを変化させることもできます。
以下コード例

```cpp:.cpp
	// パーティクルシステムコンポーネントPSCに適用されている TestParam というパラメータにカラー値を入れる
	PSC->SetColorParameter(FName("TestParam"), FLinearColor(0.0f, 1.0f, 1.0f));
```

エンジンコードを見ると他にインスタンスパラメータを取得できるメソッドが確認できます。インスタンスパラメータの `ParamType` に対応しているようです。

```cpp:ParticleSystemComponent.h
// Float型パラメータ
UFUNCTION(BlueprintCallable, Category="Effects|Components|ParticleSystem")
virtual void SetFloatParameter(FName ParameterName, float Param) {}

// Vector型パラメータ
UFUNCTION(BlueprintCallable, Category="Effects|Components|ParticleSystem")
virtual void SetVectorParameter(FName ParameterName, FVector Param) {}

// Color型パラメータ
UFUNCTION(BlueprintCallable, Category="Effects|Components|ParticleSystem")
virtual void SetColorParameter(FName ParameterName, FLinearColor Param) {}

// Actor型パラメータ
UFUNCTION(BlueprintCallable, Category="Effects|Components|ParticleSystem")
virtual void SetActorParameter(FName ParameterName, class AActor* Param) {}

// Material型パラメータ
UFUNCTION(BlueprintCallable, Category="Effects|Components|ParticleSystem")
void SetMaterialParameter(FName ParameterName, class UMaterialInterface* Param);

```

# パーティクルのパフォーマンス設定について

パーティクルのパフォーマンス設定
![performance.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/92f5b6fc-0db5-889c-2575-ae8640b12d44.jpeg)


## C++

```cpp:.cpp
// エミッタの重要度の変更
PSC->Template->MaxSignificanceLevel = EParticleSignificanceLevel::Low;

// Tickの最小時間の変更(66msec = 15FPS)
PSC->Template->MinTimeBetweenTicks = 66;

```

適正な設定をすることで処理負荷軽減が期待できます。
可能なら動的な設定をするとよりよいかもしれません。
`Max Significance Level` はカリングの優先度を決めるパラメータのようです。


# まとめ
パーティクル制御のためのメモ書きなのでまとまりがありません。
処理負荷周りについてもう少し調べたいところです。