数码资源网·下载

展开

用particle的instancer模拟爆破墙效果

作者:网友整理
Particle Flow安卓版(粒子流动态壁纸) v1.4 最新版

Particle Flow安卓版(粒子流动态壁纸) v1.4 最新版 13.21M / 简体中文

点击下载
爆破模拟最新版(恶趣味模拟游戏) v1.1.2 安卓版

爆破模拟最新版(恶趣味模拟游戏) v1.1.2 安卓版 47.64M / 简体中文

点击下载
爆破物理模拟器手机版v0.10.8 安卓版

爆破物理模拟器手机版v0.10.8 安卓版 43.01M / 简体中文

点击下载
 初始化位置
   首先打开2_1_instance_brickwall_base.mb文件,在这个文件里可以由一组砖组成的一面墙。这面墙是由一些砖块组成,稍后我们将用一些破砖代替它们,来让我们看看这个场景是怎么样设置的.

   设置如图。

 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 如果没有做错的话就已经好了,重新播放。

 

加载全部内容

相关文章
相关专题