Draft:Particles Tutorial
Displaying a static particle
To display a particle effect in a static location:
local anim_data = RogueEssence.Content.AnimData("Lock_On", 3, -1, -1, 255, Dir8.None)
local emitter = RogueEssence.Content.SingleEmitter(anim_data)
GROUND:PlayVFX(emitter, activator.Bounds.Center.X, activator.Bounds.Center.Y)
First, we create an AnimData object, which has the animation we want to display. This AnimData has the following parameters listed:
- AnimIndex: The string ID of the particle animation to play. Here, we set it to "Lock_On", so the particle uses the Lock-On animation.
- FrameTime: An integer amount of time, in frames, to spend on each frame of the animation. Here, we set it to 3, so the Lock-On animation will play at 60/3, or 20, FPS.
- StartFrame: The frame to start the animation on. Setting it to -1, as we do here, will cause the animation to play from the beginning.
- EndFrame: The frame to end the animation on. Setting it to -1, as we do here, will cause the animation to end after the last frame.
- Alpha: The opacity of the particle. Set it to 255 to make it completely opaque, or 0 to make it completely transparent.
- AnimFlip: Determines whether the particle is flipped in a particular direction. We don't want to flip the particle, so we just set it to Dir8.None.
Next, we create our particle emitter. The SingleEmitter displays a single particle in a single location, and plays its animation to completion. We pass the SingleEmitter our animation data, setting it to play that particle animation.
Now that we have our emitter all set up, we just have to play it. The "activator" here is a reference to the character that the particles will play at the position of.
Once an emitter has been created, it can be used for multiple particles at several different locations. As an example, this code plays the above Lock-On particle four times around a central point:
GROUND:PlayVFX(emitter, activator.Bounds.Center.X - 16, activator.Bounds.Center.Y) GROUND:PlayVFX(emitter, activator.Bounds.Center.X + 16, activator.Bounds.Center.Y) GROUND:PlayVFX(emitter, activator.Bounds.Center.X, activator.Bounds.Center.Y - 16) GROUND:PlayVFX(emitter, activator.Bounds.Center.X, activator.Bounds.Center.Y + 16)
Displaying a moving particle
The MoveToEmitter is a particle emitter that moves from one point in space to another over a fixed time.
This code creates a moving Mist Ball particle.
local emitter = RogueEssence.Content.MoveToEmitter()
emitter.Anim = RogueEssence.Content.AnimData("Mist_Ball", 3, -1, -1, 127, Dir8.None)
emitter.ResultLayer = RogueEssence.Content.DrawLayer.Front
emitter.OffsetStart = RogueElements.Loc(-50, 0)
emitter.OffsetEnd = RogueElements.Loc(50, 0)
emitter.HeightStart = -16
emitter.HeightEnd = 16
emitter.MoveTime = 180
GROUND:PlayVFX(emitter, activator.Bounds.Center.X, activator.Bounds.Center.Y)
The AnimData for the MoveToEmitter is formatted the same was as it was for the Lock-On particle. The only difference, aside from the particle name, is that we've set the Alpha to 127, so the particle is translucent.
However, this MoveToEmitter has several parameters of its own that control how the particle moves:
- ResultLayer: We can manually specify a layer that the particle will be drawn on. Setting it to RogueEssence.Content.DrawLayer.Front ensures that it renders in front of all other particles. The layers you can use are:
- Under: Draws on the floor, behind all entities and tiles.
- Bottom: Draws on the floor, behind entities, but not tiles.
- Back: Draws in front of entities if placed at a higher Y coordinate, but draws behind entities in a tie.
- Normal: Draws behind entities if placed at a lower Y coordinate, but draws in front of entities in a tie.
- Front: Draws in front of entities.
- Top: Draws on top of everything else.
- NoDraw: Does not render.
- OffsetStart: Controls where the particle starts, relative to its display position. This particle will start 50 pixels to the left of Pikachu.
- OffsetEnd: Controls where the particle ends, relative to its display position. This particle will end 50 pixels to the right of Pikachu.
- HeightStart: Adds an additional height offset to the particle at the start of its journey. Note that the height here is added to the object, so unlike with locations, increasing the height value will cause the particle to rise. This particle will start 16 units below Pikachu.
- HeightEnd: Adds an additional height offset to the particle at the end of its journey. This particle will end 16 units above Pikachu.
- MoveTime: Controls the lifetime of the particle. It goes in frames, so our MoveTime of 45 will cause the particle to be displayed for three quarters of a second.
Note that, unlike a SingleEmitter, a MoveToEmitter will loop its particle animation until its MoveTime has elapsed. So, the Mist Ball animation will stay looping until it completes its path.
This emitter can also be useful for creating a spray of particles in a given direction.
[TODO]
Creating particles in an area
Creating a ring of particles
The StaticAreaEmitter is used to create a burst of multiple different particle types in a ring.
This code creates a ring of blue music note particles around the emitter.
local anim_list = luanet.make_array(RogueEssence.Content.AnimData, {RogueEssence.Content.AnimData("Music_Notes", 30, 0, 0, 255, Dir8.None)})
local emitter = RogueEssence.Content.StaticAreaEmitter(anim_list)
emitter.Layer = RogueEssence.Content.DrawLayer.Front
emitter.Range = 32
emitter.Bursts = 5
emitter.ParticlesPerBurst = 8
emitter.BurstTime = 10
GROUND:PlayVFX(emitter, chara.Bounds.Center.X, chara.Bounds.Center.Y)
Unlike with previous emitters, the StaticAreaEmitter takes an array of AnimData objects. So, we first create an array of AnimData objects, and then populate it with the particle types we want. Here, we just want one particle type: the blue music note.
The Layer works the same as the DrawLayer from MoveToEmitter earlier. However, this StaticAreaEmitter has its own properties:
- Range: The radius around which particles are generated.
- Bursts: How many separate times particles are spawned.
- ParticlesPerBurst: How many particles are created at random points in the radius in each burst.
- BurstTime: The delay between each burst.
Multiple different particle types can be passed to this emitter, and they will be selected from randomly. Replace the anim_list definition above with this code, and four different music note particles will be used.
local anim_list = luanet.make_array(RogueEssence.Content.AnimData, {
RogueEssence.Content.AnimData("Music_Notes", 30, 0, 0, 255, Dir8.None),
RogueEssence.Content.AnimData("Music_Notes", 30, 10, 10, 255, Dir8.None),
RogueEssence.Content.AnimData("Music_Notes", 30, 20, 20, 255, Dir8.None),
RogueEssence.Content.AnimData("Music_Notes", 30, 30, 30, 255, Dir8.None)
})
Creating an area of static particles
The FiniteAreaEmitter is used to create particles in a whole circular area, and not just at the edge. The particles start at the center and spread out to the edge over a fixed period of time.
This code creates a mixture of leaf particles and music notes in an area centered around the entity.
local anim_list = luanet.make_array(RogueEssence.Content.AnimData, {
RogueEssence.Content.AnimData("Leaf_Storm_Leaf", 30, 0, 0, 255, Dir8.None),
RogueEssence.Content.AnimData("Leaf_Storm_Leaf", 30, 0, 0, 255, Dir8.None),
RogueEssence.Content.AnimData("Leaf_Storm_Leaf", 30, 0, 0, 255, Dir8.None),
RogueEssence.Content.AnimData("Leaf_Storm_Leaf", 30, 0, 0, 255, Dir8.None),
RogueEssence.Content.AnimData("Music_Notes", 30, 0, 0, 255, Dir8.None),
RogueEssence.Content.AnimData("Music_Notes", 30, 10, 10, 255, Dir8.None),
RogueEssence.Content.AnimData("Music_Notes", 30, 20, 20, 255, Dir8.None),
RogueEssence.Content.AnimData("Music_Notes", 30, 30, 30, 255, Dir8.None)
})
local emitter = RogueEssence.Content.FiniteAreaEmitter(anim_list)
emitter.Layer = RogueEssence.Content.DrawLayer.Front
emitter.Range = 48
emitter.Speed = 96
emitter.TotalParticles = 30
GROUND:PlayVFX(emitter, chara.Bounds.Center.X, chara.Bounds.Center.Y)
The FiniteAreaEmitter's relevant parameters are:
- Range: The radius particles will extend to at the end of the burst.
- Speed: The amount of pixels the radius extends by per second. Here, we have it set to 96, so it reaches the end in half a second.
- TotalParticles: The total amount of particles created in the burst.
Creating an area of moving particles
If you want the particles to move in a certain direction after being spawned, use a FiniteSprinkleEmitter.
This code creates a slow cloud of Ice Shard sparkle particles that drift slowly down and to the side:
local anim_list = luanet.make_array(RogueEssence.Content.AnimData, {
RogueEssence.Content.AnimData("Ice_Shard_Hit_Sparkle", 120, 1, 1, 255, Dir8.None),
RogueEssence.Content.AnimData("Ice_Shard_Hit_Sparkle", 120, 2, 2, 255, Dir8.None),
RogueEssence.Content.AnimData("Ice_Shard_Hit_Sparkle", 120, 3, 3, 255, Dir8.None)
})
local emitter = RogueEssence.Content.FiniteSprinkleEmitter(anim_list)
emitter.Layer = RogueEssence.Content.DrawLayer.Front
emitter.Range = 80
emitter.Speed = 80
emitter.HeightSpeed = -16
emitter.SpeedDiff = 12
emitter.TotalParticles = 60
GROUND:PlayVFX(emitter, chara.Bounds.Center.X, chara.Bounds.Center.Y)
In addition to all the variables possessed by the FiniteAreaEmitter, the FiniteSprinkleEmitter has these additional variables:
- HeightSpeed: How much the particles' height will change as they drift. Here, we have it set to -16, so each sparkle goes down by 16 pixels from start to finish.
- SpeedDiff: The maximum extent to which the particles will drift to the side as they move.
Making particles move to and from a specific point
Particles moving from the exterior to the center
The FiniteGatherEmitter is used to spawn particles at the edge of its range, then have them move towards the center. Like the FiniteAreaEmitter, it can take a list of multiple particles to spawn randomly.
This example spawns Ice Shard sparkle particles around the character and has them move towards the origin.
local anim_list = luanet.make_array(RogueEssence.Content.AnimData, {
RogueEssence.Content.AnimData("Ice_Shard_Hit_Sparkle", 120, 1, 1, 255, Dir8.None),
RogueEssence.Content.AnimData("Ice_Shard_Hit_Sparkle", 120, 2, 2, 255, Dir8.None),
RogueEssence.Content.AnimData("Ice_Shard_Hit_Sparkle", 120, 3, 3, 255, Dir8.None)
})
local emitter = RogueEssence.Content.FiniteGatherEmitter(anim_list)
emitter.Layer = RogueEssence.Content.DrawLayer.Front
emitter.Bursts = 5
emitter.ParticlesPerBurst = 8
emitter.BurstTime = 10
emitter.TravelTime = 40
emitter.StartDistance = 64
emitter.EndDistance = 8
emitter.StartVariance = 16
GROUND:PlayVFX(emitter, chara.Bounds.Center.X, chara.Bounds.Center.Y)
This emitter spawns particles in bursts, much like the FiniteAreaEmitter. However, it has its own unique variables as well:
- TravelTime: How many frames it takes for the particles to move from the exterior to the origin.
- StartDistance: The radius at which the particles are spawned.
- EndDistance: The radius at which the particles stop moving. If this is larger than the StartDistance, then the particles will scatter randomly.
- StartVariance: A random variance applied to the StartDistance, so the particles don't all spawn at the same location.
- Cycles: Controls how many animation cycles the particles will play while converging. For example, if Cycles is set to 10, then the particles will loop their animation 10 times before reaching the center.
Particles moving from the center to the exterior
The FiniteReleaseEmitter spawns particles within a small radius, and the particles move to a larger radius before disappearing. It can also take a list of multiple particles to spawn randomly. Unlike the FiniteGatherEmitter, the particles will despawn when their animation is complete instead of when reaching a target radius.
This example creates heart and sparkle particles that travel outwards at a fast speed.
local anim_list = luanet.make_array(RogueEssence.Content.AnimData, {
RogueEssence.Content.AnimData("Captivate_Heart", 5, -1, -1, 255, Dir8.None),
RogueEssence.Content.AnimData("Ice_Shard_Hit_Sparkle", 5, -1, -1, 255, Dir8.None)
})
local emitter = RogueEssence.Content.FiniteReleaseEmitter(anim_list)
emitter.Layer = RogueEssence.Content.DrawLayer.Front
emitter.Bursts = 5
emitter.ParticlesPerBurst = 8
emitter.BurstTime = 10
emitter.StartDistance = 8
emitter.Speed = 120
The unique variables for the FiniteReleaseEmitter are:
- StartDistance: The radius at which the particles are spawned.
- Speed: How many pixels per second, NOT per frame, the particles move.
Creating background overlays with particles
To display a static background image for a given amount of time, a FlashEmitter is used.
local bg_anim = RogueEssence.Content.BGAnimData("Cosmic_Power", 0, 0, 0, 255, Dir8.None)
local emitter = RogueEssence.Content.FlashEmitter()
emitter.Anim = bg_anim
emitter.Layer = DrawLayer.Bottom
emitter.FadeInTime = 30
emitter.HoldTime = 120
emitter.FadeOutTime = 30
emitter.StartColor = Color.White
emitter.EndColor = Color.White
GROUND:PlayVFX(emitter, GAME:GetCameraCenter().X, GAME:GetCameraCenter().Y)
Here, we create a BGAnimData object to hold the animation for the background. We set the layer to be DrawLayer.Bottom, so the background image renders under all characters in the scene. When we play the emitter, we have to do so at the camera's position; otherwise, the background will be located at the top left of the current ground map.
The emitter itself has the following parameters:
- FadeInTime: How long it takes, in frames, for the background to fade in.
- HoldTime: How long, in frames, the background is displayed for.
- FadeOutTime: How long it takes, in frames, for the background to fade out.
- StartColor: Color wash applied to the background at the start of the animation. Here, it's set to Color.White, so no color wash is applied.
- EndColor: Color wash the background fades to by the end of the animation. Here, it's set to Color.White, so no color wash is applied.
This background is static. If you want to display a moving background, you need to use a FiniteOverlayEmitter.
This code creates a Leaf Storm background that moves quickly to the left, taking up the entire screen.
local bg_anim = RogueEssence.Content.BGAnimData("Leaf_Storm", 10, -1, -1, 255, Dir8.None)
local emitter = RogueEssence.Content.FiniteOverlayEmitter()
emitter.Anim = bg_anim
emitter.Layer = DrawLayer.Bottom
emitter.Movement = RogueElements.Loc(-80, 0)
emitter.TotalTime = 180
emitter.FadeIn = 10
emitter.FadeOut = 10
emitter.RepeatX = true
emitter.RepeatY = true
emitter.Color = Color.White
GROUND:PlayVFX(emitter, GAME:GetCameraCenter().X, GAME:GetCameraCenter().Y)
This emitter has the following parameters:
- TotalTime: How long the overlay lasts for, in frames.
- FadeIn: How long it takes the overlay to fade in, in frames. Cuts into TotalTime.
- FadeOut: How long it takes the overlay to fade out, in frames. Cuts into TotalTime.
- RepeatX: Whether the overlay loops horizontally.
- RepeatY: Whether the overlay loops vertically.
- Color: Applies a color wash to the background. Here, it's set to Color.White, so the default colors are used.











