設置如圖。
1、所有的磚都需要freeze transform。
2、我們需要建粒子去發射它。可以運行這個命令://create particle node particle -n wall_front_PTL;
3、創建一個屬性,當開始播放場景的時候我們將用這個屬性旋轉磚塊。打開粒子的屬性欄,增加一個per-particle的屬性,名稱為rotationPP。
4、粒子將從每一塊磚的位置發射。我們首先從所有的節點中捕獲一個列表,然後循環每一個節點,用emit命令從每個節點的位置發射粒子。老外用了這樣一段腳本來完成這個操作。emit_brick_particles.mel:// 獲取前牆磚的名稱列表string $nodeList[] = `ls -dag -type transform "o_frontWall_LOC|*PLY" "i_frontWall_LOC|*PLY" "side_frontWall_LOC|*PLY"`;
// 用循環語句讓每一塊磚發射一個粒子for( $node in $nodeList ) { // 得到每一塊磚的位置 float $pos[] = `xform -q -ws -t $node`; // 確定每一塊磚的大致體積 // 當我們破碎磚塊的時候就會用到:
float $bb[] = `xform -q -ws -bb $node`;
float $x = abs( $bb[3] - $bb[0] );
float $y = abs( $bb[4] - $bb[1] );
float $z = abs( $bb[5] - $bb[2] );
vector $v = <<$x, $y, $z>>;
// 得到磚的長度 float $vol = mag( $v );
//發射一個粒子 emit -o wall_front_PTL
-pos $pos[0] $pos[1] $pos[2]
-at mass -fv $vol;
} // for $node in $nodeList
// 將此時的粒子保存為初始狀態
saveInitialState wall_front_PTL;
emit_brick_particles.mel (1.08k)
[page]
5.記錄粒子產生的次序。因為磚和粒子之間沒有真正的關聯,我們不得不依靠maya的物體列表清單。按字母順序獲取$nodelist變量裏的列表,這是非常重要的一點,這將幫助我們正確的對磚塊進行instance。
6.我們現在為每個粒子建立instance.我們將像前麵發射粒子一樣定義這個列表。
//選擇磚塊
select -r `ls -dag -type transform "o_frontWall_LOC|*PLY" "i_frontWall_LOC|*PLY"
"side_frontWall_LOC|*PLY"`;
7.選擇菜單particles>instancer(Replacement)>optionBox.在instanced objects列表裏,你將會看到你所選擇的按字母順序排列的所有磚塊。單擊create 按鈕,你會看到instance物體已經出現了。
你會注意到有些磚的位置不正確。當你使用默認值創建instance時,instance會使用列表中的第一個物體。我們將指定instance使每個粒子使用它相同位置的磚。因為在前麵我們已經正確的設置了我們的粒子和instance,所以現在就可以簡單的將粒子的ID作為磚的列表索引。
8.選擇粒子wall_front_PTL打開屬性編輯器找到instancer一欄。
9.打開objectindex下拉菜單,設置為particleID,我們將看到磚塊已經恢複到了正確的位置。
10.先添加一個per-particle屬性,名稱為rotationPP,打開rotation下拉菜單,設置為rotationPP。
當這些磚移動的時候,我們將用這個屬性使磚產生翻滾的效果。
開始模擬
有趣的事馬上就要發生了,我們會從內部炸毀這麵牆。我們遇到的第一個問題是重力,怎樣使磚塊受到重力影響呢,在它們被推出去之前不讓它們發生移動。有許多方法:
味 1)在粒子的runtime expression裏創建一個重力,當粒子的速度不為零的時候它將受到重力影響,如果粒子已經接觸到了地麵,將它所受的重力設置為0。
//獲取位置
vector $pos=position;
//如果粒子已經移動,並且沒有掉到地麵上,受重力影響。
if(mag(velocity)>0&&$pos.y>0.01)
velocity+=<<0,-32.17,0>>;
2)創建一個真正的重力場,將它的volume shape設置為cube。將它的位置放在牆的前麵,當粒子被推出去以後它就會受到重力的影響。
第二種方法比較實用,如果你的場景物體不是很規則,那可能需要建立很多個重力場,並確保他們互相不交迭。
[page]
現在我們打開2_2_instance_brickwall_sim_base.mb,前麵磚牆已經設置好了instance,一些場和碰撞物體將在適當的時間將磚牆碰出去。如果現在播放,會看到磚被推了出去掉在了地上。這是因為有許多的場和碰撞物體來模擬這個爆炸動畫。可以看到在磚飛出去的時候並沒有旋轉,現在我們來加這個效果,讓磚旋轉著飛出去。
我們將根據一些因素來旋轉這些磚:
a.磚的速度。
b.磚的質量(大的磚相對重一些,因此它們轉的會慢一些)。
c.上麵的磚朝一個方向旋轉,下麵的磚會朝另一個方向旋轉,距離爆炸中心的會旋轉的快一些。
d.用一個隨機數值去乘以旋轉值,讓它們的速度有些變化。
現在我們要為粒子增加一些屬性。因為我們是使用emit命令來發射的粒子,所以它們的創建表達式將被忽略。我發現可以在運行表達式中創建一個“假的”創建表達式。這個創建表達式將出現在第二幀,是在爆炸前運行。
我們也可以用epicentre_LOC這個locator的位置來決定爆炸什麼時候發生。
1.首先,我們給wall_front_PTL的粒子增加幾個per-particle的屬性。3個per-particle float屬性的,名稱為rotXPP,rotYPP和rotZPP,1個per-particle float屬性的,名稱為magVelPP。
2.接下來,我們創建這個“假的”創建表達式。它將運行在模擬之前,在runtime expression中輸入下列表達式:
//假的創建表達式if(frame==`playbackOptions -q -min`+1){
//獲取位置
vector $pos = position;
//獲取質量,並且取它的倒數
float $mass=1/(mass);
//獲取爆炸中心的位置
float $epicentreF[]=`xform -q -ws -t epicentre_LOC`;
vector $epicentre=<<$epicentreF[0],$epicentreF[1],$epicentreF[2]>>;
//確定粒子和爆炸中心的距離
float $distToCentre=mag($pos-$epicentre);
//定義旋轉值
float $rx=deg_to_rad(deg_to_rad(rand(-10)));
float $ry=deg_to_rad(deg_to_rad(rand(5)));
float $rz=deg_to_rad(deg_to_rad(rand(2)));
//根據粒子與爆炸中心的關係修改旋轉值
if($pos.y<$epicentre.y)$rx*=-1;
if($pos.x<$epicentre.x)$ry*=-1;
//根據粒子與爆炸中心的距離關係修改旋轉速度
float $distNorm=1-smoothstep(0,10,$distToCentre);
//最後為粒子指定旋轉值
rotXPP=$rx*$distNorm*$mass;
rotXPP=$ry*$distNorm*$mass;
rotXPP=$rz*$distNorm*$mass;
}//假的創建表達式結束
3.現在我們繼續給增加運行表達式
//獲取旋轉值
vector $rot=rotationPP;
//獲取速度
//確保當粒子沒有移動的時候不旋轉。
magVelPP=smoothstep(1,30,mag(velocity));
//加上現有的旋轉值
float $rx=$rot.x+rotXPP*magVelPP;
float $ry=$rot.y+rotYPP*magVelPP;
float $rz=$rot.z+rotZPP*magVelPP;
//指定給旋轉值
rotationPP=<<$rx,$ry,$rz>>;
在粒子的屬性中找到instance欄,將rotation改成rotationPP現在重新播放,可以看到磚塊旋轉著飛出去了。
[page]
下麵我們來做磚塊被炸碎的效果
首先打開2_3_instance_brickwall_broken_base.mb文件,打開outline可以看到,我們已經準備好了,20種破碎成不同形態的磚,每塊磚都把它碎成了幾塊。
每個碎塊的軸心點都在它的中心。並不在世界坐標的中心,如果讓這些碎塊的軸心都處於世界坐標的中心,可能設置起來會容易一些,可是在我們這個效果中旋轉起來會出現問題。這就是為什麼讓他們的軸心點都處在自身中心的原因。
[page]
我給大家提供一個腳本,它能使這些碎磚instance好的磚塊:emit_brick_chunks.mel
1.首先我們選擇所有的碎塊,我們使用我已經提供的一個腳本assign_brick_ids.mel,在命令行輸入
source assign_brick_ids.mel;assign_brick_ids();
assign_brick_ids.mel (0.64k)
2.接下來,我們選擇一個磚測量它與爆炸中心的距離。靠近爆炸中心的將會被炸成小碎片。如果你注意觀察了,你就會發現名稱代號小的是碎塊多的磚,名稱代號大的是碎塊少的磚。我們將在距離爆炸中心近的地方用一些碎塊多的磚,遠的地方用一些碎塊少的磚。實現這個效果的所有代碼都在emit_brick_chunks.mel。
emit_brick_chunks.mel (4.3k)
global proc emit_brick_chunks() {
// all nodes under which bricks sit
string $nodeList[] = { "o_frontWall_LOC", "i_frontWall_LOC", "side_frontWall_LOC" };
// 初始化變量
vector $epicentre = f2v( `xform -q -ws -t epicentre_LOC` );
string $brickName = "brokenBrick";
// 循環便曆所有節點
for( $node in $nodeList ) {
// 顯示出狀態
print( "// WORKING on group "+$node+"...\n" );
// 列出所有的子項
string $children[] = `listRelatives -c -type transform $node`;
//為朝向旁邊的磚進行一個不同的設置.
// 可以在 'brokenBricksR_LOC'下找到
if( $node == "side_frontWall_LOC" )
$brickName = "brokenBrickR";
// 循環遍曆每一塊磚
for( $brick in $children ) {
// 確定距離
vector $brickPos = f2v( `xform -q -ws -t $brick` );
float $dist = mag( $brickPos - $epicentre );
// 選擇一塊磚
// 我們需要選擇一塊磚. 有20塊。
// 根據距爆炸中心的距離
// 按大塊和小塊分布
float $minDist = 1.4;
float $maxDist = 9.1;
float $diffDist = $maxDist - $minDist;
// determine normalized distance
float $normDist = linstep( $minDist, $maxDist, $dist );
float $weight = $dist / $diffDist;
float $gauss = gauss( $weight );
$gauss = abs( $gauss ) * 20;
$gauss = int( $gauss );
$gauss = clamp( 1, 20, $gauss );
// $brickIndex 是實際已經選擇的磚
int $brickIndex = $gauss;
// 在每個碎塊的中心發射粒子
//列出所有的碎塊
string $chunks[] = `listRelatives -c -type transform ($brickName+$brickIndex+"_LOC")`;
// 得到磚塊的世界坐標位置
vector $brickPos = f2v( `xform -q -ws -t $brick` );
// 設置發射器
string $emitStr = "emit -object wall_front_PTL ";
// 在每個碎塊上建一個發射器
for ($chunk in $chunks) {
// 得到每一個碎塊的index號 -- 我們用這個設置粒子的objectIndexPP
int $chunkIndex = `getAttr ($chunk+".index")`;
// 得到每一個碎塊的世界坐標位置.
vector $chunkPos = f2v( `xform -q -ws -t $chunk` );
// 碎塊的位置加上磚的位置
$chunkPos += $brickPos;
// 確定每一個碎塊的體積用'bb_calculateBB'
float $vol = `bb_calculateBB( $chunk )`;
// 連接 $emitStr
$emitStr += "-pos "+$chunkPos.x+" "+$chunkPos.y+" "+$chunkPos.z+" ";
$emitStr += "-at initPosPP -vv "+$chunkPos.x+" "+$chunkPos.y+" "+$chunkPos.z+" ";
$emitStr += "-at scalePP -vv 1 1 1 ";
$emitStr += "-at rotationPP -vv 0 0 0 ";
$emitStr += "-at initMassPP -fv "+$vol+" ";
$emitStr += "-at mass -fv "+$vol+" ";
$emitStr += "-at objectIndexPP -fv "+$chunkIndex+" ";
$emitStr += "-at visibilityPP -fv 1 ";
} // for chunk in chunks
//在每一塊上執行發射命令
eval($emitStr);
} // for brick
} // for node
// 將粒子保存為初始狀態
saveInitialState wall_front_PTL;
// 顯示狀態
print("// DONE emitting chunks.\n");
} // emit_brick_chunks
[page]
3.上麵的操作讓我們為每一個碎塊的位置都放置了一個粒子。
4.然後我們對粒子進行instance,可以用objectIndexPP來做這個,首先選擇磚塊。select -r `ls -tr,rokenBrick*_*PLY"`。
5.particle>instancer>optionbox如下設置:
scale 設置為scalePP
visibility 設置為visibilityPP
objectIndex 設置為objectindexPP
rotation 設置為rotationPP
單擊create 如果沒有做錯的話就已經好了,重新播放。