说真的这版也太冷清了,
隔壁版每天上百个人在吵台湾为啥做不出ACG产业,
真的开来讨论实作ACG的版一个星期才一篇文章,
实在让人有点想苦笑 XD
人气那么低,让我来灌个水吧,
如果图片网址太长开不了可以看这里
https://www.yhorng.com/blog/?p=384
这篇是要写我知道做 2D 冲击波 (shockwave)
的几种方法。
冲击波最常在爆炸时当辅助特效用,
有时会在动作游戏的收刀效果看到,
就是一个扩散的圆环表现出空气震波的感觉,
下面会列出三种(?)不同的实作方法。
方法A: 将单一图片放大
先用 Paint.NET 画出白色圆形,
背景是透明色的单一图片,可以长得像下面。
https://www.yhorng.com/blog/wp-content/uploads/2021/05/shockwave-edge.png
Paint.NET 画这类图的时候,有几个小诀窍。
- 用 shift + eclipse shape 可以画圆
- 用 plug-in 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
加入随时间变化的scale曲线图并从小拉到大
要让画面漂亮点,
可以更改 Color -> Color Curve 更改透明度。
或是 Hue Variation 改成红绿紫火焰效果。
https://www.yhorng.com/blog/wp-content/uploads/2021/05/ezgif-7-b8b9a3353a57.gif
图片分辨率很差,抱歉。
方法B: 粒子系统 (Particle System)
先做一个光球,
用 Paint.NET 就是做一个白色圆形,
然后用 Effect -> Blurs -> Guassian Blur 模糊化,
素材自己做,版权不用愁。:)
https://www.yhorng.com/blog/wp-content/uploads/2021/05/spotlight_1_alpha_brightness.png
建立 Particle2D Node,
指定 Texture,Physical Material -> Particle Material,
然后 Particle Material 设定:
- explsion = 1 (所有粒子同时射出)
- 建立 Particle Material
- 关掉 Gravity
- Direction x, y, z = 0
- Direction -> Spread = 180 (朝四周发射出去)
- Amount 设定大一点
https://www.yhorng.com/blog/wp-content/uploads/2021/05/ezgif-7-3ecad24018b8.gif
要调整速度,
可以从 Initial Velocity (初始速度),Damping (阻泥)。
如果希望颜色越来越透明就改 Color -> Color Curve。
希望有帅气的光晕可以从 Hue Variation 和 Color 透明色着手。
方法C: 着色器 (Shader)
https://www.yhorng.com/blog/wp-content/uploads/2021/05/ezgif-7-9e631a6b5967.gif
下面的 shader code 并没有直接在画面上画东西,
而是用画面上已经有的像素去取代目前的像素,
所以没背景是看不出效果的,
因此上图借了个背景用。
用着色器做出的冲击波其实跟做水波作法是一样。
1. 在需要做效果的最上层的 Node
(需要是 CanvasItem Node)
2. 加入 Material -> Shader Material -> Shader
点击开启 shader language editor。
3. 开始写 shader language
<code>
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);
}
</code>
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 的方法如下。
<code>
# increase size over time
var size = 0.0
material.set_shader_param("size", size)
</code>
虽然效果上 shader 做的最酷,single image 看起来很2D古风 (?),但是这跟游戏风格
有关,太写实也不见得好。
方法4: 其他方法
还有一种方法是覆写 Node2D 的 func _draw(),
利用 draw_circle() 自己画。
但是都用 Game Engine 了还自己画有点找麻烦的感觉。