Gaming Life

一日24時間、ゲームは10時間

UE4 マテリアルとRenderTargetを使ってライフゲームを作ってみる

一体どこに需要があるのかわからないが、マテリアルをとRenderTargetを使ってライフゲームを作ってみた。

wikipediaだったり検索すればすぐに分かりやすい説明が見つけられるので、ここではライフゲームがなんぞやという説明はしない。

バージョン

UE4.21.2

概要

パラメータとしてTextureを与えると、次のタイムステップのライフゲームの結果を吐き出すマテリアルを作成。

ブループリントでRenderTargetを2つ(RT1とRT2とする)用意し、最初に上のマテリアルで計算した結果をRT1、RT2に書き込む。

続けて、

  • RT2をマテリアルのパラメータに与えその結果をRT1に書き込む
  • RT1をマテリアルのパラメータに与えその結果をRT2に書き込む

をTickを使って交互に実行することでライフゲームの時間軸を進めていく。

マテリアルの中身

  • M_LifeGame

f:id:ai_gaminglife:20190202133223p:plain

注意はBase Colorに繋ぐのではなくEmmisive Colorに繋がねばならないこと。理由は後述。

  • MF_8NeighbourPixel

f:id:ai_gaminglife:20190202133236p:plain

f:id:ai_gaminglife:20190202133249p:plain

今見ているピクセルの8近傍ピクセルのR値を取得する関数。

キモはTexel SizeノードとTexCoord。

Texel SizeはTexture Propertyと検索すれば使えるノードで、入力として与えたテクスチャのピクセルサイズを取得することができる。

TexCoordには今見ているピクセルのUV座標が入っている。

  • MF_8NeghbourLifeGame

f:id:ai_gaminglife:20190202133258p:plain

MF_8NeighborPixelで取得した8近傍ピクセルのR値を四捨五入。それと現在のピクセルのR値を使って現在のピクセルの生存・死亡・誕生をCustomノードを使って判定する。

今回採用したライフゲームのルールは、

  • 今死んでいる時周囲のピクセルがちょうど3つ生きていれば誕生する
  • 今生きている時周囲のピクセルが2か3つ生きていれば生存する
  • 今生きている時周囲のピクセルが1つ生きていれば過疎で死亡する
  • 今生きている時周囲のピクセルが4つ以上生きていれば過密で死亡する

Customノードの中身は以下の通り。

float1 count = 0;
if(N1 == 0){count++;}
if(N2 == 0){count++;}
if(N3 == 0){count++;}
if(N4 == 0){count++;}
if(N5 == 0){count++;}
if(N6 == 0){count++;}
if(N7 == 0){count++;}
if(N8 == 0){count++;}

if(thisPixel==0)
{
    if(count <= 1)
    {
        return float3(1.0, 1.0, 1.0);
    }
    if(count >= 4)
    {
        return float3(1.0, 1.0, 1.0);
    }
    else
    {
        return float3(0.0, 0.0, 0.0);
    }
}
else
{
    if(count == 3)
    {
        return float3(0.0, 0.0, 0.0);
    }
    else
    {
        return float3(1.0, 1.0, 1.0);
    }
}

ブループリントの中身

  • 構成

Static Mesh Actorを継承し、MeshにPlaneなりBoxなりを割り当てる。マテリアルには先程作成したM_LifeGameを設定。

ライフゲームの初期状態として与えるTexture2D型の変数、InitialTextureを用意。Instance Editableにチェックを入れて置くと便利。

  • Construction Script

f:id:ai_gaminglife:20190202133309p:plain

メッシュのDynamic Material Instanceを作成し変数LifeGameDMIとして保存しておく。続けてSet Texture Parameter Valueを使ってライフゲームの初期状態を与える。

  • Event Begin Play

f:id:ai_gaminglife:20190202133318p:plain

初期状態テクスチャと同じサイズのレンダーターゲットを2枚作成。それぞれにLifeGameDMIの結果を書き込む。

ここにハマりポイントが2つある。

1つはCreate Render Target 2Dするタイミング。

この関数をConstruction Scriptで実行すると正しく動作しないので、必ずBeginPlayで作成する。

もう1つはレンダーターゲットに描くマテリアルについて。

Draw Material to Render TargetするのはEmissive Colorの結果のみ。Base Colorの結果は一切関係ない。先程ライフゲームの結果は必ずEmissive Colorに繋がなければいけないといったのはこういうこと。

  • Event Tick

f:id:ai_gaminglife:20190202133335p:plain

FlipFlopを使って交互に実行する。

最後にClass SettingでTickが呼ばれる回数を好みに設定して完成。

まとめ

Customノードの中身を変えれば簡単に他のルールのライフゲームを再現できそう。Draw Material to Render Targetはかなり面白いことができるのでもうすこし遊んでみたい。

参考

qiita.com