雑食性雑感雑記

知識の整理場。ため込んだ知識をブログ記事として再構築します。

ProcedualMeshComponent で波打つメッシュを作ってみた

最近はお仕事で UE4 を扱ってます。
その中で ProcedualMeshComponent を扱う機会があったので、その習作。波打つメッシュを作ってみました。

ProcedualMeshComponent

ProceduralMeshComponent | Unreal Engine Documentation
簡単には動的にメッシュを生成したり動かしたりする仕組みです。
作るためにはメッシュの約束事を覚える必要がありますが、フラグ一つで collision つけたりもできるので覚えると便利そう。

検索すると簡単な使い方紹介しているブログがいくつか見つかるので、詳細は省略。

こんなの作った。

作ったサンプルプロジェクトは github に置きました。( C++ プロジェクトです )
github.com

f:id:kazuki_nagasawa:20200409154923g:plain
sin カーブで波打つサンプルです。色をくるくる変えてます。

f:id:kazuki_nagasawa:20200409155033g:plain
Collision つけてオブジェクト乗せたりも可能。
( トランポリン的なものも作れるかも? )

技術的要点

( 以下添付のコードは説明用。一部が省略されています。詳しいコードは github 見てください。)

メッシュ構造

Initialize() でメッシュ頂点の定義をまとめて行っていますが、Triangle の通り、正方形 (0 -> 1 -> 2, 0 -> 2 -> 3) を一つの単位としています。

void AWaveActor::Initialize()
{
	int MeshIndexSize = (this->XMax - 1) * (this->YMax - 1) * 4;

	int midx;
	for (int my = 0; my < this->YMax - 1; my++) {
		for (int mx = 0; mx < this->XMax - 1; mx++) {
			midx = this->GetMeshIndex(mx, my);

			this->Triangles.Add(0 + midx * 4);
			this->Triangles.Add(1 + midx * 4);
			this->Triangles.Add(2 + midx * 4);
			this->Triangles.Add(0 + midx * 4);
			this->Triangles.Add(2 + midx * 4);
			this->Triangles.Add(3 + midx * 4);
		}
	}
}

各頂点は Tick() で動く UpdateMesh() で。

void AWaveActor::UpdateMesh(float DeltaTime)
{
	this->ElapsedTime += DeltaTime;

	TArray<FVector> Vertices;
	TArray<FLinearColor> VertexColors;

	float OffsetX = -this->SizeBase * this->cx;
	float OffsetY = -this->SizeBase * this->cy;

	// Calc per mesh
	FLinearColor Color = GetColor(this->ElapsedTime);
	float SizeBaseHalf = this->SizeBase / 2.0;
	float x0, x1, y0, y1;
	for (int my = 0; my < this->YMax - 1; my++) {
		for (int mx = 0; mx < this->XMax - 1; mx++) {
			x0 = this->SizeBase * (mx - this->cx) + SizeBaseHalf;
			x1 = x0 + this->SizeBase;
			y0 = this->SizeBase * (my - this->cy) + SizeBaseHalf;
			y1 = y0 + this->SizeBase;

			Vertices.Add(FVector(x0, y0, this->GetHeight(mx + 0, my + 0)));
			Vertices.Add(FVector(x0, y1, this->GetHeight(mx + 0, my + 1)));
			Vertices.Add(FVector(x1, y1, this->GetHeight(mx + 1, my + 1)));
			Vertices.Add(FVector(x1, y0, this->GetHeight(mx + 1, my + 0)));

			for (int c = 0; c < 4; c++) {
				VertexColors.Add(Color);
			}
		}
	}
}

と、正方形の各頂点を DeltaTime に従った値で更新しています。

メッシュの作成、更新

メッシュの作成は CreateMeshSection_LinearColor で行っています。第一引数の 0 はメッシュのインデックスの様で、これを増やしていけば複数レイヤに書き込みができるっぽいです (未検証 )。
更新は UpdateMeshSection_LinearColor の方で、対応するインデックスのメッシュの更新を行ってます。
( 全替えしてるのに処理的に重くないのかな…?いちおうサクサク動いてます。 )

void AWaveActor::UpdateMesh(float DeltaTime)
{
	( 中略 )

	if (this->IsCreated) {
		mesh->UpdateMeshSection_LinearColor(0, Vertices, Normals, UV0, VertexColors, Tangents);
	}
	else {
		this->IsCreated = true;
		mesh->CreateMeshSection_LinearColor(0, Vertices, Triangles, Normals, UV0, VertexColors, Tangents, this->IsCollision);
		mesh->ContainsPhysicsTriMeshData(this->IsCollision);
		mesh->CreateDynamicMaterialInstance(0, this->MaterialInterface);
	}
}

メッシュ可変色

ただ色を変えて VertexColors に入れ込むだけでは色は変わりませんでした。
VertexColor を入力にもつ Material を作成し、それを CreateDynamicMaterialInstance() で設定してあげる必要があります。
f:id:kazuki_nagasawa:20200409155405p:plain

まとめ

  • ProcedualMeshComponent で遊んでみました。
    • プログラマブルに動きのあるものを作ってみるのは面白そうです。
    • 応用例を考えるのが大変そうですが…。