This tutorial will explain how to use clones to create a utility for particle effects. This will begin with using clones to create a "spark" effect, then optimize the code, and finally create an easy-to-use package for a user so that they can incorporate it into a project easily.
Instructions
Creating the Effect
First, you need to create the parent sprite. This sprite will be hidden and generate all the particles of the spark effect. The general plan of the code is:
- Create 10 clones, each of which is a particle of the spark.
- When the particles are created, they fly out and fall.
Name the sprite "Spark" and make its costume a single orange dot.
Create two local variables called "velocity x" and "velocity y". Make sure that they're set to "for this sprite only" so they can store values unique to each clone.
Now you will need to write the common velocity-arc script under a "when I start as a clone" block:
when [space v] key pressed repeat (10) // create 10 spark clones create clone of [myself v] end when I start as a clone set [velocity x v] to ((pick random (-5) to (5)) / (3)) // initial velocity set [velocity y v] to (pick random (1) to (10)) // initial velocity repeat (20) change x by (velocity x) change y by (velocity y) change [velocity y v] by (-1) // gravity end delete this clone // make sure to delete the clone after it is no longer needed
At this point, you can run the script! Just press space to see some sparks.
One of the issues with this script is that if you press space multiple times quickly, each spark duplicates itself. This is clearly wrong behavior; it happens because the clones also run the script that detects the space key. So we need to determine whether the script is running in the original sprite or clone. We will use a new local variable to store this:
when gf clicked // only the original sprite runs this hat
set [is clone? v] to [false] // original sprite is not a clone
when [space v] key pressed
if <(is clone?) = [false]> then // only run the script if it is the original sprite
repeat (10)
create clone of [myself v]
end
end
when I start as a clone
set [is clone? v] to [true] // the clone sets its variable to true
set [velocity x v] to ((pick random (-5) to (5)) / (3))
set [velocity y v] to (pick random (1) to (10))
repeat (20)
change x by (velocity x)
change y by (velocity y)
change [velocity y v] by (-1)
end
delete this clone
Optimizations and Improvements
In this section, we will add the following optimizations to the spark engine:
- Sparks will be created gradually, instead of suddenly.
- Sparks will fade out and "die" automatically, instead of waiting to touch the edge.
First, we hide the parent spark, because it should not be visible. We also make the spark creation routine a custom block. Then we add a small delay when creating a clone, which changes over time to give a more realistic effect:
when gf clicked
set [is clone? v] to [false]
hide
when I start as a clone
show
set [is clone? v] to [true]
set [velocity x v] to ((pick random (-5) to (5)) / (3))
set [velocity y v] to (pick random (1) to (10))
repeat (20)
change x by (velocity x)
change y by (velocity y)
change [velocity y v] by (-1)
end
delete this clone
define create effect at x: (x) y: (y)
go to x: (x) y: (y)
set [creation_delay v] to [-0.05]
if <(is clone?) = [false]> then
repeat (10)
change [creation delay v] by (0.001)
wait ((creation delay) * (creation delay)) secs
create clone of [myself v]
end
end
when [space v] key pressed
create effect at x: (1) y: (1)
To make sparks fade out, we simply change the ghost effect:
when gf clicked
set [is clone? v] to [false]
hide
when I start as a clone
show
set [is clone? v] to [true]
set [velocity x v] to ((pick random (-5) to (5)) / (3))
set [velocity y v] to (pick random (1) to (10))
repeat (20)
change [ghost v] effect by (5)
change x by (velocity x)
change y by (velocity y)
change [velocity y v] by (-1)
end
delete this clone
define create effect at x: (x) y: (y)
go to x: (x) y: (y)
set [creation delay v] to [-0.03]
if <(is clone?) = [false]> then
repeat (10)
change [creation delay v] by (0.001)
wait ((creation delay) * (creation delay)) secs
create clone of [myself v]
create clone of [myself v]
create clone of [myself v]
create clone of [myself v]
end
end
when [space v] key pressed
create effect at x: (1) y: (1)
We also create more sparks for a richer effect, and tweak the creation delay mildly.
Distributing the Code
To distribute the sparks library, we need to create a custom block which can be defined on any sprite. When called by the user, it should send a broadcast to the spark sprite to generate a spark effect.
We do this by creating a global list called "(spark) preferences" which contains a list of information about the spark library. It has, in order:
- X position to spawn
- Y position to spawn
- Color to spawn
Adding these touches, we have:
when [space v] key pressed create effect at x: (1) y: (1) define create effect at x: (x) y: (y) go to x: (x) y: (y) set [creation_delay v] to [-0.002] if <(is clone?) = [false]> then repeat (3) change [creation delay v] by (0.005) wait ((creation delay)*(creation delay)) secs create clone of [myself v] create clone of [myself v] create clone of [myself v] create clone of [myself v] end end when I start as a clone show set [is clone? v] to [true] set [velocity x v] to ((pick random (-5) to (5))/(3)) set [velocity y v] to (pick random (1) to (10)) repeat (20) change [ghost v] effect by (5) change x by (velocity x) change y by (velocity y) change [velocity y v] by (-1) end delete this clone when gf clicked set [is clone? v] to [false] hide when I receive [(spark) spawn v] set [color v] effect to (item (3) of [(spark) preferences v]) create effect at x: (item (1) of [(spark) preferences v]) y: (item (2) of [(spark) preferences v])
We also create the global block, and a small demo script:
when [space v] key pressed spawn spark effect at x: (mouse x) y: (mouse y) color: (pick random (1) to (200)) define spawn spark effect at x: (x) y: (y) color: (color) replace item (1) of [(spark) preferences v] with (x) replace item (2) of [(spark) preferences v] with (y) replace item (3) of [(spark) preferences v] with (color) broadcast [(spark) spawn v]
An issue here is that when you create a spark while an old one is running, the old spark effect stops. This is because of the broadcast. To avoid this, we use a trigger variable instead of a broadcast. We add a new item to our preferences list. Whenever a new spark is created, we simply set this to "new", and our library resets it back to blank once the effect has been created:
when gf clicked
set [is clone? v] to [false]
hide
when I start as a clone
show
set [is clone? v] to [true]
set [velocity x v] to ((pick random (-5) to (5)) / (3))
set [velocity y v] to (pick random (1) to (10))
repeat (20)
change [ghost v] effect by (5)
change x by (velocity x)
change y by (velocity y)
change [velocity y v] by (-1)
end
delete this clone
define create effect at x: (x) y: (y)
go to x: (x) y: (y)
set [creation delay v] to [-0.002]
if <(is clone?) = [false]> then
repeat (3)
change [creation delay v] by (0.005)
wait ((creation delay) * (creation delay)) secs
create clone of [myself v]
create clone of [myself v]
create clone of [myself v]
create clone of [myself v]
end
end
when gf clicked
forever
if <(item (4) of [(sparks) preferences v]) = [new]> then
replace item (4) of [(sparks) preferences v] with []
set [color v] effect to (item (3) of [(sparks) preferences v])
create effect at x: (item (1) of [(sparks) preferences v]) y: (item (2) of [(sparks) preferences v])
when [space v] key pressed
spawn spark effect at x: (mouse x) y: (mouse y) color: (pick random (1) to (200))
define spawn spark effect at x: (x) y: (y) color: (color)
replace item (1) of [(spark) preferences v] with (x)
replace item (2) of [(spark) preferences v] with (y)
replace item (3) of [(spark) preferences v] with (color)
replace item (4) of [(spark) preferences v] with [new]