GPUParticles2D/3D di Godot 4 — Panduan Lengkap

1. Pendahuluan

Godot 4 menggunakan GPUParticles2D dan GPUParticles3D sebagai node sistem partikel default. Keduanya berjalan sepenuhnya di GPU, sehingga jauh lebih cepat daripada padanan CPU-nya untuk jumlah partikel yang besar. ParticlesMaterial lama dari Godot 3 telah digantikan oleh ParticleProcessMaterial, yang menawarkan kontrol lebih besar atas perilaku partikel.

CPUParticles2D dan CPUParticles3D masih ada sebagai node fallback untuk perangkat yang tidak mendukung GPU compute atau ketika kamu perlu mengakses data partikel individual dari GDScript. Untuk sebagian besar kasus, partikel GPU adalah pilihan yang direkomendasikan.

2. GPUParticles2D vs CPUParticles2D

Fitur GPUParticles2D CPUParticles2D
Pemrosesan GPU (compute shader) CPU (thread utama)
Partikel maksimum 100.000+ secara efisien ~1.000-5.000 batas praktis
Sub-emitter Ya Tidak
Trail Ya Tidak
Akses per partikel Tidak (hanya GPU) Ya (dari GDScript)
Mode kompatibilitas Membutuhkan Vulkan/Metal/D3D12 Berfungsi di mana saja
Tipe material ParticleProcessMaterial Properti bawaan (tanpa material)
Tips: Gunakan GPUParticles2D secara default. Beralih ke CPUParticles2D hanya jika kamu membutuhkan dukungan renderer Compatibility, perlu membaca posisi partikel di GDScript, atau menargetkan perangkat keras yang sangat lawas.

3. Penyiapan Dasar

Untuk membuat efek partikel dasar, tambahkan sebuah node GPUParticles2D ke scene-mu dan tetapkan sebuah ParticleProcessMaterial padanya. Berikut penyiapan minimal dalam GDScript:

# Create a GPUParticles2D node via code
var particles = GPUParticles2D.new()
particles.amount = 50
particles.lifetime = 2.0
particles.explosiveness = 0.0  # 0 = continuous, 1 = all at once
particles.randomness = 0.5

# Create and assign the material
var mat = ParticleProcessMaterial.new()
mat.direction = Vector3(0, -1, 0)  # Note: uses Vector3 even in 2D
mat.initial_velocity_min = 50.0
mat.initial_velocity_max = 100.0
mat.gravity = Vector3(0, 98, 0)

particles.process_material = mat
add_child(particles)

Properti penting pada node GPUParticles2D itu sendiri:

  • amountJumlah partikel yang hidup pada satu waktu (default: 8)
  • lifetimeBerapa lama tiap partikel hidup dalam detik (default: 1.0)
  • one_shotMemancarkan sekali lalu berhenti (default: false)
  • explosiveness0.0 = aliran kontinu, 1.0 = semua partikel sekaligus
  • randomnessOffset acak pada timing emisi (0.0-1.0)
  • speed_scalePengali untuk kecepatan simulasi
  • emittingApakah sistem sedang memancarkan saat ini

4. ParticleProcessMaterial Secara Mendalam

ParticleProcessMaterial mengendalikan bagaimana tiap partikel berperilaku sepanjang masa hidupnya. Semua properti arah menggunakan Vector3, bahkan untuk partikel 2D (komponen Z cukup diabaikan).

Arah & Kecepatan

var mat = ParticleProcessMaterial.new()

# Direction: normalized vector for initial particle heading
mat.direction = Vector3(0, -1, 0)  # Upward in 2D (Y is flipped)

# Spread: cone angle in degrees (0 = straight line, 180 = hemisphere)
mat.spread = 45.0

# Initial velocity: particles spawn with speed in this range
mat.initial_velocity_min = 100.0
mat.initial_velocity_max = 200.0

# Gravity: constant acceleration applied every frame
mat.gravity = Vector3(0, 98, 0)  # Pulls downward in 2D

Kecepatan Sudut

Membuat partikel berputar saat bergerak. Berguna untuk serpihan, konfeti, atau dedaunan.

mat.angular_velocity_min = -180.0  # degrees per second
mat.angular_velocity_max = 180.0

Skala Sepanjang Masa Hidup

Gunakan sebuah CurveTexture untuk mengubah ukuran partikel sepanjang masa hidupnya:

