SpawnParticle (ZScript)
Note: This feature is for ZScript only. |
native void SpawnParticle(FSpawnParticleParams p)
Usage
Spawns a particle on the map. This acts as a struct-using replacement for A_SpawnParticleEx, due to how many parameters that method has.
This is a method of the LevelLocals struct, so to perform the spawn itself, it must be called as level.SpawnParticle
(level is a global pointer to an instance of LevelLocals). The FSPawnParticleParams struct must be set up before that, however.
Parameters
- p: A pointer to the FSpawnParticleParams struct that stores the parameters the function will use.
FSpawnParticleParams
![]() |
Warning: The default values for the fields in a struct are the same as the default values of a variable upon declaration (so, for example, for integer fields the default value is 0, for double fields it's 0.0, etc.). As such, fields like startalpha and color1 do not have the same default values as the parameters of A_SpawnParticleEx and must be set to the necessary value explicitly!
For example, you must explicitly set startalpha to 1.0 and size to a positive value, otherwise the particle will be invisible! |
The parameters the FSpawnParticleParams struct has are the same as the ones A_SpawnParticleEx uses. However, several parameters are squashed into Vector3 parameters, and some are renamed:
- int color1
- This parameter is what 'color1' is renamed to. Color can be applied to textured particles as well, resulting in tinted textures.
- Since this is an integer field, the default value for it is
0
, which means black. If you want to use a textured particle and NOT apply colorization to it, you need to explicitly setcolor1 = ""
.
- TextureID texture
- The texture used by the particle, same as A_SpawnParticleEx. If omitted, default untextured particles will be used. This takes a TextureID that can be obtained with TexMan.CheckForTexture.
- vector3 pos
- The absolute XYZ coordinate to spawn the particle in. It replaces the relative x/y/zoff parameters that the other particle spawn methods have.
- vector3 vel
- The velocity to apply to the particle on the XYZ axes, like the velx/y/z parameters it replaces, these values are absolute as well.
- vector3 accel
- The acceleration to apply to the particle on the XYZ axes. It replaces the accelx/y/z parameters that the other particle spawn methods have.
- int flags
- SPF_FULLBRIGHT — Makes the particle full bright.
- SPF_NOTIMEFREEZE — The spawned particle is not affected by the time freeze powerup or cheat.
- SPF_REPLACE — If the the particle limit is reached, the oldest particles will be removed to make room for particles with SPF_REPLACE.
- SPF_FACECAMERA — Makes the particle graphic face the camera. Like the BILLBOARDFACECAMERA actor flag.
- SPF_NOFACECAMERA — Makes the particle graphic face the opposite direction the camera. Like the BILLBOARDNOFACECAMERA actor flag.
- SPF_NEGATIVE_FADESTEP — Forces negative fadestep to be interpreted literally, causing the particle to fade in (for example, with this flag a fadestep of -0.1 will cause the particle's alpha to increase by 0.1 every tic). Without this flag, any negative fadestep value will cause the particle to gradually fade out over its lifetime.
- SPF_NO_XY_BILLBOARD - The particle does not have any sort of billboarding, causing it to render similarly to normal actor sprites, instead of facing the players' view at all times.
- The following are only supported by A_SpawnParticleEx and SpawnParticle (ZScript):
- SPF_ROLL — The particle is allowed to use its startroll, rollvel, and rollacc parameters.
- SPF_ROLLCENTER — Rolls the particle around the center of the graphic regardless of offsets, like the ROLLCENTER actor flag.
- SPF_LOCAL_ANIM — Spawns an animated particle whose animation runs independently of the games' timer. This means the graphics can be animated at different times, and that pausing the game also stops them from running.
- SPF_STRETCHPIXELS — Rolling particle graphics will not ignore aspect ratio correction and continue to appear stretched.
- Note: SPF_REL* flags are not supported, in contrast to A_SpawnParticleEx, since FSpawnparticleParams has no angle field. Use RotateVector to convert absolute offsets to relative according to a specified angle.
- int style
- The render style of the particle. STYLE_Normal and STYLE_Translucent have the same effect.
- For shaded render styles, the shading color is determined by the
color1
parameter. Note that even without STYLE_Shaded, ifcolor1
is not""
, the particle will be shaded to that color.
- int lifetime
- The time in tics the particle will exist for.
- double size
- The size of the particle. Note that it's the absolute size in map units, and that, if the particle is textured, its texture will be scaled to this size. If you wish to apply scale in the same way you'd apply it to an actor (i.e. relative to the size of the graphic), you will first need to obtain the texture's size with
TexMan.GetSize()
and then multiply the result by the desired scale (such as 0.5 for 50% of the size), and use that value as size. - Note that particles are always round/square. As such, non-square textures will be stretched to fit.
- double sizestep
- By how much the size of the particle will change every tic. Negative values will make it smaller, positive values will make it bigger. Note, this isn't relative scale, this is absolute value, just like size.
- double startalpha
- This parameter is what 'startalphaf' is renamed to.
- Since this is a double field, its default value is
0.0
Unless you explicitly set it to a higher value, your particle will spawn invisible!
- double fadestep
- Same as fadestepf in A_SpawnParticle: determines by how much the particle's alpha will be reduced every tic as follows:
- 0.0 — the particle's alpha won't change; the startalphaf value will be used throughout its lifetime. This value will be used by default unless you specify a different one.
- Positive value — this value will be subtracted from the particle's alpha each tic, fading it out. Note, this isn't affected by its lifetime: i.e. if the value is high enough, the particle may become completely invisible before its lifetime runs out, and conversely, if it's low enough, the particle won't completely fade out before its lifetime runs out and it disappears.
- Negative value — the particle will gradually fade out from its startalphaf to zero throughout its lifetime. (This is, essentially, the same as using a value of
startalphaf / lifetime
but without the need to manually precalculate it.) - Negative value and the SPF_NEGATIVE_FADESTEP flag is used — this value will be added to the particle's alpha each tic. This allows fading the particles in rather than out.
- double startroll
- The starting roll value in degrees. Requires the SPF_ROLL flag.
- double rollvel
- This value will be added to the particle's roll every tic. Requires the SPF_ROLL flag.
- double rollacc
- This value will be added to rollvel every tic (which allows you to increase or decrease the value by which the roll changes). Requires the SPF_ROLL flag.
The angle parameter does not exist in FSpawnParticleParams, since the SpawnParticle method uses absolute coordinates. This also means that the SPF_REL*
flags do nothing by extension.
Examples
This simple actor uses textures particles to spawn textured particles (using the PLSSB0 sprite from the PlasmaBall) that move in a flame-like manner.
Note that it spawns 5 particles per tic using a FOR loop, and you can choose how much randomization you want to choosing what parts of the definition are inside the loop and which are outside.
class ParticlePlasmaFire : Actor
{
Default
{
+NOINTERACTION
+NOBLOCKMAP
radius 16;
}
override void Tick()
{
Super.Tick();
if (isFrozen())
{
return;
}
FSpawnParticleParams fp;
fp.texture = TexMan.CheckForTexture('PLSSB0');
fp.flags = SPF_FULLBRIGHT|SPF_ROLL|SPF_REPLACE;
fp.color1 = "";
for (int i = 5; i > 0; i--)
{
fp.lifetime = random[fp](40, 80);
fp.pos.x = pos.x + frandom[fp](-radius, radius);
fp.pos.y = pos.y + frandom[fp](-radius, radius);
fp.pos.z = pos.z + frandom[fp](0, 16);
fp.vel.xy = (frandom[fp](-2, 2), frandom[fp](-2, 2));
fp.vel.z = frandom[fp](2, 4);
fp.accel.xy = -(fp.vel.xy * 0.035); //acceleration is aimed to the opposite of velocity
fp.accel.z = -(fp.vel.z / fp.lifetime);
fp.size = random[fp](4, 10);
fp.sizeStep = -(fp.size / fp.lifetime); //size reduces to 0 over lifetime
fp.startalpha = frandom[fp](0.75, 1.0);
fp.fadestep = -1;
fp.startRoll = frandom[fp](0, 360);
fp.rollvel = frandom[fp](-15, 15);
fp.rollacc = -(fp.rollvel / fp.lifetime); //rollvel reduces to 0 over lifetime
Level.SpawnParticle(fp);
}
}
}
This is an expanded version of a similar spawner that can be modified directly from the map editor with the help of user variables and editor keys. This allows specifying size, velocity and texture:
class CustomizableParticleFire : Actor
{
String user_partTexName;
TextureID partTexCached;
Color partColor;
Default
{
// $Title "Customizable Particle Fire"
// $Category "Decoration"
// $Arg0 "Particle size"
// $Arg0Tooltip "Size in map units. 0 makes it equal to the texture's size (if provided)."
// $Arg1 "Random offset range"
// $Arg1Tooltip "Range of the horizontal spawn offset from the spawner's center"
// $Arg2 "Horizontal speed"
// $Arg2Tooltip "This will be multiplied by 0.5-1.0"
// $Arg3 "Vertical speed"
// $Arg3Tooltip "This will be multiplied by 0.75-1.0"
// $Arg4 "Color"
// $Arg4Type 11
// $Arg4Enum { 1 = "None"; 2 = "Red"; 3 = "Orange"; 4 = "Yellow"; 5 = "Green"; 6 = "Dark green"; 7 = "Blue"; 8 = "Dark blue"; 9 = "Cyan"; 10 = "White"; 11 = "Black"; 12 = "Gray"; 13 = "Dark gray"; }
// $Arg4Tooltip "Color of the particles. When using textured particles, select None to use textures as-is."
+NOINTERACTION
+NOGRAVITY
Radius 16;
Height 16;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
// Cache the texture provided with the user variable (if any):
if (user_partTexName)
{
partTexCached = TexMan.CheckForTexture(user_partTexName);
}
// Convert the color in argument 4 to a valid color value:
switch (args[4])
{
default:
partColor = "";
break;
case 2:
partColor = color(255, 0, 0);
break;
case 3:
partColor = color(255, 128, 0);
break;
case 4:
partColor = color(255, 255, 0);
break;
case 5:
partColor = color(0, 255, 0);
break;
case 6:
partColor = color(0, 128, 0);
break;
case 7:
partColor = color(0, 0, 255);
break;
case 8:
partColor = color(0, 0, 128);
break;
case 9:
partColor = color(0, 255, 255);
break;
case 10:
partColor = color(255, 255, 255);
break;
case 11:
partColor = color(0, 0, 0);
break;
case 12:
partColor = color(128, 128, 128);
break;
case 13:
partColor = color(64, 64, 64);
break;
}
}
override void Tick()
{
Super.Tick();
// Do nothing if frozen:
if (isFrozen())
{
return;
}
FSpawnParticleParams p;
// We'll spawn 3 particles at once, but some values
// will be the same for all of them, so start with
// those values:
p.lifetime = TICRATE; //each particle will last for 1 second
p.startalpha = 1.0;
p.fadestep = -1;
p.color1 = partColor;
if (partTexCached.IsValid())
{
p.texture = partTexCached;
}
// If argument 0 is 0, try setting the particle's size to
// the size of the texture, if a valid one was found:
if (args[0] <= 0)
{
if (p.texture.isValid())
{
Vector2 size = TexMan.GetScaledSize(p.texture);
p.size = max(size.x, size.y);
}
else
{
p.size = 1;
}
}
else
{
p.size = args[0];
}
// Further values should be randomized for each particle,
// so the rest are wrapped in a FOR loop:
for (int i = 3; i > 0; i--)
{
// Randomize horizontal position. We want to randomize it
// in a round shape rather than a square one, so we'll take
// a vector where X is args[1] multiplied by 0.5-1.0 and Y is 0,
// and rotate it by 0-360 degrees. Then we'll by taking a
// vector equal to 0.5-1.0 of the value of argument 1, and
// rotating it randomly by 0-360 degrees. Then we'll append
// that vector to the spawner's position:
Vector3 partOfs;
partOfs.xy = Actor.RotateVector((args[1] * frandom[pfire](0.5, 1.0), 0), frandom[pfire](0, 360));
p.pos = level.Vec3Offset(self.pos, partOfs);
// Do the same with horizontal velocity:
p.vel.xy = Actor.RotateVector((args[2] * frandom[pfire](0.5, 1.0), 0), frandom[pfire](0, 360));
// And randomized provided vertical velocity:
p.vel.z = args[3] * frandom[pfire](0.75, 1.0);
// Lose a little of the vertical velocity over its lifetime:
p.accel.z = p.vel.z / (p.lifetime * -1.2);
// Change its horizontal direction over its lifetime
// to create a vertical sine wave:
p.accel.xy = p.vel.xy / (p.lifetime * -0.5);
level.SpawnParticle(p);
}
}
}