|
群体动画是表达式或mel的强项,下面是我做的追逐的表达式练习,感兴趣的朋友可以看看。为形象,现用小鸡抢食的情景来说明。 一群小鸡在觅食,突然一只小鸡发现一只小虫,其他小鸡过来抢食,于是就会出现一群小鸡追逐一只小鸡的场景,在具体的测试中,我用一个球体代表叼着小虫的小鸡,其他的小鸡用圆锥表示。下面详细说明整个制作过程。 1.首先制作场景,建立一块崎岖不平的地面,代表小鸡的球体和圆锥。如图。 
2.随机的在地面上排布 “小鸡”。 3.为了上小鸡们能够贴在地面并且是随着地面坡度而发生相应的角度倾斜,将小鸡们与地面进行法线约束和几何体约束,如图。 
4.打开表达式编辑窗口,定义各小鸡初始位置。 for($k=1;$k<41;$k=$k+1) { if(frame==1) {$tx[0]=ball.translateX;// $tx[1]=ball1.translateX=7.9; $tx[2]=ball2.translateX=3; $tx[3]=ball3.translateX=-0.8; $tx[4]=ball4.translateX=1.3; $tx[5]=ball5.translateX=-2; $tx[6]=ball6.translateX=-4.3; $tx[7]=ball7.translateX=8.8; $tx[8]=ball8.translateX=16.02; $tx[9]=ball9.translateX=12.0; $tx[10]=ball10.translateX=5.2; $tx[11]=ball1.translateX=-7.9; $tx[12]=ball2.translateX=13; $tx[13]=ball3.translateX=-10.8; $tx[14]=ball4.translateX=21.3; $tx[15]=ball5.translateX=-12; $tx[16]=ball6.translateX=-14.3; $tx[17]=ball7.translateX=28.8; $tx[18]=ball8.translateX=10.02; $tx[19]=ball9.translateX=18.0; $tx[20]=ball10.translateX=15.2;
$tz[0]=ball.translateZ; $tz[1]=ball1.translateZ=1; $tz[2]=ball2.translateZ=3.347; $tz[3]=ball3.translateZ=8.908; $tz[4]=ball4.translateZ=0.736; $tz[5]=ball5.translateZ=2.525; $tz[6]=ball6.translateZ=-2.145; $tz[7]=ball7.translateZ=4.207; $tz[8]=ball8.translateZ=0.542; $tz[9]=ball9.translateZ=6.277; $tz[10]=ball10.translateZ=5; $tz[11]=ball11.translateZ=-1; $tz[12]=ball12.translateZ=-3.347; $tz[13]=ball13.translateZ=-8.908; $tz[14]=ball14.translateZ=-0.736; $tz[15]=ball15.translateZ=-2.525; $tz[16]=ball16.translateZ=-10.145; $tz[17]=ball17.translateZ=-4.207; $tz[18]=ball18.translateZ=-5.542; $tz[19]=ball19.translateZ=16.277; $tz[20]=ball20.translateZ=15; 具体定义值根据当时各小鸡法线和几何体约束后其位移通道的参数来定义,需要一些耐心,如果小鸡更多的话,需要耐心更多,我知道我的方法很笨。有朋友知道简单方便的定义方式吗?
5.下面进行核心的算法说明,在追逐中,不管小鸡当时在什么方位,他们的目标都为有虫的小鸡,另外小鸡不能相互穿过,所以在后面的小鸡若要跃过前面的小鸡,需要改变奔跑的方向,找空当穿插追逐。
for($j=1;$j<41;$j=$j+1)
{$dz[$j]=$tz[$j]-$tz[0]; $sign[$k]=sign($vz[$k])*sign($tz[$k]-$tz[$j]); $dx[$j]=abs($tx[$j]-$tx[$k]); if((abs($dz[$k]-$dz[$j])<5) &&($sign[$k]>0)&&($dx[$j]<=5)) {
$lz[$k]=abs($dz[$k]-$dz[$j]); $lz[$k]=min($lz[$k],$zz[$k]); $zz[$k]=$lz[$k];
$code[$k]=$j; $n[$k]=$n[$k]+1;
} } 这段程序是避免小鸡们相互穿插的,通过判断各小鸡相对其它小鸡的位置及距离,决定其加速或减速或改道。在这个练习中,我定义的小鸡个体半径为1个单位,所以我定义小鸡前面若5个单位内有其他小鸡,它会考虑改变速度或方向。 if((abs($dz[$k]-$dz[$j])<5) &&($sign[$k]>0)&&($dx[$j]<=5)) 其中$lz[$k]是用来表示小鸡前其他小鸡在z轴方向相对其最近的距离。$n[$k]表示在同一帧中,某小鸡前面(z轴方向)其他小鸡距其距离小于5单位的个数。以上是用按照序号逐个小鸡遍历方式比较得出结果,因此在小鸡数量较多或小鸡速度较快时,会有较大的误差,有朋友知道根本解决问题的方法吗? if($n[$k]==0) $aFz[$k]=15+0*abs($dz[$k]/3); else $aFz[$k]=15+0*abs($dz[$k]/3)-$k1*(7-$lz[$k]); if($tz[$k]<$tz[0]) $aFz[$k]=-$aFz[$k]; if(($lz[$k]<3)&&($n[$k]>0)) $afz[$k]=8*$k2*$vz[$k]+10*sign($vz[$k]); else $afz[$k]=$k2*$vz[$k]; $az[$k]=$aFz[$k]-$afz[$k]; if(($lz[$k]<2)&&($n[$k]>0)) $vz[$k]=$vz[$code[$k]]; else $vz[$k]=$vz[$k]+$az[$k]*$t; $tz[$k]=-$vz[$k]*$t-0.5*$az[$k]*$t*$t+$tz[$k]; 这段是定义各小鸡在z轴方向奔跑加速度和速度及位置的程序。其中$aFz[$k]表示其他小鸡相对有虫小鸡的主动加速度,$afz[$k]表示其他小鸡在奔跑中因速度和前面障碍而产生的反向阻碍其追逐的加速度。 if(($lz[$k]<2)&&($n[$k]>0)) $vz[$k]=$vz[$code[$k]]; 其中的这句表示如果某小鸡前面有小鸡与之距离小于2个单位时,强制其速度与前面的这只小鸡相同,从而避免穿插。另外式中的一些系数和参数大多是试验确定的,并不是最合理的定义。
$lx[$k]=$tx[$k]-$tx[$code[$k]]; $dx[$k]=$tx[$k]-$tx[0]; $aFx[$k]=-3*($dx[$k]/5)+0.05*sign($dx[$k]); if(($lx[$k]<2)&&($n[$k]>0)&&($lz[$k]<2)) {$afx[$k]=-5*sign($lx[$k])*((2.5-$lx[$k])/2)*((2.5-$lx[$k])/2)+$k3*$vx[$k]; $ax[$k]=0;} else if(($n[$k]>0)&&($lz[$k]>=2)) $afx[$k]=-0.1*sign($lx[$k])*((5-$lx[$k])/2)+$k3*$vx[$k]; else $afx[$k]=$k3*$vx[$k]; $ax[$k]=$aFx[$k]-$afx[$k]; $vx[$k]=$vx[$k]+$ax[$k]*$t; $tx[$k]=$tx[$k]+$vx[$k]*$t+0.5*$ax[$k]*$t*$t;
这段是定义各小鸡在x轴方向奔跑加速度和速度及位置的程序。各变量含义与前类似。 最后,给各小鸡的实体进行赋值,如下: ball.translateX=$tx[0]; ball1.translateX=$tx[1]; ball2.translateX=$tx[2]; ball3.translateX=$tx[3]; ball4.translateX=$tx[4]; ball5.translateX=$tx[5]; ball6.translateX=$tx[6]; ball7.translateX=$tx[7]; ball8.translateX=$tx[8]; ball9.translateX=$tx[9]; ball10.translateX=$tx[10]; ball11.translateX=$tx[11]; ball12.translateX=$tx[12]; ball13.translateX=$tx[13]; ball14.translateX=$tx[14]; ball15.translateX=$tx[15]; ball16.translateX=$tx[16]; ball17.translateX=$tx[17]; ball18.translateX=$tx[18]; ball19.translateX=$tx[19]; ball20.translateX=$tx[20];
ball1.translateZ=$tz[1]; ball2.translateZ=$tz[2]; ball3.translateZ=$tz[3]; ball4.translateZ=$tz[4]; ball5.translateZ=$tz[5]; ball6.translateZ=$tz[6]; ball7.translateZ=$tz[7]; ball8.translateZ=$tz[8]; ball9.translateZ=$tz[9]; ball10.translateZ=$tz[10]; ball11.translateZ=$tz[11]; ball12.translateZ=$tz[12]; ball13.translateZ=$tz[13]; ball14.translateZ=$tz[14]; ball15.translateZ=$tz[15]; ball16.translateZ=$tz[16]; ball17.translateZ=$tz[17]; ball18.translateZ=$tz[18]; ball19.translateZ=$tz[19]; ball20.translateZ=$tz[20];
同时还可以打印一些具体参量以便分析检查小鸡们的运动情况。 print("l:"+$lx[2]+" "+$lx[5]+" "+$lx[10] +" n:"+$n[1]+", "+$n[2]+", "+$n[3]+", "+$n[4]+", "+$n[5]+", "+$n[6]+", "+$n[7]+", "+$n[8]+", "+$n[10]+"\n" +" dx:"+$dx[2]+" "+$dx[5]+" "+$dx[10]+"\n" +" aFx:"+$aFx[2]+" "+$aFx[5]+" "+$aFx[10]+"\n" +" ax:"+$ax[2]+" "+$ax[5]+" "+$ax[10]+" "+"\n" +" afx:"+$afx[2]+" "+$afx[5]+" "+$afx[10]+"\n" +" vx:"+$vx[2]+" "+$vx[5]+" "+$vx[10]+"\n" +" tx:"+$tx[2]+" "+$tx[5]+" "+$tx[10]+"\n");
最后给出全部表达式的程序,供参考。 global float $tx[],$tz[],$dx[],$dz[],$lx[],$lz[],$vx[], $vz[],$aFx[],$afx[],$aFz[],$afz[],$ax[],$az[],$t=0.053, $sign[],$zx[],$zz[],$k1=0.5,$k2=0.5,$k3=2,$k4; int $code[],$i,$j,$k,$m,$n[];
for($k=1;$k<41;$k=$k+1) { if(frame==1) {$tx[0]=ball.translateX;// $tx[1]=ball1.translateX=7.9; $tx[2]=ball2.translateX=3; $tx[3]=ball3.translateX=-0.8; $tx[4]=ball4.translateX=1.3; $tx[5]=ball5.translateX=-2; $tx[6]=ball6.translateX=-4.3; $tx[7]=ball7.translateX=8.8; $tx[8]=ball8.translateX=16.02; $tx[9]=ball9.translateX=12.0; $tx[10]=ball10.translateX=5.2; $tx[11]=ball1.translateX=-7.9; $tx[12]=ball2.translateX=13; $tx[13]=ball3.translateX=-10.8; $tx[14]=ball4.translateX=21.3; $tx[15]=ball5.translateX=-12; $tx[16]=ball6.translateX=-14.3; $tx[17]=ball7.translateX=28.8; $tx[18]=ball8.translateX=10.02; $tx[19]=ball9.translateX=18.0; $tx[20]=ball10.translateX=15.2;
$tz[0]=ball.translateZ; $tz[1]=ball1.translateZ=1; $tz[2]=ball2.translateZ=3.347; $tz[3]=ball3.translateZ=8.908; $tz[4]=ball4.translateZ=0.736; $tz[5]=ball5.translateZ=2.525; $tz[6]=ball6.translateZ=-2.145; $tz[7]=ball7.translateZ=4.207; $tz[8]=ball8.translateZ=0.542; $tz[9]=ball9.translateZ=6.277; $tz[10]=ball10.translateZ=5; $tz[11]=ball11.translateZ=-1; $tz[12]=ball12.translateZ=-3.347; $tz[13]=ball13.translateZ=-8.908; $tz[14]=ball14.translateZ=-0.736; $tz[15]=ball15.translateZ=-2.525; $tz[16]=ball16.translateZ=-10.145; $tz[17]=ball17.translateZ=-4.207; $tz[18]=ball18.translateZ=-5.542; $tz[19]=ball19.translateZ=16.277; $tz[20]=ball20.translateZ=15;
for($i=1;$i<41;$i=$i+1) {$aFz[$i]=$afz[$i]=$vz[$i]=$az[$i]=$n[$i]=0; $z[$i]=$lz[$i]=5; $aFx[$i]=$afx[$i]=$vx[$i]=$ax[$i]=0; $lx[$i]=3;
}
}
if(frame>=1) { $tz[0]=ball.translateZ; $tx[0]=50*noise(0.006*frame); $dz[$k]=$tz[$k]-$tz[0]; $n[$k]=0; $z[$k]=5; {for($j=0;$j<41;$j=$j+1)
{$dz[$j]=$tz[$j]-$tz[0]; $sign[$k]=sign($vz[$k])*sign($tz[$k]-$tz[$j]); $dx[$j]=abs($tx[$j]-$tx[$k]); if((abs($dz[$k]-$dz[$j])<5) &&($sign[$k]>0)&&($dx[$j]<=5)) {
$lz[$k]=abs($dz[$k]-$dz[$j]); $lz[$k]=min($lz[$k],$zz[$k]); $zz[$k]=$lz[$k];
$code[$k]=$j; $n[$k]=$n[$k]+1;
} } }
} if($n[$k]==0) $aFz[$k]=15+0*rand($k)+0*abs($dz[$k]/3); else $aFz[$k]=15+0*abs($dz[$k]/3)-$k1*(7-$lz[$k]); if($tz[$k]<$tz[0]) $aFz[$k]=-$aFz[$k]; if(($lz[$k]<3)&&($n[$k]>0)) $afz[$k]=8*$k2*$vz[$k]+10*sign($vz[$k]); else $afz[$k]=$k2*$vz[$k]; $az[$k]=$aFz[$k]-$afz[$k]; if(($lz[$k]<2)&&($n[$k]>0)) $vz[$k]=$vz[$code[$k]]; else $vz[$k]=$vz[$k]+$az[$k]*$t;
$tz[$k]=-$vz[$k]*$t-0.5*$az[$k]*$t*$t+$tz[$k];
$lx[$k]=$tx[$k]-$tx[$code[$k]]; $dx[$k]=$tx[$k]-$tx[0]; $aFx[$k]=-3*($dx[$k]/5)+0.05*sign($dx[$k]); if(($lx[$k]<2)&&($n[$k]>0)&&($lz[$k]<2)) {$afx[$k]=-5*sign($lx[$k])*((2.5-$lx[$k])/2)*((2.5-$lx[$k])/2)+$k3*$vx[$k]; $ax[$k]=0;} else if(($n[$k]>0)&&($lz[$k]>=2)) $afx[$k]=-0.1*sign($lx[$k])*((5-$lx[$k])/2)+$k3*$vx[$k]; else $afx[$k]=$k3*$vx[$k]; $ax[$k]=$aFx[$k]-$afx[$k]; $vx[$k]=$vx[$k]+$ax[$k]*$t; $tx[$k]=$tx[$k]+$vx[$k]*$t+0.5*$ax[$k]*$t*$t;
} ball.translateX=$tx[0]; ball1.translateX=$tx[1]; ball2.translateX=$tx[2]; ball3.translateX=$tx[3]; ball4.translateX=$tx[4]; ball5.translateX=$tx[5]; ball6.translateX=$tx[6]; ball7.translateX=$tx[7]; ball8.translateX=$tx[8]; ball9.translateX=$tx[9]; ball10.translateX=$tx[10]; ball11.translateX=$tx[11]; ball12.translateX=$tx[12]; ball13.translateX=$tx[13]; ball14.translateX=$tx[14]; ball15.translateX=$tx[15]; ball16.translateX=$tx[16]; ball17.translateX=$tx[17]; ball18.translateX=$tx[18]; ball19.translateX=$tx[19]; ball20.translateX=$tx[20];
ball1.translateZ=$tz[1]; ball2.translateZ=$tz[2]; ball3.translateZ=$tz[3]; ball4.translateZ=$tz[4]; ball5.translateZ=$tz[5]; ball6.translateZ=$tz[6]; ball7.translateZ=$tz[7]; ball8.translateZ=$tz[8]; ball9.translateZ=$tz[9]; ball10.translateZ=$tz[10]; ball11.translateZ=$tz[11]; ball12.translateZ=$tz[12]; ball13.translateZ=$tz[13]; ball14.translateZ=$tz[14]; ball15.translateZ=$tz[15]; ball16.translateZ=$tz[16]; ball17.translateZ=$tz[17]; ball18.translateZ=$tz[18]; ball19.translateZ=$tz[19]; ball20.translateZ=$tz[20];
print("l:"+$lx[2]+" "+$lx[5]+" "+$lx[10] +" n:"+$n[1]+", "+$n[2]+", "+$n[3]+", "+$n[4]+", "+$n[5]+", "+$n[6]+", "+$n[7]+", "+$n[8]+", "+$n[10]+"\n" +" dx:"+$dx[2]+" "+$dx[5]+" "+$dx[10]+"\n" +" aFx:"+$aFx[2]+" "+$aFx[5]+" "+$aFx[10]+"\n" +" ax:"+$ax[2]+" "+$ax[5]+" "+$ax[10]+" "+"\n" +" afx:"+$afx[2]+" "+$afx[5]+" "+$afx[10]+"\n" +" vx:"+$vx[2]+" "+$vx[5]+" "+$vx[10]+"\n"
对于这个程序仅仅是练习的东西,还有很多问题,例如虽然考虑到了小鸡们的穿插情况,但实际运行中依然有可能出现这样的问题。我想应该每帧都要对小鸡们重新排序,然后按新的次序关系计算相互的影响,前面简单的按照初始定义的序号为次序计算是不太妥当的,有时间再改改,还有就是计算频率还是太低,现在每帧计算一次,如果可以每帧计算多次应该会好很多,由朋友知道如何定义每帧的计算次数吗?当然希望有朋友可以提出更好的方法来。
表达式可以考虑继续完善和扩展,例如可以考虑以单个小鸡的奔跑方向来定义各小鸡的局部坐标,再根据这个局部坐标来计算小计的奔跑动作;可以给各小鸡添加不同的属性,例如可以使每个小鸡的体质不同;可以给整个场景添加围栏控制,使小鸡们在一定的范围内奔跑;可以考虑有小鸡抢到了小虫,小鸡们追逐对象改变的效果;可以考虑用这个表达式模拟其他追逐情况或大队人马冲锋的情景。希望朋友们做出更多更好的效果来。
|