var curve = Curve.new()
curve.add_point(Vector2(0.0, 1.0))  # Full size at birth
curve.add_point(Vector2(0.5, 1.2))  # Slightly larger at midlife
curve.add_point(Vector2(1.0, 0.0))  # Shrink to nothing at death

var curve_tex = CurveTexture.new()
curve_tex.curve = curve
mat.scale_curve = curve_tex

Damping & Attractor

Damping memperlambat partikel seiring waktu, menyimulasikan hambatan udara. Attractor (node terpisah) menarik partikel menuju sebuah titik.

mat.damping_min = 5.0
mat.damping_max = 10.0

5. Gradien Warna

Properti color_ramp pada ParticleProcessMaterial menerima sebuah GradientTexture1D untuk mengubah warna partikel secara mulus sepanjang masa hidupnya. Ini penting untuk efek api, asap, dan sihir yang realistis.

Contoh Gradien Api

var gradient = Gradient.new()
gradient.colors = PackedColorArray([
    Color(1.0, 1.0, 1.0, 1.0),    # White (hot core)
    Color(1.0, 1.0, 0.2, 1.0),    # Yellow
    Color(1.0, 0.5, 0.0, 1.0),    # Orange
    Color(0.8, 0.1, 0.0, 0.8),    # Red
    Color(0.2, 0.0, 0.0, 0.0),    # Dark red, transparent (fade out)
])
gradient.offsets = PackedFloat32Array([0.0, 0.15, 0.4, 0.7, 1.0])

var grad_tex = GradientTexture1D.new()
grad_tex.gradient = gradient
mat.color_ramp = grad_tex
Tips: Selalu akhiri gradienmu dengan alpha = 0 agar partikel memudar secara mulus alih-alih lenyap seketika.

Untuk partikel berwarna seragam, kamu juga bisa mengatur properti color polos alih-alih gradien:

mat.color = Color(0.2, 0.6, 1.0, 0.8)  # Semi-transparent blue

6. Bentuk Emisi

Bentuk emisi menentukan di mana partikel muncul. Diatur melalui emission_shape pada ParticleProcessMaterial.

Point

Semua partikel muncul di titik asal (origin) node. Bentuk default.

mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_POINT

Sphere / Sphere Surface

Partikel muncul secara acak di dalam sebuah bola (atau hanya pada permukaannya).

mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_SPHERE
mat.emission_sphere_radius = 50.0

# Surface only:
mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_SPHERE_SURFACE
mat.emission_sphere_radius = 50.0

Box

Partikel muncul secara acak di dalam sebuah volume berbentuk persegi panjang.

mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX
mat.emission_box_extents = Vector3(100, 10, 0)  # Wide and thin

Ring

Partikel muncul dalam bentuk cincin (donat), didefinisikan oleh radius dalam/luar dan tinggi.

mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_RING
mat.emission_ring_radius = 80.0       # Outer radius
mat.emission_ring_inner_radius = 60.0 # Inner radius (hole)
mat.emission_ring_height = 0.0        # Flat ring
mat.emission_ring_axis = Vector3(0, 0, 1)  # Ring normal
Tips: Kombinasikan EMISSION_SHAPE_BOX dengan rentang X yang lebar dan Y yang sempit untuk menciptakan efek setinggi tanah seperti jejak debu atau partikel langkah kaki.

7. Resep Praktis

Api

Gradien oranye-merah, kecepatan ke atas, sedikit sebaran, skala menurun sepanjang masa hidup.

func create_fire() -> GPUParticles2D:
    var p = GPUParticles2D.new()
    p.amount = 40
    p.lifetime = 0.8
    p.randomness = 0.3

    var mat = ParticleProcessMaterial.new()
    mat.direction = Vector3(0, -1, 0)
    mat.spread = 15.0
    mat.initial_velocity_min = 40.0
    mat.initial_velocity_max = 80.0
    mat.gravity = Vector3(0, -20, 0)  # Slight upward pull
    mat.damping_min = 5.0
    mat.damping_max = 10.0

    # Color ramp: white -> yellow -> orange -> red -> transparent
    var grad = Gradient.new()
    grad.colors = PackedColorArray([
        Color(1, 1, 1, 1), Color(1, 1, 0.2, 1),
        Color(1, 0.4, 0, 0.9), Color(0.6, 0.1, 0, 0.4),
        Color(0.2, 0, 0, 0)
    ])
    grad.offsets = PackedFloat32Array([0.0, 0.1, 0.35, 0.7, 1.0])
    var grad_tex = GradientTexture1D.new()
    grad_tex.gradient = grad
    mat.color_ramp = grad_tex

    # Scale: shrink over time
    var curve = Curve.new()
    curve.add_point(Vector2(0, 0.8))
    curve.add_point(Vector2(0.3, 1.0))
    curve.add_point(Vector2(1, 0.0))
    var curve_tex = CurveTexture.new()
    curve_tex.curve = curve
    mat.scale_curve = curve_tex

    p.process_material = mat
    return p

