衝突時の火花

衝突した時に火花を作る

リジッドボディ同士がぶつかった時に、 火花が散るというアニメーションを作ってみましょう。
また今回のスクリプトを応用すれば、火花の代わりに衝突時に穴を開けたり、へこみを作ることもできるようになります。

作り方

  1. 衝突するオブジェクトをアクティブリジッドボディにします。
  2. 衝突されるオブジェクトをパッシブブリジッドボディにします。
  3. 衝突するオブジェクトをフィールドなどを使って、 衝突されるオブジェクトにぶつかるようにします。
  4. rigidSolver ノードの contactData アトリビュートを 1 に設定しておきます。
    この設定がされていないと衝突したかどうかがわかりません。
  5. 衝突するオブジェクトにぶつかった時に火花をだすエクスプレッションを設定します。
    衝突されるリジッドボディの contactCount アトリビュートが 1 以上になったら 2 つのリジッドボディが衝突しているので、 その時に rigidBody(q=True, contactPosition=True) によって衝突した点の座標を求めます。
    その点に emit() 関数か、 または、エミッターによって新しいパーティクルを発生させます。

火花のスクリプト

最初のスクリプトの火花は小さくて見にくいかもしれません。 それはパーティクルのアトリビュートを適切に設定していないからです。 それらのアトリビュートの設定は練習問題で行ないます。
また、衝突した後、火花がずっと出続ける問題も練習問題で解決しましょう。

なお、今回のスクリプトはプロシージャが 2 つ含まれているので注意して作ってください。

  1. 以下の MEL スクリプトを Python スクリプトに書き直して、 makeSpark1.py という名前で保存します。
    global proc makeSpark1()
    {
    	string $gra[] = `gravity -m 9.8`;
    	polySphere;
    	move 1 10 1;
    	rigidBody -active;
    	connectDynamic -f $gra[0];
    
    	polyPlane;
    	scale 20 1 20;
    	rotate 0 0 -45;
    	string $rb = `rigidBody -passive`;
    	expression -o $rb -s ("makeEmitter1(\"" + $rb + "\", contactCount)");
    
    	setAttr rigidSolver.contactData 1;
    }
    
    global proc makeEmitter1(string $rigidbody, int $contval)
    {
    	if($contval > 0)
    	{
    		string $pp[] = `rigidBody -q -contactPosition $rigidbody`;
    		string $pos[];
    		tokenize $pp[0] $pos;
    		string $emi[] = `emitter -pos $pos[0] $pos[1] $pos[2]`;
    		string $pname[] = `particle`;
    		connectDynamic -em $emi[0];
    
    		setAttr ($pname[1] + ".particleRenderType") 6;
    		setAttr ($pname[1] + ".lifespanMode") 2;
    		setAttr ($pname[1] + ".lifespanRandom") 0.5;
    		setAttr ($pname[1] + ".lifespan") 5;
    	}
    }
    
  2. 以下の Python スクリプトを makeSpark1.py という名前で作ります。
    import maya.cmds
    
    def makeSpark1():
        gra = maya.cmds.gravity(m=9.8)
        maya.cmds.polySphere()
        maya.cmds.move(1, 10, 1)
        maya.cmds.rigidBody(active=True)
        maya.cmds.connectDynamic(f=gra[0])
    
        maya.cmds.polyPlane()
        maya.cmds.scale(20, 1, 20)
        maya.cmds.rotate(0, 0, -45)
        rb = maya.cmds.rigidBody(passive=True)
        maya.cmds.expression(o=rb, s=('python("makeEmitter1(\'' + rb + '\', " + contactCount + ")")'))
    
        maya.cmds.setAttr('rigidSolver.contactData', 1)
    
    def makeEmitter1(rigidbody, contval):
        if contval > 0:
            pp = maya.cmds.rigidBody(rigidbody, q=True, contactPosition=True)
            pos = pp[0].split()
            emi = maya.cmds.emitter(pos=(pos[0], pos[1], pos[2]))
            pname = maya.cmds.particle()
            maya.cmds.connectDynamic(em=emi[0])
    
            maya.cmds.setAttr((pname[1] + '.particleRenderType'), 6)
            maya.cmds.setAttr((pname[1] + '.lifespanMode'), 2)
            maya.cmds.setAttr((pname[1] + '.lifespanRandom'), 0.5)
            maya.cmds.setAttr((pname[1] + '.lifespan'), 5)
    
  3. スクリプト エディタ の ファイル → スクリプトのロード によって makeSpark1.py を読み込んで、テンキーの Enter キーなどで実行します。
  4. Script Editor の下のウインドウから makeSpark1() と打ち込んで実行します。
    [makeSpark1() の実行図]
  5. プレイバックしてアニメーションを実行します。
    [makeSpark1() のアニメーション実行図]

(注意)
再度プレイバックする場合は衝突によって作られたパーティクルとエミッターを消去しておいてください。

スクリプトの解説

