2D Shockwave Effect in Godot three different approaches - enlarge single sprite, particle system and shader

2D 衝擊波 (shockwave) 最常在爆炸時當輔助特效用,有時會在動作遊戲的收刀效果看到,就是一個擴散的圓環表現出空氣震波的感覺,下面會列出三種不同的實作方法。

將單一圖片放大

先用 Paint.NET 畫出白色圓形,背景是透明色的單一圖片,可以長得像下面三個其中一個。Paint.NET 畫這類圖的時候,有幾個小訣竅。

  1. 用 shift + eclipse shape 可以畫圓
  2. 用外掛效果 Effect -> Align Object 將物件放在中間
  

接下來有二種做法。

比較麻煩的做法是建立 Sprite Node,將 Texture 指定成上面的圖片,開啟 GDScipt,在 func _process(delta) 依時間改變 scale.x / scale.y。

簡單 (不用動到 GDScript) 的作法是建立 Particle2D Node

  1. 指定 Texture
  2. Amount: 1
  3. One Shot: true
  4. Physical Material 建立 Paritcle Material 並將 Gravity 和 Directure 都設定為 0
  5. Particle Material -> Scale -> Scale Curve 加入隨時間變化的曲線圖並自己從小拉到大

要讓畫面漂亮點,可以更改 Color -> Color Curve 更改透明度。或是 Hue Variation 改成紅綠紫火焰效果。

圖片解析度很差,抱歉。

粒子系統 (Particle System)

先做一個光球,用 Paint.NET 就是做一個白色圓形,然後用 Effect -> Blurs -> Guassian Blur 模糊化,素材自己做,版權不用愁。:)

建立 Particle2D Node,指定 Texture,Physical Material -> Particle Material,然後 Particle Material 設定:

  1. explsion = 1 (所有粒子同時射出)
  2. 建立 Particle Material
  3. 關掉 Gravity
  4. Direction x, y, z = 0
  5. Direction -> Spread = 180 (朝四周發射出去)
  6. Amount 設定大一點

要調整速度,可以從 Initial Velocity (初始速度),Damping (阻泥)。如果希望顏色越來越透明就改 Color -> Color Curve。希望有帥氣的光暈可以從 Hue Variation 和 Color 透明色著手。

著色器 Shader

下面的 shader code 並沒有直接在畫面上畫東西,而是用畫面上已經有的像素去取代目前的像素,所以沒背景是看不出效果的,因此上圖借了個背景用。

用著色器做出的衝擊波其實跟做水波作法是一樣。

  1. 在需要做效果的最上層的 Node (需要是 CanvasItem Node),加入 Material -> Shader Material -> Shader,點擊開啟 shader language editor。
  2. 開始寫 shader language
shader_type canvas_item;

uniform float force = 0.0;
uniform vec2 center = vec2(0,0);
uniform float size = 10;
uniform float thinkness = 0.01;

void fragment()
{
    float ratio = TEXTURE_PIXEL_SIZE.x / TEXTURE_PIXEL_SIZE.y;
    vec2 scaledUV = ( UV - vec2(0.5, 0.0) ) / vec2( ratio, 1.0 ) + vec2(0.5, 0.0);
    float mask = ( 1.0 - step(size, length( scaledUV - center ))) * step( size - thinkness, length(scaledUV - center ));
    vec2 disp = normalize(scaledUV - center) * force * mask;
    COLOR = texture(TEXTURE, UV - disp);
}

Shader language 請看其他教學,那是顯卡用的語言,簡單的說:

  • 每一個 pixel 會 call fragement() 一次。
  • uniform 跟 GDScript 的 export 一樣,會在編輯器 Node Panel 看到該參數,也可以在 GDScript runtime 修改。
  • 顯卡不適合做 condition 判斷 (if/while/for),所以在 code 中是看不到的,比大小時是用 step()。
  • UV 座標指的是假設整個畫布長寬都是 1.0 (不管畫面是否真的是 1:1),該 pixel 的位置會在哪,正中間就是(0.5,0.5)。
  • scaledUV 是參考 ratio 算出的 UV (因為 shockwave 是圓的,不是橢圓)。
  • mask 是一個甜甜圈,在圈圈裡的 pixel 我們才會改值 (disp > 0)
  • disp 距離中心點越遠數值就越大。
  • COLOR = 是修改目前 pixel 的顏色
  • texture(TEXTURE, UV) 是取得位在UV座標的 pixel 顏色

然後開啟 Node 的 GDScipt,在執行期間更改 shader uniform parameter 的方法如下。

# increase size over time
var size = 0.0
material.set_shader_param("size", size)

雖然效果上 shader 做的最酷,single image 看起來很2D古風 (?),但是這跟遊戲風格有關,太寫實也不見得好。

其他方法

還有一種方法是覆寫 Node2D 的 func _draw(),利用 draw_circle() 自己畫。但是都用 Game Engine 了還自己畫有點找麻煩的感覺。