由于最近想做一個用粒子替換實現射箭的特效,一時摸不著頭腦。粒子替換實現射箭的效果的最大問題在于,如果用粒子的速度velocity作為instancer的Aim Direction,那么一旦粒子與地面產生碰撞(這里我們碰撞屬性中的摩擦力Friciton設置為1,彈力Resilience設置為0),勢必導致速度瞬時變為0,使得箭無法插在地面上,而是一碰到地面就倒下。
于是網上找了一下相關教程特別是G_no_mon的在線教程給了我很多提示。國內大部分相關教程也是翻譯的那個版本,該教程提供的解決方案是使用了一個JMS的腳本,先是通過記錄粒子的運動軌跡生成若干條曲線,然后將曲線上的的末端的一些點給刪除了(也就是粒子碰撞地面后生成的軌跡點),最后讓劍沿著這些曲線做"路徑動畫"。從而實現亂箭射在地面上(或是其他物體)的效果。研究過程中覺得用JMS腳本來一遍一遍的計算生成曲線有些浪費時間,而且對于我們這些MEL初學者來說,還的另外花些時間研究下JSM(當然了,學習是必須的)。
只是我從原來的教程中得到些啟發覺得可以用更為簡單的方法去實現這一射箭的效果?,F講一下我的大概想法,既然我們不能直接用velocity作為instancer的Aim Direction,那么我們可以自定義一個變量用來存儲粒子在碰撞前一幀,速度還沒變為0的那個速度值,用該矢量速度值作為箭插在物體上的方向。為了完整起見,我把整個制作過程詳細介紹一下:1.先制作一只簡單的箭
2.制作一個地面,和一個用來發射箭的面
3.選擇用來發射箭的那個面,通過Particles Emit form object,從該面上發射粒子
4.打開粒子發射器屬性:emitter type :surfacerate(particles/sec): 5speed : 20speed random: 4
5.給粒子加個重力場
6.選中粒子和地面,打開菜單命令particles make collide 后面的屬性對話框,將彈力resilience設置為0,摩擦力friction設置為1,點擊create生成碰撞事件
7.執行play場景,看到粒子與地面產生正確的碰撞,并停止在地面上(如果地面凹凸不平,會有滑動,不妨,這個我們稍后解決)
8.先選弓箭再選粒子,打開菜單命令particles instancer
9.打開粒子屬性中的instancer一欄,勾啟allow all data types,aimdirection設置velocity,此時播放我們就會發現劍一射中就立馬"爬"下了
10.解決這一問題需要添加一個自定義屬性,在粒子屬性窗口中add dynamic attributes一欄中,點擊general,添加一個名為dirpp的每粒子屬性,類型為vector矢量(此處一點要注意)
11.右擊dirpp屬性,添加runtime before dynamics表達式:float $speed = mag(arrow_particleshape.velocity);if($speed != 0){arrow_particleshape.dirpp = arrow_particleshape.velocity;}這里詳細解釋一下,我用$speed來存儲粒子的速率(速度是個矢量,是三維的,速率是個標量,是一維的,函數mag()正是將三維的矢量計算得到一個相應的一維標量),只要粒子在運動,速率不為零,那么我自定義的dirpp將不停的更新為粒子的速率,而一旦碰撞產生,速度為零,那么我的dirpp就不會更新,所存儲的是粒子停止之前一幀的那個速率,并且之后也將一直存儲這個值,這樣我們就可以用這個dirpp作為instancer的aimdirection了
12. 修改粒子屬性面板中instancer一欄中aim direction 為 dirpp,在播放一下看看,箭就不會射中倒下了
13. 接下來要解決的問題就是,即便摩擦力為1,彈力為0了,箭還是會在地面上滑動這里我們為粒子添加個runtime after aynamics的表達式:float $speed = mag(arrow_particleshape.velocity);if($speed < 4){arrow_particleshape.velocity = <<0,0,0;}這樣,當粒子在碰撞時速度小于一定值后我們將其速率強行限制為<<0,0,0,注意,你也可以試試其他的數值不一定用4,但數值不宜過小,否則箭在射出的一瞬間會有個抖動。
14. 這是我們發現即使箭的速率為零了,但還是會在地面上滑動,我想到的解決方法還是和上面的差不多,用一個自定義的pospp來存儲碰撞產生前一幀的position,以此來作為箭插入地面后的永久坐標選中粒子,添加自定義的每粒子屬性pospp,類型還是vector(矢量)
15. 右擊pospp添加runtime before daynamics的表達式,這里我直接在原先的表達式的做些添加就可以了,修改后如下float $speed = mag(arrow_particleshape.velocity);if($speed != 0){arrow_particleshape.dirpp = arrow_particleshape.velocity;arrow_particleshape.pospp = arrow_particleshape.position;}else{arrow_particleshape.position = arrow_particleshape.pospp;}后果有朋友看不懂,我再解釋一下其意思:
每過一幀,只要粒子速度不為零,我就將粒子的坐標值position賦給pospp,一旦產生碰撞,速度為零了,那么也就停止了position賦值給pospp這一操作,我的pospp存儲的值是粒子停止前一幀所處的位置,接下來,我們反過來將這一固定了的值賦給position,以防止例子因為重力場的作用會出現position不停地在一個范圍內抖動的結果。
16.至此,整個過程也就差不多完成了,你也可以再為粒子添加一些擾亂場,不用擔心箭射到地面出現抖動情況了
ok!教程結束,希望本教程對大家有所幫助!
新聞熱點
疑難解答