[3ds Max][TyFlow]TyFlowでGradient Ramp Textureをランダムなパーティクルエイジに同期させてマッピングする方法　Gradient Ramp Texture Mapping with sync random Particle Age

# はじめに
今回は、3dsMaxのプラグインTyflowのGradient Ramp Textureをランダムなパーティクルエイジに同期させてマッピングする方法をご紹介します。

パーティクルエイジがランダムでなければ、完全なノンプログラミングで実現できるのですが、いろいろ調べてみたら、結局ScriptOperatorでスクリプトを書かないと出来なさそうでしたので、現状自分がわかっている範囲の対処法のご紹介となります。

そして、最初に対処法を書いて、記事の後半で詳細や注意点をご紹介します。
お急ぎの方で基本的にTyflowのMappingOperatorを知っている方は、記事の前半だけ読んでいただければ大丈夫なようにしました。

また、自分も調べている最中で、もっといい方法などありましたらお教えいただけると助かります。

## ScriptOperater内のスクリプト


まずは、TyflowのMappingOperatorの使い方がわかっている方のためにご紹介します。
TyflowのMappingOperatorはCustomPropertyのfloatにそって、マッピングすることが出来ますが。
肝心のCustomPropertyが、ランダムにパーティクルエイジに従って0から1.0になるようなCustomPropertyを設定することが出来ません。
それをする際には、ScriptOperatorを使用するしかありません。
※現状自分が調べた範囲では
今回ご紹介するScriptOperatorの内部のスクリプトがこちらになります。

```C#:NormalizeRandomParticleAGe.tfs
public void simulationStart()
{	
}
public void simulationStep()
{		
	for (int i = 0; i < eventParticleCount; i++) 	
	{
		int sInx = tf.GetSimIndex(i); 
		tf.SetSeed(sInx,Mathf.RoundToInt(GetFloat("seed")));
		if(tf.GetCustomFloat(sInx,"particleDeadAge") == 0 )
		{
			float minvalue = GetFloat("particleAge") - GetFloat("RandomRange") ,maxvalue = GetFloat("particleAge") + GetFloat("RandomRange");
			float pdAge =tf.GetRandFloat( sInx, minvalue ,maxvalue ) ;
			tf.SetCustomFloat( sInx,"particleDeadAge", pdAge);
		}
		float particleNormalizeAge =  tf.GetEventAge(sInx) / tf.GetCustomFloat(sInx,"particleDeadAge") ; 
		tf.SetCustomFloat(sInx,"particleNormalizeAge",particleNormalizeAge);
	}	
}
public void simulationStepThreaded(int startInx, int endInx, int threadInx)					
{	
}
public void postSimulationStep()
{	
}
```

こちらをScriptOperatorの内部にコピペ。
以下の画像のように。
ScriptOperatorの下のロールアウトFloatを上から三つのチェックボックスをオンに設定。
上から[particleAge][RandomRange][seed]と記入。

![スクリーンショット 2022-10-11 142046.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1690940/241dc7b4-3cf9-d762-f311-2d90102b1404.jpeg)

これで、TyflowのCustomPropertyの[particleNormalizeAge]のチャンネルが作成されるので、そちらに沿ってマッピングされるので、それをマッピングに設定すればOKです。

ScriptOperatorのFloatで設定したパラメータの概要は次の通りです。

<d1>
 <dt>[particleAge]</dt><dd>基本となるパーティクルエイジです。こちらは正確にはparticleAgeではなくEventAgeとなっています。</dd>
<dt>[RandomRange]</dt><dd>基本となるパーティクルエイジのランダムのレンジとなります。0にすれば一律同じパーティクルエイジになりますし、上記の画像のように5に設定すれば、基本のパーティクルエイジの+-5で設定されます。</dd>
<dt>[seed]</dt><dd>ランダムシードになります。乱数を変えたければこちらを調整してください。</dd>

なので、画像で言うとランダムシードは1234でパーティクルエイジ50+-5で0から1.0になるCustomPropertyの[particleNormalizeAge]を設定することが出来ます。

## スクリプトがわからない、MappingOperatorの使い方がいまいちわからない方へ

スクリプトがわからない、MappingOperatorの使い方がわからない方は、上記のScriptOperatorを設定してください。
一連の詳細はYoutubeの動画でご紹介してますので、そちらをご参考にしていただければ大丈夫かと思います。
YouTubeのリンクはこちら。

https://youtu.be/TtqXtwIPepk

ScriptOperatorを設定し終えたら以下の記事のように設定してください。
以下の画像のようにPropertyTestOperatorとMappingOperatorを設定する事で、ランダムなパーティクルエイジでグラデーションランプテクスチャをパーティクルエイジに沿って貼ることが出来ます。

#### PropertyTestOperatorの設定
![スクリーンショット 2022-10-11 150032.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1690940/e51d19fd-3016-4f5d-1536-b7c1a60d1bbe.jpeg)
設定したいEventにPropertyTestOperatorを追加して、画像の赤線の箇所のパラメーターを画像のように設定してください。
画像ではPropertyTestの送り先でDeleteしていますが、もちろん好きなようにOperatorを組んでももらえれば大丈夫です。

