GPUParticles2D/3D dans Godot 4 — Guide complet

1. Introduction

Godot 4 utilise GPUParticles2D et GPUParticles3D comme nœuds de système de particules par défaut. Ceux-ci s'exécutent entièrement sur le GPU, ce qui les rend nettement plus rapides que leurs équivalents CPU pour de grandes quantités de particules. L'ancien ParticlesMaterial de Godot 3 a été remplacé par ParticleProcessMaterial, qui offre davantage de contrôle sur le comportement des particules.

CPUParticles2D et CPUParticles3D existent toujours comme nœuds de repli pour les appareils dépourvus de prise en charge du calcul GPU, ou lorsque vous devez accéder aux données de particules individuelles depuis GDScript. Dans la plupart des cas, les particules GPU sont le choix recommandé.

2. GPUParticles2D vs CPUParticles2D

Caractéristique GPUParticles2D CPUParticles2D
Traitement GPU (compute shader) CPU (thread principal)
Particules max. 100 000+ efficacement ~1 000-5 000 limite pratique
Sous-émetteurs Oui Non
Traînées Oui Non
Accès par particule Non (GPU uniquement) Oui (depuis GDScript)
Mode de compatibilité Nécessite Vulkan/Metal/D3D12 Fonctionne partout
Type de matériau ParticleProcessMaterial Propriétés intégrées (pas de matériau)
Astuce : Utilisez GPUParticles2D par défaut. Ne passez à CPUParticles2D que si vous avez besoin de la prise en charge du moteur de rendu Compatibility, de lire les positions des particules en GDScript, ou si vous ciblez du matériel très ancien.

3. Configuration de base

Pour créer un effet de particules simple, ajoutez un nœud GPUParticles2D à votre scène et attribuez-lui un ParticleProcessMaterial. Voici une configuration minimale en 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)

Propriétés clés du nœud GPUParticles2D lui-même :

  • amountNombre de particules vivantes à un instant donné (par défaut : 8)
  • lifetimeDurée de vie de chaque particule en secondes (par défaut : 1.0)
  • one_shotÉmettre une fois puis s'arrêter (par défaut : false)
  • explosiveness0.0 = flux continu, 1.0 = toutes les particules d'un coup
  • randomnessDécalage aléatoire du timing d'émission (0.0-1.0)
  • speed_scaleMultiplicateur de la vitesse de simulation
  • emittingIndique si le système émet actuellement

4. ParticleProcessMaterial en détail

ParticleProcessMaterial contrôle le comportement de chaque particule tout au long de sa durée de vie. Toutes les propriétés directionnelles utilisent Vector3, même pour les particules 2D (la composante Z est simplement ignorée).

Direction et vitesse

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

Vitesse angulaire

Fait tourner les particules pendant leur déplacement. Utile pour les débris, les confettis ou les feuilles.

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

Échelle sur la durée de vie

Utilisez une CurveTexture pour faire varier la taille des particules au cours de leur durée de vie :

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

Amortissement et attracteur

L'amortissement ralentit les particules avec le temps, simulant la résistance de l'air. Les attracteurs (nœuds distincts) attirent les particules vers un point.

mat.damping_min = 5.0
mat.damping_max = 10.0

5. Dégradés de couleur

La propriété color_ramp du ParticleProcessMaterial accepte une GradientTexture1D pour faire évoluer en douceur la couleur des particules au cours de leur durée de vie. C'est indispensable pour des effets réalistes de feu, de fumée et de magie.

Exemple : dégradé de feu

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
Astuce : Terminez toujours votre dégradé par alpha = 0 afin que les particules disparaissent en fondu au lieu de s'effacer brusquement.

Pour des particules d'une couleur uniforme, vous pouvez aussi définir une propriété color unie au lieu d'un dégradé :

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

6. Formes d'émission

La forme d'émission détermine où apparaissent les particules. Elle se définit via emission_shape sur le ParticleProcessMaterial.

Point

Toutes les particules apparaissent à l'origine du nœud. Forme par défaut.

mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_POINT

Sphere / Sphere Surface

Les particules apparaissent aléatoirement à l'intérieur d'une sphère (ou uniquement sur sa surface).

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

Les particules apparaissent aléatoirement à l'intérieur d'un volume rectangulaire.

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

Ring

Les particules apparaissent en forme d'anneau (donut), défini par un rayon interne/externe et une hauteur.

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
Astuce : Combinez EMISSION_SHAPE_BOX avec une large étendue en X et une faible étendue en Y pour créer des effets au ras du sol, comme des traînées de poussière ou des particules de pas.

7. Recettes pratiques

Feu

Dégradé orange-rouge, vitesse vers le haut, léger éparpillement, échelle décroissante au cours de la durée de vie.

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

Fumée

Dégradé gris, mouvement ascendant lent, large éparpillement, faible alpha pour un aspect vaporeux.

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

Pluie