Asap

Gradien abu-abu, gerakan naik lambat, sebaran besar, alpha rendah untuk tampilan tipis mengambang.

func create_smoke() -> GPUParticles2D:
    var p = GPUParticles2D.new()
    p.amount = 25
    p.lifetime = 3.0
    p.randomness = 0.5

    var mat = ParticleProcessMaterial.new()
    mat.direction = Vector3(0, -1, 0)
    mat.spread = 35.0
    mat.initial_velocity_min = 10.0
    mat.initial_velocity_max = 30.0
    mat.gravity = Vector3(0, -5, 0)
    mat.damping_min = 2.0
    mat.damping_max = 5.0
    mat.angular_velocity_min = -30.0
    mat.angular_velocity_max = 30.0

    var grad = Gradient.new()
    grad.colors = PackedColorArray([
        Color(0.6, 0.6, 0.6, 0.0), Color(0.5, 0.5, 0.5, 0.3),
        Color(0.4, 0.4, 0.4, 0.2), Color(0.3, 0.3, 0.3, 0.0)
    ])
    grad.offsets = PackedFloat32Array([0.0, 0.15, 0.6, 1.0])
    var grad_tex = GradientTexture1D.new()
    grad_tex.gradient = grad
    mat.color_ramp = grad_tex

    # Scale: grow over time (smoke expands)
    var curve = Curve.new()
    curve.add_point(Vector2(0, 0.3))
    curve.add_point(Vector2(0.5, 1.0))
    curve.add_point(Vector2(1, 1.5))
    var curve_tex = CurveTexture.new()
    curve_tex.curve = curve
    mat.scale_curve = curve_tex

    p.process_material = mat
    return p

Hujan

Garis biru-putih, gravitasi ke bawah yang kuat, sebaran sempit, jumlah partikel tinggi.

func create_rain() -> GPUParticles2D:
    var p = GPUParticles2D.new()
    p.amount = 200
    p.lifetime = 1.0

    # Use a box emission to cover the screen width
    var mat = ParticleProcessMaterial.new()
    mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX
    mat.emission_box_extents = Vector3(600, 0, 0)
    mat.direction = Vector3(0.05, 1, 0)  # Slightly angled
    mat.spread = 3.0
    mat.initial_velocity_min = 400.0
    mat.initial_velocity_max = 500.0
    mat.gravity = Vector3(0, 200, 0)

    var grad = Gradient.new()
    grad.colors = PackedColorArray([
        Color(0.7, 0.8, 1.0, 0.6), Color(0.7, 0.85, 1.0, 0.4),
        Color(0.7, 0.85, 1.0, 0.0)
    ])
    grad.offsets = PackedFloat32Array([0.0, 0.8, 1.0])
    var grad_tex = GradientTexture1D.new()
    grad_tex.gradient = grad
    mat.color_ramp = grad_tex

    # Stretch particles to look like streaks
    mat.scale_min = 0.5
    mat.scale_max = 0.5

    p.process_material = mat
    return p

Percikan

Kuning-oranye, ledakan eksplosif, kecepatan awal tinggi, masa hidup singkat.