#### MappingOperatorの設定
![スクリーンショット 2022-10-11 150903.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1690940/8aba32a6-f57f-c9e6-55e4-291f9bec3ab4.jpeg)
MappingOperatorを追加し、TimingをContnuousに設定。
<font color="Red">__※忘れがち注意__</font>

![スクリーンショット 2022-10-11 150943.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1690940/b6d915ae-d3ee-34ef-80b8-747fae261c72.jpeg)
MappingFromDataのロールアウトをMapping from custom floatをオン、channelを[particleNormalizeAge]に設定。

設定は以上です。
後は、tyflowに好きなマテリアルとグラデーションランプテクスチャを貼って、レンダリングすればOKです。
![スクリーンショット 2022-10-11 152802.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1690940/028c993e-960f-a3e6-335e-2144e995f226.jpeg)


## 今回のチュートリアルの注意点

### TyflowのCustomPropertyに計算式は入れられない
こういったノードベースは大体CalculatorOperatorやMathOperatorなど、特定のパラメータに対して計算式を入れて、その結果を保存すると言ったオペレーターやノードがあったりするのですが、Tyflowの場合はそういったものなく、あったとしても提供しているパラメータを設定したり、判定したりするにとどめてあります。
おそらくユーザー入りやすさを優先しての開発思想のもと、そういったものは用意しないスタンスをとっているのだと思います。
もちろんCustomProperty内部で他のパーティクルの距離を測ったりなど、Tyflow内部で用意されている機能があれば、そちらに頼るのがいいかと思いますが。
それらが用意されてない計算をしたい場合は、簡単な計算であっても、もうScriptOperatorに頼るほかなさそうです。


### TyflowのScriptOperatorはC#
TyflowのScriptOperatorはMaxScirptではなく、C#で記述する仕様になっています。
なので、MaxScriptしかやってないよっという方は戸惑う方が多いかと思います。
他の言語と比べて比較的似ている言語なので、まだいいのですが。
一応MaxScriptしかやってないよという方は、私が感じている大きな違い4点、ここを意識すると比較的入りやすいかと思います。
C#の詳細な書き方についてはそれぞれ調べてください。

* 大文字小文字は区別する。
* 変数は必ず型を宣言する。
* コマンドの終わりに必ず[;]セミコロンを入れる。
* 関数は終わりに（）をつける。


```maxscrpt:sample.ms
Test = "Hallo world"
print test
-- Hallo world
```

```C#:sample.cs
string Test = "Hallo world";
Print(Test);
//Hallo world
```

### HelpはScriptOperatorのScirptEditorのAPIから

ScriptOperatorはマニュアルらしいマニュアルがなく、ScriptOperatorのScirptEditorのAPIから確認できる、コメントアウトしたリファレンスぐらいしかないです。
私の方もこれを探すのにすこし時間がかかったのと、そのリファレンスをまるまるコピーした記事とどうやって表示するかを記事にしましたので、こちらをご確認ください。

https://qiita.com/fudieY/items/5f39217e77e77e9dd3d0

### C#標準のRandomは使わず、TyflowのAPIのランダムを使用する

日本のgoogleでC#の乱数を検索と半分以上はUnityのRandom.Randが当たってしまいます。
こちらは、Unityが提供するランダム生成のなので、もちろんTyflowのScript内では使用できません。
直ぐ代替案として、出てくるのがC#標準のRandomなのですが、これがすごくランダムじゃないです。
調べてみたらTyflowのScriptOperatorが提供する乱数生成のメソッドがあるので、そちらを使用するといい感じです。
上記のリンクからも見れますが、こちらにもそのメソッドを記載しておきます。

<b> [tyFlow] CLASS PROPERTIES  </b>
* [void]	 SetSeed(int sInx, int seed)					= set particle random seed from simulation index
* [int]	 GetRandInt(int sInx, int min, int max)			= get random integer from simulation particle [>= min && < max]
* [float]	 GetRandFloat(int sInx, float min, float max)	= get random float from simulation particle [>= min && <= max]
* [Point3]	 GetRandVector(int sInx)						= get random vector on unit sphere from simulation particle


### simulationStartがなぜか動かない

maxのデフォルトのParticleFlowのScriptOperatorと同じで、simulationStart内のコマンドはEventに入った最初に一度だけ実行するのかと思ったのですが、全然実行されませんでした。
あまりしたくなかったのですが、simulationStepで、Eventに入った瞬間のパーティクルのみ例外処理をするように書いています。
こちらの詳細がわかる方は教えていただけると幸いです。

## 最後に
かなり駆け足の説明になりましたが、実際に使用する方法の詳細はYoutubeで1から作成していますので、そちらをご参考にしていただければと思います。
また、設定するパラメータも多いので、一度Maxファイルを作成して、そちらのオペレーターを再利用するなどした方がいいかと思いました。
こちらの記事が何かのお役にたてれば幸いです。