Traînées bleu-blanc, forte gravité vers le bas, éparpillement étroit, grand nombre de particules.

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

Étincelles

Jaune-orange, éclatement explosif, vitesse initiale élevée, courte durée de vie.

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

Neige

Particules blanches, descente lente et douce, large éparpillement, légère dérive horizontale.

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. Particules 3D (GPUParticles3D)

GPUParticles3D fonctionne à l'identique de GPUParticles2D, mais opère dans l'espace 3D. Le même ParticleProcessMaterial est utilisé. Différences principales :

  • Les 3 axes de Vector3 sont actifs (X, Y, Z)
  • Les formes d'émission fonctionnent en 3D complète (les sphères sont de vraies sphères, les boîtes sont des volumes)
  • Vous pouvez utiliser draw_pass_1 avec un QuadMesh ou un mesh personnalisé pour des particules en billboard
  • Les particules de type mesh peuvent utiliser StandardMaterial3D ou 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)
Astuce : Pour les particules de type mesh (débris, douilles, fragments), attribuez un mesh .tres personnalisé à draw_pass_1 et appliquez-lui un StandardMaterial3D. Chaque particule sera rendue comme une copie de ce mesh.

9. Sous-émetteurs

Godot 4 prend en charge les sous-émetteurs pour des effets de particules sur particules. Un sous-émetteur est un autre nœud GPUParticles2D/GPUParticles3D qui émet des particules à la position de chaque particule parente. C'est idéal pour :

  • Des étincelles jaillissant des particules de feu
  • Des traînées de fumée derrière les explosions de feux d'artifice
  • Des gouttelettes d'éclaboussure lors d'une collision
  • Des explosions secondaires

Pour configurer des sous-émetteurs :

# 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)

Modes de sous-émetteur :

  • SUB_EMITTER_CONSTANTÉmet en continu depuis chaque particule parente
  • SUB_EMITTER_AT_ENDÉmet lorsque la particule parente meurt
  • SUB_EMITTER_AT_COLLISIONÉmet lorsque la particule parente entre en collision
Avertissement : Les sous-émetteurs ne fonctionnent qu'avec GPUParticles, pas avec CPUParticles. Chaque sous-émetteur augmente la charge GPU ; gardez donc le nombre de particules des sous-émetteurs bas (4-8 par particule parente).

10. Conseils de performance

  • Compromis quantité vs durée de vie : Total de particules visibles = amount. Si vous augmentez lifetime, les particules s'accumulent plus longtemps. Réduisez amount pour compenser, ou augmentez-le pour des effets plus denses.
  • Utiliser la Visibility Range (3D) : Définissez visibility_range_end sur GPUParticles3D pour que les systèmes de particules éloignés cessent d'être rendus. C'est un gain considérable pour les mondes ouverts.
  • FPS fixe : Définissez fixed_fps sur le nœud de particules (p. ex. 30) pour limiter les mises à jour de la simulation. Les particules continuent d'interpoler en douceur mais consomment moins de temps GPU.
  • One-shot pour les éclats : Pour les effets de type explosion, utilisez one_shot = true avec explosiveness = 1.0. Le nœud peut ensuite être libéré une fois la durée de vie écoulée.
  • Utiliser le LOD pour la 3D : Combinez visibility_range_begin/end avec plusieurs nœuds de particules à différents niveaux de détail pour des effets de particules à niveau de détail (LOD).
  • Éviter le surtracé (overdraw) : De nombreuses particules semi-transparentes qui se chevauchent = un fill rate coûteux. Réduisez amount et utilisez plutôt des particules plus grandes avec un alpha plus faible.

11. Migration de Godot 3 vers 4

Si vous migrez un projet de Godot 3 vers Godot 4, voici les principaux changements concernant les particules :

Godot 3 Godot 4 Remarques
Particles2D GPUParticles2D Renommé
Particles (3D) GPUParticles3D Renommé
ParticlesMaterial ParticleProcessMaterial Renommé avec de nouvelles fonctionnalités
CPUParticles2D CPUParticles2D Inchangé
CPUParticles CPUParticles3D Renommé pour plus de clarté
No sub-emitters sub_emitter_mode Nouveau dans Godot 4
No trails trail_enabled Nouveau dans Godot 4
flag_align_y particle_flag_align_y Propriété renommée
Astuce : Le convertisseur de projet de Godot gère automatiquement la plupart des renommages. Cependant, toute référence à ParticlesMaterial dans GDScript devra être mise à jour manuellement vers ParticleProcessMaterial.

Automatiser les effets de particules avec MCP Pro

Vous voulez que l'IA crée des effets de particules à votre place ? Godot MCP Pro inclut des outils de particules dédiés avec des préréglages pour le feu, la fumée, la pluie, la neige, les étincelles et plus encore. Contrôle total sur les matériaux, les dégradés et les formes d'émission depuis une seule conversation.

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