--- title: Particles type: docs weight: 1 --- The particle system in Hytale provides rich visual effects through configurable emitters and renderers. Create explosions, trails, auras, and complex visual effects with precise control over behavior and appearance. **Package:** `com.hypixel.hytale.server.core.asset.type.particle` {{< cards cols="3" >}} {{< card link="#spawning-particles" title="Spawning" subtitle="Create particle effects" icon="sparkles" >}} {{< card link="#particle-system-configuration" title="Configuration" subtitle="System and spawner setup" icon="adjustments" >}} {{< card link="#particle-attractors" title="Attractors" subtitle="Forces and movement" icon="arrow-circle-right" >}} {{< /cards >}} --- ## Spawning Particles ### ParticleUtil Methods {{< tabs items="Basic,Advanced,WorldParticle" >}} {{< tab >}} ```java import com.hypixel.hytale.server.core.universe.world.ParticleUtil; // Basic spawn at position (auto-finds nearby players) ParticleUtil.spawnParticleEffect( "explosion_small", // Particle system ID position, // Vector3d position componentAccessor ); // Spawn for specific players ParticleUtil.spawnParticleEffect( "magic_trail", position, playerRefs, // List> componentAccessor ); ``` **Broadcast Distance:** `DEFAULT_PARTICLE_DISTANCE = 75` blocks {{< /tab >}} {{< tab >}} ```java // With source entity reference ParticleUtil.spawnParticleEffect( "attack_swing", position, sourceEntityRef, // Entity that spawned this playerRefs, componentAccessor ); // With rotation control ParticleUtil.spawnParticleEffect( "directional_beam", position, yaw, pitch, roll, // Rotation angles sourceEntityRef, playerRefs, componentAccessor ); // With scale and color ParticleUtil.spawnParticleEffect( "colored_burst", position, yaw, pitch, roll, 2.0f, // Scale multiplier new Color(255, 100, 50, 255), // RGBA color playerRefs, componentAccessor ); ``` {{< /tab >}} {{< tab >}} ```java import com.hypixel.hytale.server.core.asset.type.particle.config.WorldParticle; // Full control with WorldParticle wrapper WorldParticle worldParticle = new WorldParticle( "my_particle_system", new Color(255, 100, 50, 255), // RGBA color override 1.5f, // Scale multiplier new Vector3f(0, 1, 0), // Position offset new Direction(0, 0, 0) // Rotation offset ); ParticleUtil.spawnParticleEffect( worldParticle, position, sourceRef, playerRefs, componentAccessor ); ``` {{< /tab >}} {{< /tabs >}} --- ## Particle System Configuration ### ParticleSystem Class The root configuration for a particle effect: ```java public class ParticleSystem { protected String id; // Unique identifier protected ParticleSpawnerGroup[] spawners; // Spawner configurations protected float lifeSpan; // System lifetime (seconds) protected float cullDistance; // Distance before culling protected float boundingRadius; // Collision bounding box protected boolean isImportant; // Network priority flag } ``` | Field | Type | Description | |-------|------|-------------| | `id` | String | Unique identifier for the particle system | | `spawners` | ParticleSpawnerGroup[] | Array of spawner configurations | | `lifeSpan` | float | How long the system lives (seconds) | | `cullDistance` | float | Distance at which particles are culled | | `boundingRadius` | float | Bounding sphere for culling calculations | | `isImportant` | boolean | If true, prioritized in network sync | ### ParticleSpawner Configuration Controls how individual particles are emitted: {{< tabs items="Properties,Emission,Movement,Rendering" >}} {{< tab >}} ```java public class ParticleSpawner { protected String id; protected EmitShape shape; protected RangeVector3f emitOffset; protected boolean useEmitDirection; protected Range totalParticles; protected float lifeSpan; protected int maxConcurrentParticles; protected Rangef particleLifeSpan; protected Rangef spawnRate; protected boolean spawnBurst; protected InitialVelocity initialVelocity; protected ParticleAttractor[] attractors; protected FXRenderMode renderMode; protected float lightInfluence; protected Particle particle; } ``` {{< /tab >}} {{< tab >}} **Emission Properties:** | Property | Type | Description | |----------|------|-------------| | `shape` | EmitShape | Sphere or Cube emission shape | | `emitOffset` | RangeVector3f | Random offset range from origin | | `useEmitDirection` | boolean | Use spawn direction for velocity | | `totalParticles` | Range | Min/max total particles to emit | | `spawnRate` | Rangef | Particles per second | | `spawnBurst` | boolean | Emit all at once vs over time | **Example emission setup:** ```java // Burst explosion - all at once spawner.setSpawnBurst(true); spawner.setTotalParticles(new Range(50, 100)); // Continuous stream spawner.setSpawnBurst(false); spawner.setSpawnRate(new Rangef(10, 20)); // 10-20 per second spawner.setLifeSpan(5.0f); // Emit for 5 seconds ``` {{< /tab >}} {{< tab >}} **Movement Properties:** | Property | Type | Description | |----------|------|-------------| | `initialVelocity` | InitialVelocity | Starting velocity configuration | | `attractors` | ParticleAttractor[] | Forces applied to particles | **Initial Velocity:** ```java public class InitialVelocity { protected float speed; // Base speed protected float speedVariance; // Random variance protected Vector3f direction; // Base direction protected float coneAngle; // Spread angle (degrees) } ``` {{< /tab >}} {{< tab >}} **Rendering Properties:** | Property | Type | Description | |----------|------|-------------| | `renderMode` | FXRenderMode | How particles are blended | | `lightInfluence` | float | How much lighting affects particles | | `particle` | Particle | Texture and animation config | {{< /tab >}} {{< /tabs >}} --- ## Emit Shapes Control the volume from which particles spawn: ```java public enum EmitShape { Sphere, // Emit from spherical volume Cube // Emit from cubic volume } ``` {{< tabs items="Sphere,Cube" >}} {{< tab >}} **Spherical Emission:** Particles spawn within a spherical volume, with direction pointing outward from center: ```java // Configure spherical emission spawner.setShape(EmitShape.Sphere); spawner.setEmitOffset(new RangeVector3f( new Rangef(-1, 1), // X range (radius) new Rangef(-1, 1), // Y range (radius) new Rangef(-1, 1) // Z range (radius) )); // Use emit direction for outward velocity spawner.setUseEmitDirection(true); ``` Best for: Explosions, bursts, radial effects {{< /tab >}} {{< tab >}} **Cubic Emission:** Particles spawn within an axis-aligned box volume: ```java // Configure cubic emission spawner.setShape(EmitShape.Cube); spawner.setEmitOffset(new RangeVector3f( new Rangef(-2, 2), // X range new Rangef(0, 3), // Y range (above ground) new Rangef(-2, 2) // Z range )); ``` Best for: Area effects, rain, ground effects {{< /tab >}} {{< /tabs >}} --- ## Render Modes Determine how particles blend with the scene: ```java public enum FXRenderMode { BlendLinear, // Standard transparency blending BlendAdd, // Additive blending (bright/glowing) Erosion, // Erosion/dissolve effect Distortion // Distortion/refraction effect } ``` {{< tabs items="BlendLinear,BlendAdd,Erosion,Distortion" >}} {{< tab >}} ### BlendLinear Standard alpha blending. Best for: - Smoke - Dust - Clouds - Solid-looking particles ```java spawner.setRenderMode(FXRenderMode.BlendLinear); ``` {{< /tab >}} {{< tab >}} ### BlendAdd Additive blending - particles add light to the scene. Best for: - Fire - Sparks - Magic effects - Glowing particles - Light beams ```java spawner.setRenderMode(FXRenderMode.BlendAdd); ``` {{< callout type="info" >}} Use `BlendAdd` for any glowing or luminous effects. Multiple overlapping particles will create brighter spots. {{< /callout >}} {{< /tab >}} {{< tab >}} ### Erosion Creates dissolving/eroding visual effect. Best for: - Disintegration - Dissolve transitions - Energy dissipation ```java spawner.setRenderMode(FXRenderMode.Erosion); ``` {{< /tab >}} {{< tab >}} ### Distortion Refracts the background, creating heat-shimmer effects. Best for: - Heat waves - Portals - Energy fields - Underwater effects ```java spawner.setRenderMode(FXRenderMode.Distortion); ``` {{< callout type="warning" >}} Distortion effects are more GPU-intensive. Use sparingly. {{< /callout >}} {{< /tab >}} {{< /tabs >}} --- ## Particle Attractors Apply forces to particles for dynamic movement: ```java public class ParticleAttractor { protected Vector3f position; // Local attractor position protected Vector3f radialAxis; // Radial force direction protected float radius; // Influence radius // Accelerations (continuous forces) protected float radialAcceleration; // Outward/inward acceleration protected float radialTangentAcceleration; // Tangential (orbit) acceleration protected Vector3f linearAcceleration; // Direct linear acceleration // Impulses (one-time forces) protected float radialImpulse; // Outward/inward impulse protected float radialTangentImpulse; // Tangential impulse protected Vector3f linearImpulse; // Direct linear impulse // Damping protected Vector3f dampingMultiplier; // Velocity reduction per frame } ``` ### Force Types {{< tabs items="Radial,Tangential,Linear,Damping" >}} {{< tab >}} **Radial Forces:** Push particles toward or away from the attractor position: ```java ParticleAttractor attractor = new ParticleAttractor(); attractor.setPosition(new Vector3f(0, 0, 0)); // Center of system // Outward explosion attractor.setRadialAcceleration(10.0f); // Positive = outward // Inward pull (black hole effect) attractor.setRadialAcceleration(-5.0f); // Negative = inward // Instant outward burst attractor.setRadialImpulse(20.0f); ``` {{< /tab >}} {{< tab >}} **Tangential Forces:** Create swirling/orbiting motion around the attractor: ```java ParticleAttractor attractor = new ParticleAttractor(); attractor.setPosition(new Vector3f(0, 0, 0)); attractor.setRadialAxis(new Vector3f(0, 1, 0)); // Orbit around Y axis // Clockwise swirl attractor.setRadialTangentAcceleration(5.0f); // Counter-clockwise attractor.setRadialTangentAcceleration(-5.0f); ``` {{< /tab >}} {{< tab >}} **Linear Forces:** Apply constant directional force (like gravity or wind): ```java ParticleAttractor attractor = new ParticleAttractor(); // Gravity (downward) attractor.setLinearAcceleration(new Vector3f(0, -9.8f, 0)); // Wind (horizontal) attractor.setLinearAcceleration(new Vector3f(2.0f, 0, 0)); // Upward buoyancy attractor.setLinearAcceleration(new Vector3f(0, 3.0f, 0)); ``` {{< /tab >}} {{< tab >}} **Damping:** Slow particles down over time: ```java ParticleAttractor attractor = new ParticleAttractor(); // Uniform damping (air resistance) attractor.setDampingMultiplier(new Vector3f(0.98f, 0.98f, 0.98f)); // Strong horizontal damping, weak vertical attractor.setDampingMultiplier(new Vector3f(0.9f, 0.99f, 0.9f)); // No damping (particles maintain velocity) attractor.setDampingMultiplier(new Vector3f(1.0f, 1.0f, 1.0f)); ``` {{< callout type="info" >}} Damping values < 1.0 slow particles. Values of 0.98-0.99 give realistic air resistance. {{< /callout >}} {{< /tab >}} {{< /tabs >}} --- ## Particle Visual Configuration Configure particle appearance and animation: ```java public class Particle { protected String texture; // Texture atlas path protected Size frameSize; // Frame dimensions protected ParticleUVOption uvOption; // None, Animated, Random protected SoftParticle softParticle; // Soft particle blending protected float softParticlesFadeFactor; // 0.1 to 2.0 // Animation protected ParticleAnimationFrame initialAnimationFrame; protected Map animation; } ``` ### Animation Options ```java public enum ParticleUVOption { None, // Single static frame Animated, // Play through frames in sequence Random // Random frame per particle } ``` ### Animation Frames ```java public class ParticleAnimationFrame { protected int frame; // Frame number in atlas protected Rangef scale; // Size range protected Rangef alpha; // Transparency range (0-1) protected Color color; // Color tint protected Rangef rotation; // Rotation range (degrees) } ``` --- ## Accessing Particle Assets ```java // Get particle system by ID ParticleSystem system = ParticleSystem.getAssetMap().getAsset("explosion_large"); // Get spawner configuration ParticleSpawner spawner = ParticleSpawner.getAssetMap().getAsset("fire_spawner"); // Use in command arguments // ArgTypes.PARTICLE_SYSTEM for command parameters ``` --- ## Common Patterns ### Event-Based Particles {{< callout type="warning" >}} **Note:** The examples below are simplified. Entity doesn't have a direct `getPosition()` method. In real code, obtain position via `TransformComponent` from the entity's store. Example: ```java TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType()); Vector3d position = transform.getPosition(); ``` {{< /callout >}} {{< tabs items="Block Break,Combat,Death" >}} {{< tab >}} ```java @Subscribe public void onBlockBreak(BreakBlockEvent event) { // Use getTargetBlock() - not getPosition() Vector3i blockPos = event.getTargetBlock(); Vector3d pos = new Vector3d( blockPos.x + 0.5, blockPos.y + 0.5, blockPos.z + 0.5 ); // Spawn break particles at block center ParticleUtil.spawnParticleEffect( "block_break_particles", pos, componentAccessor ); } ``` {{< /tab >}} {{< tab >}} ```java @Subscribe public void onEntityDamage(EntityDamageEvent event) { Entity target = event.getTarget(); Vector3d hitPos = target.getPosition().add(0, 1, 0); // Blood/damage effect ParticleUtil.spawnParticleEffect( "damage_hit", hitPos, componentAccessor ); // Critical hit special effect if (event.isCritical()) { ParticleUtil.spawnParticleEffect( "critical_hit_sparks", hitPos, 0, 0, 0, // rotation 1.5f, // larger scale new Color(255, 215, 0, 255), // gold color getNearbyPlayers(hitPos, 50), componentAccessor ); } } ``` {{< /tab >}} {{< tab >}} ```java @Subscribe public void onEntityDeath(EntityDeathEvent event) { Entity entity = event.getEntity(); Vector3d deathPos = entity.getPosition(); // Death particles ParticleUtil.spawnParticleEffect( "entity_death_poof", deathPos, componentAccessor ); // Soul rising effect for players if (entity instanceof Player) { ParticleUtil.spawnParticleEffect( "soul_ascend", deathPos.add(0, 0.5, 0), componentAccessor ); } } ``` {{< /tab >}} {{< /tabs >}} ### Continuous Effects {{< tabs items="Player Trail,Aura,Area Effect" >}} {{< tab >}} ```java // Spawn particle trail at player position public void spawnTrailEffect(Player player, Store store, ComponentAccessor componentAccessor) { if (hasTrailEffect(player)) { // Get position from TransformComponent Ref entityRef = player.getReference(); TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType()); if (transform != null) { Vector3d position = transform.getPosition(); ParticleUtil.spawnParticleEffect( "magic_trail", position, componentAccessor ); } } } ``` {{< callout type="warning" >}} **Note:** Player doesn't have a `getPosition()` method. Get position via `TransformComponent` from the entity store. {{< /callout >}} {{< /tab >}} {{< tab >}} ```java // Persistent aura around player public class AuraManager { private final Map activeAuras = new HashMap<>(); private Store store; private ComponentAccessor componentAccessor; public void enableAura(Player player) { // Use player.getPlayerRef().getUuid() - not player.getUuid() activeAuras.put(player.getPlayerRef().getUuid(), true); } @Subscribe public void onTick(ServerTickEvent event) { for (Map.Entry entry : activeAuras.entrySet()) { if (entry.getValue()) { Player player = getPlayer(entry.getKey()); if (player != null) { // Get position from TransformComponent Ref entityRef = player.getReference(); TransformComponent transform = store.getComponent( entityRef, TransformComponent.getComponentType()); if (transform != null) { Vector3d pos = transform.getPosition(); ParticleUtil.spawnParticleEffect( "magic_aura", new Vector3d(pos.getX(), pos.getY() + 1, pos.getZ()), componentAccessor ); } } } } } } ``` {{< /tab >}} {{< tab >}} ```java // Area healing effect public void createHealingZone(Vector3d center, double radius, int durationTicks) { // Spawn ring particles for (int i = 0; i < 16; i++) { double angle = (2 * Math.PI * i) / 16; Vector3d pos = center.add( Math.cos(angle) * radius, 0.1, Math.sin(angle) * radius ); ParticleUtil.spawnParticleEffect( "healing_sparkle", pos, componentAccessor ); } // Central pillar effect ParticleUtil.spawnParticleEffect( "healing_pillar", center, componentAccessor ); } ``` {{< /tab >}} {{< /tabs >}} --- ## Performance Guidelines {{< callout type="warning" >}} **Performance Notes:** - Default broadcast distance: **75 blocks** (`DEFAULT_PARTICLE_DISTANCE`) - Particles use `SpawnParticleSystem` packet (ID: 152) - Set `isImportant = true` for critical visual feedback - Limit particle count for client performance - Use `maxConcurrentParticles` to cap active particles {{< /callout >}} ### Optimization Tips | Tip | Description | |-----|-------------| | Limit total particles | Keep under 100-200 for burst effects | | Use appropriate `cullDistance` | Don't render particles too far away | | Batch spawns | Spawn multiple particles in same tick | | Use `spawnBurst` wisely | Bursts are cheaper than continuous | | Consider player count | More players = more network traffic | ```java // Good: Efficient particle spawning public void spawnEfficiently(Vector3d position) { // Only spawn for nearby players List nearbyPlayers = getNearbyPlayers(position, 50); if (!nearbyPlayers.isEmpty()) { ParticleUtil.spawnParticleEffect( "my_effect", position, nearbyPlayers, // Limited audience componentAccessor ); } } ``` --- ## Related Topics - [Entity Effects]({{< relref "entity-effects" >}}) - Status effects with visual components - [Networking]({{< relref "/advanced/networking" >}}) - How particles are broadcast - [Events]({{< relref "/core-concepts/events" >}}) - Triggering particles from events