昨天學會了如何利用粒子系統做爆炸特效,今天我們要來將特效套用至專案中,來實作苦力怕自爆的效果,並且利用更多層的爆炸、由黑到白不同的顏色,來模擬得更真實一些。

Photo by Cristian Escobar on Unsplash
這是本系列第 18 篇,如果還沒看過第 17 篇可以點以下連結前往:
用 Three.js 來當個創世神 (17):粒子系統 - 爆炸
在實作之前,最重要的是先觀察預期的爆炸效果是怎麼樣的,這邊找到另一個有慢動作的苦力怕爆炸影片。從慢動作可以觀察到,苦力怕的爆炸有以下幾個重點:
瞭解了狀況之後以下就開始來實作了!

請參考完整原始碼及成果展示,使用 explosionTrigger 來觸發苦力怕爆炸、 resetCreeper 來重置苦力怕。
不知道是不是筆者每天被鐵人賽 deadline 追趕壓力大,實際上操作起來好像也蠻像舒壓小遊戲的。
 
這邊我們拿第 11 篇的模板來做,在那篇文章中,已經準備好了苦力怕爆炸的前置膨脹動作,而剩下的就是要將爆炸特效套上去並模擬得更像一些,就可以完成了。
首先先將上一篇中的 Explosion 物件加進去:
// points
const pointCount = 1000
const movementSpeed = 10
let explosion = [] // 爆炸物件陣列
let size = 10
const smokeTexture = textureLoader.load('./smoke.png')
class Explosion {
  constructor(x, y, z, color) {
    ...
    this.material = new THREE.PointsMaterial({
      size: size,
      color: color,
      map: smokeTexture,
      blending: THREE.CustomBlending,
      depthWrite: false,
      transparent: true,
      opacity: 0.7
    })
	 ...
  }
  ...
}
主要是做一些參數的調整來讓爆炸效果更像,除了比較基本的粒子數、爆炸速度、粒子尺寸外,這裡讓新增爆炸物件時還能定義粒子顏色,而這邊各層爆炸的顏色下面會指定由黑到白共五種顏色,但遇到一個問題是不管怎麼改都是白色,後來在各種 try & error 後也將各種融合模式試了一輪:
THREE.NoBlending
THREE.NormalBlending
THREE.AdditiveBlending
THREE.SubtractiveBlending
THREE.MultiplyBlending
THREE.CustomBlending
後來改為 THREE.CustomBlending 時就成功了,看文件這種融合模式還可以設定幾種不同的算法,不過文件也沒提到這幾種融合模式差在哪,稍微 google 一下後意外地找到這篇文章中有個 depthWrite: false 覺得很眼熟,設定這個屬性後爆炸粒子就可以正確穿透地板,也一起去把前面第 15 篇中關於 depthTest 的問題修正了。
詳細的原因應可參考這篇,大概瀏覽的意思應是
depthTest會關掉比較多遮蔽的效果,而其他圖學的內容之後有空再來深入研究。
this.explosionTrigger = function() {
  for (let i = 0; i < scene.children.length; i++) {
    const object = scene.children[i]
    // 場景內有苦力怕才爆炸
    if (object.name === 'creeper') {
      // 清除之前爆炸粒子
      if (explosion) {
        const len = explosion.length
        if (len > 0) {
          for (let i = 0; i < len; i++) {
            explosion[i].destroy()
          }
        }
        explosion.length = 0
      }
      // 移除苦力怕
      scene.remove(creeperObj.creeper)
      // 產生爆炸
      explosion[0] = new Explosion(0, 0, 0, 0x000000)
      explosion[1] = new Explosion(5, 5, 5, 0x333333)
      explosion[2] = new Explosion(-5, 5, 10, 0x666666)
      explosion[3] = new Explosion(-5, 5, 5, 0x999999)
      explosion[4] = new Explosion(5, 5, -5, 0xcccccc)
    }
  }
}
這邊利用 dat.GUI 做一個觸發爆炸的開關,首先簡單做了一下防呆,就是當場景中沒有苦力怕物件時就不做爆炸效果,若有的話則先清掉之前的爆炸粒子殘骸,然後在爆炸前先把苦力怕從場景中移除。
而這裡新的 explosion 改為陣列,總共觸發五個爆炸,分別給予不同的爆炸中心與由黑到白的顏色,讓爆炸的煙霧更逼真一些。
this.resetCreeper = function() {
  scene.add(creeperObj.creeper)
}
重置則非常單純,只是將苦力怕加回場景。
function render() {
  if (explosion) {
    const len = explosion.length
    if (len > 0) {
      for (let i = 0; i < len; i++) {
        explosion[i].update()
      }
    }
  }
  ...
  requestAnimationFrame(render)
  renderer.render(scene, camera)
}
爆炸動畫的設定也很單純,只做了每個爆炸的 update(),原本有考慮加上爆炸時間差的效果,但暫時還沒做出來,就只先將爆炸速度調快達到比較像的爆炸效果。
// 相機設定
camera = new THREE.PerspectiveCamera(
  60,
  window.innerWidth / window.innerHeight,
  20,
  1000
)
camera.position.set(50, 50, 50)
camera.lookAt(scene.position)
前面提到關於相機近平面距離這裡設 20。
還可以補上每一層爆炸間的時間差,效果會更逼真。
在粒子材質中,關於融合模式與 dapthTest、depthWrite 不算完全理解他們的作用,可以再深入研究。
今天完成了苦力怕的自爆動畫,也替粒子系統與專案中苦力怕相關的部分做個收尾,明天開始會進入第二部分來研究物理引擎,之後會開始實做遊戲中的射擊效果,我們明天見!