-
Notifications
You must be signed in to change notification settings - Fork 6
en Basic PRT System
PRT is an abbreviation for 'Particle', used to distinguish it from other particle systems.
Compared to the vanilla Dust system, the PRT system provided by InnoVault offers greater customization, allowing developers to implement more complex particle effects more efficiently.
PRT system features:
- Multiple draw blend modes (Alpha blend, Additive blend, Non-premultiplied blend)
-
Shader support (attachable
ArmorShaderData) - History position/rotation caches (for trail effects)
- Lifetime management (automatic lifecycle tracking)
-
Global hooks (modify all particle behavior via
GlobalPRT) -
Local particle collections (manage particles in non-global contexts via
PRTGroup)
BasePRT is the base class for PRT entities. Inherit from it to create various special particle effects.
The implementation and development process of PRT entities is similar to that of projectiles.
Each PRT entity is an independent instance with its own field space — they do not interfere with each other.
| Property/Field | Type | Default | Description |
|---|---|---|---|
Texture |
string |
"" |
Texture path, supports auto-loading same-name .png |
TexValue |
Texture2D |
— | Gets the loaded texture (read-only) |
ID |
int |
— | Globally unique ID |
active |
bool |
false |
Whether this particle is active |
Time |
int |
0 |
Frames alive (auto-incremented) |
Lifetime |
int |
-1 |
Maximum lifetime in frames, -1 means infinite |
LifetimeCompletion |
float |
— | Lifetime ratio 0~1 (read-only) |
Position |
Vector2 |
— | World position |
Velocity |
Vector2 |
— | Movement velocity |
Origin |
Vector2 |
— | Draw origin |
Color |
Color |
— | Color |
Rotation |
float |
0 |
Rotation angle (radians) |
Scale |
float |
1 |
Scale multiplier |
Opacity |
float |
0 |
Opacity/fade value |
ai |
float[3] |
[0,0,0] |
AI data for custom behavior |
Frame |
Rectangle |
default |
Generic frame index rectangle |
PRTDrawMode |
PRTDrawModeEnum |
AlphaBlend |
Draw blend mode |
PRTLayersMode |
PRTLayersModeEnum |
InWorld |
Update and draw layer |
shader |
ArmorShaderData |
null |
Attached shader |
ShouldKillWhenOffScreen |
bool |
true |
Auto-destroy when off screen |
InGame_World_MaxCount |
int |
4000 |
Max count of this particle type in the world |
oldPositions |
Vector2[] |
null |
Historical position cache (requires manual initialization) |
oldRotations |
float[] |
null |
Historical rotation cache (requires manual initialization) |
Below is a simple particle example from InnoVaultExample
internal class ExamplePRT : BasePRT
{
// The Texture property doesn't need to be overridden — BasePRT has an auto-loading mechanism
// that automatically loads a same-name .png in the same directory.
// So, let's prepare a .png file called "ExamplePRT".
public override void SetProperty() {
// Set draw blend mode to additive blending (glow effect)
PRTDrawMode = PRTDrawModeEnum.AdditiveBlend;
Lifetime = Main.rand.Next(220, 360); // 220 to 360 tick lifetime
}
public override void AI() {
// A simple AI that randomly changes direction
if (--ai[0] <= 0) {
Velocity = Velocity.RotatedByRandom(0.3f);
ai[0] = Main.rand.Next(10);
}
Rotation += Velocity.X * 0.02f;
Color.A = (byte)(1 - LifetimeCompletion * 255f);
if (LifetimeCompletion > 0.8f) {
Color *= 0.9f;
}
}
public override bool PreDraw(SpriteBatch spriteBatch) => true;
}| Method | When Called | Description |
|---|---|---|
SetProperty() |
Called once when particle is spawned | Initialize instance data, similar to ModProjectile.SetDefaults
|
AI() |
Every frame update | Logic update, controls particle behavior. Position has already been updated by Velocity at this point |
ShouldUpdatePosition() |
Before AI each frame | Return false to disable automatic position updates |
PreDraw(SpriteBatch) |
Before drawing | Return true for default draw, return false for custom draw |
PostDraw(SpriteBatch) |
After default drawing | Runs regardless of what PreDraw returns |
DrawInUI(SpriteBatch) |
Manual call | Not called automatically, used for manual drawing in UI scenarios |
Kill() |
Manual call | Sets active to false, removed on next update |
| Mode | Description |
|---|---|
AlphaBlend |
Alpha blending (default), for normal particles |
NonPremultiplied |
Non-premultiplied blending |
AdditiveBlend |
Additive blending, for glow effects |
Each frame the system dynamically assigns particles to the corresponding draw batch based on PRTDrawMode, so it can be changed at runtime in real-time.
If a particle has a shader set, it will be additionally assigned to the shader draw batch.
| Mode | Description |
|---|---|
InWorld |
Update and draw in the world (default) |
NoDraw |
Update logic only, no drawing |
None |
No automatic update or draw, only loaded into the list |
Used to implement particle trails and similar effects. Must be initialized in SetProperty():
public override void SetProperty() {
// Initialize 15 position and rotation cache points
InitializeCaches(15);
}
public override void AI() {
// Update caches every frame
UpdatePositionCache(oldPositions.Length);
UpdateRotationCache(oldRotations.Length);
}Available methods:
-
InitializePositionCache(int length)— Initialize position cache -
InitializeRotationCache(int length)— Initialize rotation cache -
InitializeCaches(int length)— Initialize both position and rotation caches simultaneously -
UpdatePositionCache(int length)— Update position cache (shift forward one slot, record current position at the end) -
UpdateRotationCache(int length)— Update rotation cache
This class is responsible for most of the logical updates in the PRT system and is the core of the PRT system.
- Global particle total limit:
InGame_World_MaxPRTCount = 20000 - Each particle type can set an independent limit by overriding
InGame_World_MaxCount(default 4000)
Use the PRTLoader.NewParticle static method to spawn particles into the world:
// Generic method (recommended)
PRTLoader.NewParticle<ExamplePRT>(
player.Center, // Spawn position
Main.rand.NextFloat(MathHelper.TwoPi).ToRotationVector2() * 6, // Velocity
Color.White, // Color
Main.rand.NextFloat(1, 2) // Scale
);
// Via ID method
PRTLoader.NewParticle(
PRTLoader.GetParticleID<ExamplePRT>(), // Get particle ID
player.Center, // Position
velocity, // Velocity
Color.White, // Color
1f // Scale
);| Method | Description |
|---|---|
NewParticle<T>(pos, vel, color, scale) |
Spawn particle via generic type |
NewParticle(id, pos, vel, color, scale) |
Spawn particle via ID |
GetParticleID<T>() |
Get the internal ID of a particle type |
GetPRTInstance<T>() |
Get a new instance of the specified particle type |
GetPRTInstance(int id) |
Get a new particle instance via ID |
Note: The particle system is client-side only. On the server (
Main.dedServ),NewParticlereturnsnulldirectly and does not spawn any particles.
PRTGroup manages a set of local particles, independent from the global particle system. Suitable for scenarios where you need separate control over a batch of particles (e.g., particles attached to a specific NPC/projectile).
public class MyProjectile : ModProjectile
{
private PRTGroup _particles = new();
public override void AI() {
// Spawn particles into the local collection
_particles.NewParticle<ExamplePRT>(
Projectile.Center,
Vector2.UnitY * -2f,
Color.Cyan
);
// Manual update
_particles.Update();
}
public override bool PreDraw(ref Color lightColor) {
// Manual draw
_particles.Draw(Main.spriteBatch);
return true;
}
}| Method | Description |
|---|---|
NewParticle<T>(center, velocity, color, scale) |
Create and add a particle via generic type |
NewParticle(center, velocity, type, color, scale) |
Create and add a particle via ID |
Add(BasePRT particle) |
Directly add a particle instance |
Update() |
Update all particles in the collection |
Draw(SpriteBatch) |
Draw all particles in the world |
DrawInUI(SpriteBatch) |
Draw all particles in UI |
Clear() |
Clear all particles |
Any() / Any(int id)
|
Check if any particles exist / if particles of a specific ID exist |
PRTGroup implements IEnumerable<BasePRT>, allowing direct iteration with foreach.
Inheriting GlobalPRT allows you to modify the behavior of all particles, similar to the GlobalItem / GlobalNPC concepts in tModLoader.
public class MyGlobalPRT : GlobalPRT
{
// When a particle is spawned into the world
public override void OnSpawn(BasePRT prt) { }
// Before all particles update, return false to prevent all particle updates
public override bool PreUpdatePRTAll() => true;
// After all particles update
public override void PostUpdatePRTAll() { }
// Before a single particle draws, return false to prevent that particle from drawing
public override bool PreDrawPRT(SpriteBatch spriteBatch, BasePRT prt) => true;
// After a single particle draws
public override void PostDrawPRT(SpriteBatch spriteBatch, BasePRT prt) { }
}// Find a registered PRT instance from your mod
BasePRT prt = Mod.FindPRT<ExamplePRT>();
BasePRT prt2 = Mod.FindPRT("ExamplePRT");| Previous | Next |
|---|---|
| Basic VaultLoaden | Basic TP Entity |