7) Particle Effects (Pro)
Goal: build a responsive spark emitter, tune its behavior, and understand the performance choices that matter at high particle counts.
License: this tutorial requires the Pro GamePhysics feature.
Step 1: Reserve Capacity Once
Dim particles As Integer
particles = CreateParticleSystem(20000)
If particles = 0 Then
DebugPrint("Could not create particle system")
Terminate()
End If
Capacity is the maximum number of live particles, not the number created immediately. Storage is fixed up front so emission and simulation do not allocate per particle during gameplay.
Step 2: Describe the Emitter
SetParticleLife(particles, 0.45, 1.1)
SetParticleSpeed(particles, 90.0, 240.0)
SetParticleDirection(particles, -120.0, -60.0)
SetParticleGravity(particles, 0.0, 360.0)
SetParticleSize(particles, 0.6, 1.4)
Each emitted particle randomly chooses values inside these ranges. Negative 90 degrees points upward in screen coordinates; positive Y gravity pulls sparks back down.
Step 3: Emit from an Event
If MouseButton(0) Then
EmitParticles(particles, 18, MouseX(), MouseY())
End If
If MouseHit(1) Then
EmitParticles(particles, 500, MouseX(), MouseY())
End If
Continuous input produces a trail; a hit produces a one-shot burst. Emission stops at capacity, so the system remains bounded.
Step 4: Update and Draw Exactly Once Per Frame
UpdateParticles(particles, FrameDelta())
Cls()
SetDrawColor(255, 190, 55)
DrawParticles(particles)
Flip()
Simulation and rendering are separate. Calling each once keeps timing understandable and avoids accidentally doubling work.
Step 5: Add Bouncing Bounds
SetParticleBounds( _
particles, _
20, 80, _
ScreenWidth() - 40, _
ScreenHeight() - 100, _
0.65 _
)
The last value controls bounce: 0 removes rebound energy and 1 preserves it.
Step 6: Add an Optional Particle Image
Dim sparkImage As Integer
sparkImage = LoadImage("assets/spark.png")
If sparkImage <> 0 Then
SetImageHotspot( _
sparkImage, _
ImageWidth(sparkImage) / 2, _
ImageHeight(sparkImage) / 2 _
)
SetParticleImage(particles, sparkImage)
End If
Passing image handle 0 uses the fast native primitive renderer. Image particles are richer but carry more rendering work, so use them when the visual difference matters.
Step 7: Use Attraction Deliberately
SetParticleAttraction(particles, 700.0) ' Attraction
SetParticleAttraction(particles, -700.0) ' Repulsion
SetParticleAttraction(particles, 0.0) ' Disabled
Nonzero attraction introduces particle-to-particle forces. The runtime automatically switches between direct and adaptive multipole solving, but zero is still the cheapest choice for ordinary smoke, rain, and sparks.
Checkpoint: Interactive Spark Lab
Graphics(960, 600, False)
SetClsColor(8, 12, 22)
Dim fontRef As Integer
Dim particles As Integer
Dim attractionEnabled As Boolean
Dim running As Boolean
fontRef = LoadFont("", 18)
particles = CreateParticleSystem(20000)
running = True
If particles = 0 Then
Terminate()
End If
SetParticleLife(particles, 0.45, 1.1)
SetParticleSpeed(particles, 90.0, 240.0)
SetParticleDirection(particles, -120.0, -60.0)
SetParticleGravity(particles, 0.0, 360.0)
SetParticleSize(particles, 0.6, 1.4)
SetParticleBounds(particles, 20, 70, 920, 510, 0.65)
Do
If MouseButton(0) Then
EmitParticles(particles, 18, MouseX(), MouseY())
End If
If KeyHit(KEY_SPACE) Then
EmitParticles(particles, 600, MouseX(), MouseY())
End If
If KeyHit(KEY_A) Then
attractionEnabled = Not attractionEnabled
If attractionEnabled Then
SetParticleAttraction(particles, 700.0)
Else
SetParticleAttraction(particles, 0.0)
End If
End If
If KeyHit(KEY_C) Then
ClearParticles(particles)
End If
UpdateParticles(particles, FrameDelta())
Cls()
SetDrawColor(255, 185, 55)
DrawParticles(particles)
SetTextColor(235, 240, 255)
DrawText(fontRef, 18, 16, _
"Hold mouse: emit | Space: burst | A: attraction | C: clear")
DrawText(fontRef, 18, 42, _
"Live particles: " + Str(ParticleCount(particles)))
Flip()
If KeyHit(KEY_ESC) Then
running = False
End If
Loop Until Not running
FreeParticleSystem(particles)
FreeFont(fontRef)
Performance Checklist
- Create systems during level setup, not in the frame loop.
- Choose a realistic capacity and control the emission rate.
- Prefer primitive particles for dense effects.
- Leave attraction at zero unless the effect requires long-range interaction.
- Use
ParticleCountwhile profiling so the current workload is visible. - Clear or free systems when changing scenes instead of leaving invisible simulations alive.
Try It Yourself
Create three presets: upward fire, downward rain, and a zero-gravity explosion. Change only configuration values, not the update loop.
Functions Introduced
CreateParticleSystem, EmitParticles, UpdateParticles, DrawParticles, SetParticleBounds, and SetParticleAttraction.
Navigation: Previous | Course outline | Next: Build a Physics Game