import maya.cmds
maya.cmds モジュールをインポートします。
def makeSpark1():
makeSpark1() という関数の宣言です。
gra = maya.cmds.gravity(m=9.8)
gravty (重力)フィールドを作ります。
重力係数(m)は 9.8 です。
maya.cmds.polySphere()
ポリゴンの球を作ります。
maya.cmds.move(1, 10, 1)
ボリゴンの球を (1, 10, 1) に移動します。
maya.cmds.rigidBody(active=True)
ポリゴンの球をアクティブ・リジッドボディにします。
maya.cmds.connectDynamic(f=gra[0])
ポリゴンの球に gravty フィールドをコネクトして Y 軸マイナス方向に落ちてゆくようにします。
ポリゴンの球は、 この時点でセレクトされた状態なので、 関数で球の名前を指定する必要はありません。
maya.cmds.polyPlane()
ポリゴンの平面を作ります。
maya.cmds.scale(20, 1, 20)
ポリゴンの平面を X 方向 20、Z 方向 20 に拡大します。
maya.cmds.rotate(0, 0, -45)
ポリゴンの平面を Z 方向に -45 度回転します。
rb = maya.cmds.rigidBody(passive=True)
ポリゴンの平面をパッシブ・リジッドボディにします。
作成されたリジッドボディの名前を文字列の変数 rb に代入しておきます。
maya.cmds.expression(o=rb, s=('python("makeEmitter1(\'' + rb + '\', " + contactCount + ")")'))
変数 rb に代入されているリジッドボディにエクスプレッションを設定します。
o=rb
エクスプレッションを定義するオブジェクトを rb に名前が代入されているリジッドボディに設定します。
s=('python("makeEmitter1(\'' + rb + '\', " + contactCount + ")")')
エクスプレッションの式を設定します。
MEL コマンドの python によって Python の関数 makeEmitter1() を実行します。
式全体が ( ) で囲まれているので、 まず ( ) の中の文字列の処理を行ない、 結果が式を表す 1 つの文字列になります。
( ) 内におけるエクスプレッションの式は、 以下のように組み立てられてゆきます。
  1. 最初の状態
    ('python("makeEmitter1(\'' + rb + '\', " + contactCount + ")")')
  2. rb がリジッドボディの名前(例えば、rigidBody2)に置き換わります。
    ('python("makeEmitter1(\'' + 'rigidBody2' + '\', " + contactCount + ")")')
  3. 文字列が結合されて、1 つの文字列になります。
    ('python("makeEmitter1(\'rigidBody2\', " + contactCount + ")")')
    ここで、' の前に \ があるのは式を表す文字列が、 まだ終りではないことを示すためのものです。
    また、contactCount はリジッドボディのアトリビュートで、 他のリジッドボディが衝突した点の個数を表しています。
結局、このエクスプレッションでは makeEmitter1 という名前のプロシージャを、 リジッドボディの名前と衝突の回数の 2 つの引数をつけて呼び出すことになります。
maya.cmds.setAttr('rigidSolver.contactData', 1)
rigidSolver の contactData アトリビュートを 1 に設定します。
これによって各リジッドボディが衝突したかどうかが、わかるようになります。
ここで rigidSolver はリジッドボディソルバとよばれるもので、 リジッドボディのアニメーションをコントロールするノードです。
def makeEmitter1(rigidbody, contval):
makeEmitter1 という名前の関数を定義します。
上のリジッドボディになっているポリゴンの平面に定義されたエクスプレッションから呼ばれます。
各引数の説明は以下の通りです。
rigidbody
他のリジッドボディが衝突したかどうか調べるリジッドボディの名前
contval
リジッドボディが衝突した点の個数
if contval > 0:
リジッドボディが衝突した点の個数が 0 以上なら以下のスクリプトを実行します。
pp = maya.cmds.rigidBody(rigidbody, q=True, contactPosition=True)
リジッドボディが衝突した点の座標が文字列のリストになって返ってくるので、 pp に代入します。 衝突した点の個数は 1 個とは限らないのでリストになります。
pos = pp[0].split()
文字列 pp[0] を分解してリスト pos に代入します。
なぜこのような処理が必要かというと、pp の中身であるX, Y, Z 座標値が一つの文字列になっているからです。
ここでの処理は以下のように進んでゆきます。
  1. 例えば、pp[0] の中身が '1.5 2.3 3.6' という文字列になっているとします。
  2. split() 関数によって、pp[0] が分解されて pos の中に代入されてゆきます。
    pos[0] = '1.5'
    pos[1] = '2.3'
    pos[2] = '3.6'
なお、ここでは 1 回の衝突で 1 個だけエミッターを作ることにしているので pp[1] 以下はわざと無視しています。、
emi = maya.cmds.emitter(pos=(pos[0], pos[1], pos[2]))
エミッターを (pos[0], pos[1], pos[2]) の位置に作ります。
エミッターの名前は emi に代入します。
pname = maya.cmds.particle()
エミッターのために空のパーティクルを作ります。
パーティクルのトランスフォームノードの名前が pname[0] に、 シェープノードの名前が pname[1] 代入されます。
maya.cmds.connectDynamic(em=emi[0])
空のパーティクルにエミッター(emi)をコネクトします。
maya.cmds.setAttr((pname[1] + '.particleRenderType'), 6)
パーティクルのシェープノード pname[1] の particleRenderType アトリビュートに 6 (Streak)を設定します。
これでパーティクルのレンダリングタイプが細長い線になります。
maya.cmds.setAttr((pname[1] + '.lifespanMode'), 2)
ライフスパンモードを 2 (Random range)に設定します。
これによって個々のパーティクルに違ったライフスパンを割り当てることができます。
ライフスパンとはパーティクルの生存時間のことです。
maya.cmds.setAttr((pname[1] + '.lifespanRandom'), 0.5)
ライフスパンの平均値(lifespan)からの分布の幅を設定します。
平均値が 5 、lifespanRandom が 0.5 であれば、 ライフスパンは 4.5 から 5.5 の間のランダムな値になります。
maya.cmds.setAttr((pname[1] + '.lifespan'), 5)
ライフスパンの平均値を 5 に設定します。

練習

参考


Prev
Home | Contents
Mail