func create_sparks() -> GPUParticles2D:
    var p = GPUParticles2D.new()
    p.amount = 30
    p.lifetime = 0.5
    p.one_shot = true
    p.explosiveness = 1.0  # All at once

    var mat = ParticleProcessMaterial.new()
    mat.direction = Vector3(0, -1, 0)
    mat.spread = 180.0  # Full sphere
    mat.initial_velocity_min = 150.0
    mat.initial_velocity_max = 300.0
    mat.gravity = Vector3(0, 200, 0)  # Fall quickly
    mat.damping_min = 10.0
    mat.damping_max = 20.0

    var grad = Gradient.new()
    grad.colors = PackedColorArray([
        Color(1, 1, 0.8, 1), Color(1, 0.7, 0.1, 1),
        Color(1, 0.3, 0, 0.5), Color(0.5, 0.1, 0, 0)
    ])
    grad.offsets = PackedFloat32Array([0.0, 0.2, 0.6, 1.0])
    var grad_tex = GradientTexture1D.new()
    grad_tex.gradient = grad
    mat.color_ramp = grad_tex

    # Scale: start small, stay small
    mat.scale_min = 0.3
    mat.scale_max = 0.6

    p.process_material = mat
    return p

Salju

Partikel putih, turun perlahan dan lembut, sebaran lebar, sedikit hanyut horizontal.

func create_snow() -> GPUParticles2D:
    var p = GPUParticles2D.new()
    p.amount = 100
    p.lifetime = 5.0
    p.randomness = 0.8

    var mat = ParticleProcessMaterial.new()
    mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX
    mat.emission_box_extents = Vector3(500, 0, 0)
    mat.direction = Vector3(0.1, 1, 0)
    mat.spread = 10.0
    mat.initial_velocity_min = 20.0
    mat.initial_velocity_max = 40.0
    mat.gravity = Vector3(0, 10, 0)
    mat.damping_min = 1.0
    mat.damping_max = 3.0
    mat.angular_velocity_min = -45.0
    mat.angular_velocity_max = 45.0

    var grad = Gradient.new()
    grad.colors = PackedColorArray([
        Color(1, 1, 1, 0), Color(1, 1, 1, 0.8),
        Color(1, 1, 1, 0.7), Color(1, 1, 1, 0)
    ])
    grad.offsets = PackedFloat32Array([0.0, 0.05, 0.8, 1.0])
    var grad_tex = GradientTexture1D.new()
    grad_tex.gradient = grad
    mat.color_ramp = grad_tex

    mat.scale_min = 0.3
    mat.scale_max = 0.8

    p.process_material = mat
    return p

8. Partikel 3D (GPUParticles3D)

GPUParticles3D bekerja identik dengan GPUParticles2D tetapi beroperasi di ruang 3D. ParticleProcessMaterial yang sama digunakan. Perbedaan utama:

  • Ketiga sumbu Vector3 aktif (X, Y, Z)
  • Bentuk emisi bekerja dalam 3D penuh (bola adalah bola nyata, box adalah volume)
  • Kamu bisa menggunakan draw_pass_1 dengan sebuah QuadMesh atau mesh kustom untuk partikel billboard
  • Partikel mesh bisa menggunakan StandardMaterial3D atau ORMMaterial3D
# 3D fire torch example
var particles_3d = GPUParticles3D.new()
particles_3d.amount = 60
particles_3d.lifetime = 1.0

var mat = ParticleProcessMaterial.new()
mat.direction = Vector3(0, 1, 0)  # Upward in 3D
mat.spread = 20.0
mat.initial_velocity_min = 1.0
mat.initial_velocity_max = 2.0
mat.gravity = Vector3(0, -0.5, 0)  # Slight counter-gravity

# Emission from a small sphere
mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_SPHERE
mat.emission_sphere_radius = 0.2

particles_3d.process_material = mat

# Use a QuadMesh as the particle shape
var quad = QuadMesh.new()
quad.size = Vector2(0.3, 0.3)
particles_3d.draw_pass_1 = quad

# Billboard mode: particles always face the camera
mat.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED

add_child(particles_3d)
Tips: Untuk partikel mesh (serpihan, selongsong, pecahan), tetapkan sebuah mesh .tres kustom ke draw_pass_1 dan terapkan sebuah StandardMaterial3D padanya. Setiap partikel akan dirender sebagai salinan mesh tersebut.

9. Sub-Emitter

Godot 4 mendukung sub-emitter untuk efek partikel-di-atas-partikel. Sebuah sub-emitter adalah node GPUParticles2D/GPUParticles3D lain yang memancarkan partikel pada posisi tiap partikel induk. Ini ideal untuk:

  • Percikan yang beterbangan dari partikel api
  • Jejak asap di belakang ledakan kembang api
  • Tetesan cipratan saat tumbukan
  • Ledakan sekunder

Cara menyiapkan sub-emitter:

# 1. Create the sub-emitter node (must be a sibling or child)
var sub_sparks = GPUParticles2D.new()
sub_sparks.amount = 4
sub_sparks.lifetime = 0.3
sub_sparks.explosiveness = 1.0
sub_sparks.emitting = false  # Controlled by parent
var sub_mat = ParticleProcessMaterial.new()
sub_mat.direction = Vector3(0, -1, 0)
sub_mat.spread = 180.0
sub_mat.initial_velocity_min = 50.0
sub_mat.initial_velocity_max = 100.0
sub_sparks.process_material = sub_mat
add_child(sub_sparks)

# 2. Reference it from the parent material
var parent_mat: ParticleProcessMaterial = fire_particles.process_material
parent_mat.sub_emitter_mode = ParticleProcessMaterial.SUB_EMITTER_AT_END
# Also: SUB_EMITTER_AT_COLLISION, SUB_EMITTER_CONSTANT

# 3. Set the sub_emitter property on the parent node
fire_particles.sub_emitter = fire_particles.get_path_to(sub_sparks)

Mode sub-emitter:

  • SUB_EMITTER_CONSTANTMemancar terus-menerus dari tiap partikel induk
  • SUB_EMITTER_AT_ENDMemancar ketika partikel induk mati
  • SUB_EMITTER_AT_COLLISIONMemancar ketika partikel induk bertumbukan
Peringatan: Sub-emitter hanya berfungsi dengan GPUParticles, bukan CPUParticles. Setiap sub-emitter menambah beban GPU, jadi jaga jumlah partikel sub-emitter tetap rendah (4-8 per partikel induk).

10. Tips Performa

  • Tradeoff Amount vs Lifetime: Total partikel terlihat = amount. Jika kamu menaikkan lifetime, partikel menumpuk lebih lama. Kurangi amount untuk mengimbangi, atau naikkan untuk efek yang lebih padat.
  • Gunakan Visibility Range (3D): Atur visibility_range_end pada GPUParticles3D agar sistem partikel yang jauh berhenti dirender. Ini keuntungan besar untuk dunia terbuka.
  • FPS Tetap: Atur fixed_fps pada node partikel (mis. 30) untuk membatasi pembaruan simulasi. Partikel tetap terinterpolasi mulus tetapi memakai lebih sedikit waktu GPU.
  • One-shot untuk ledakan: Untuk efek seperti ledakan, gunakan one_shot = true dengan explosiveness = 1.0. Node lalu bisa dibebaskan setelah masa hidupnya berakhir.
  • Gunakan LOD untuk 3D: Kombinasikan visibility_range_begin/end dengan beberapa node partikel pada tingkat detail berbeda untuk efek partikel Level of Detail (LOD).
  • Hindari overdraw: Banyak partikel semi-transparan yang saling tumpang tindih = fill rate yang mahal. Kurangi amount dan gunakan partikel yang lebih besar dengan alpha lebih rendah sebagai gantinya.

11. Migrasi dari Godot 3 ke 4

Jika kamu memigrasikan proyek dari Godot 3 ke Godot 4, berikut perubahan utama untuk partikel:

Godot 3 Godot 4 Catatan
Particles2D GPUParticles2D Diganti nama
Particles (3D) GPUParticles3D Diganti nama
ParticlesMaterial ParticleProcessMaterial Diganti nama dengan fitur baru
CPUParticles2D CPUParticles2D Tidak berubah
CPUParticles CPUParticles3D Diganti nama untuk kejelasan
No sub-emitters sub_emitter_mode Baru di Godot 4
No trails trail_enabled Baru di Godot 4
flag_align_y particle_flag_align_y Properti diganti nama
Tips: Konverter proyek Godot menangani sebagian besar penggantian nama secara otomatis. Namun, setiap referensi ParticlesMaterial di GDScript perlu diperbarui secara manual menjadi ParticleProcessMaterial.

Otomatiskan Efek Partikel dengan MCP Pro

Ingin AI membuatkan efek partikel untukmu? Godot MCP Pro menyertakan alat partikel khusus dengan preset untuk api, asap, hujan, salju, percikan, dan lainnya. Kontrol penuh atas material, gradien, dan bentuk emisi dari satu percakapan.

  • create_particles
  • set_particle_material
  • set_particle_color_gradient
  • apply_particle_preset
  • get_particle_info
Dapatkan MCP Pro — $15