This commit is contained in:
2026-01-20 20:33:59 +01:00
commit b16a40e431
583 changed files with 87339 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
---
title: Effects
type: docs
weight: 10
---
Visual effects in Hytale include particles, dynamic lights, and entity status effects.
{{< cards >}}
{{< card link="particles" title="Particles" subtitle="Spawn and configure particle systems" >}}
{{< card link="dynamic-lights" title="Dynamic Lights" subtitle="Glow effects on entities" >}}
{{< card link="entity-effects" title="Entity Effects" subtitle="Status effects with visuals" >}}
{{< /cards >}}
## Overview
Hytale provides three complementary visual effect systems:
| System | Purpose | Use Cases |
|--------|---------|-----------|
| **Particles** | Visual atmosphere and feedback | Explosions, trails, ambient effects |
| **Dynamic Lights** | Glow effects on entities | Glowing items, magic auras |
| **Entity Effects** | Status effects with visuals | Buffs, debuffs, status indicators |
## Quick Start
### Spawning Particles
```java
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;
// Spawn particles at a position
ParticleUtil.spawnParticleEffect(
"explosion_small", // Particle system ID
position, // Vector3d position
componentAccessor
);
```
### Adding Dynamic Light
```java
import com.hypixel.hytale.protocol.ColorLight;
import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight;
// Create a colored light (radius, R, G, B)
ColorLight light = new ColorLight(
(byte) 15, // Radius
(byte) 255, // Red
(byte) 100, // Green
(byte) 50 // Blue
);
// Add to entity
DynamicLight dynamicLight = new DynamicLight(light);
componentAccessor.putComponent(entityRef, DynamicLight.getComponentType(), dynamicLight);
```
### Applying Entity Effects
```java
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
// Get effect controller
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
// Apply effect
EntityEffect effect = EntityEffect.getAssetMap().getAsset("fire_resistance");
controller.addEffect(entityRef, effect, componentAccessor);
```
## Key Classes
| Class | Package | Purpose |
|-------|---------|---------|
| `ParticleUtil` | server.core.universe.world | Spawn particles |
| `ParticleSystem` | asset.type.particle.config | System configuration |
| `DynamicLight` | modules.entity.component | Entity glow effects |
| `ColorLight` | protocol | Light color/radius data |
| `EntityEffect` | asset.type.entityeffect.config | Status effect definition |
## Network Considerations
{{< callout type="info" >}}
**Network Details:**
- Particles are sent within 75 blocks by default (`DEFAULT_PARTICLE_DISTANCE`)
- DynamicLight changes sync automatically when marked as outdated
- Entity effects sync via `EffectControllerComponent`
{{< /callout >}}

View File

@@ -0,0 +1,92 @@
---
title: Effets
type: docs
weight: 10
---
Les effets visuels dans Hytale incluent les particules, les lumières dynamiques et les effets de statut d'entité.
{{< cards >}}
{{< card link="particles" title="Particules" subtitle="Générer et configurer les systèmes de particules" >}}
{{< card link="dynamic-lights" title="Lumières Dynamiques" subtitle="Effets de lueur sur les entités" >}}
{{< card link="entity-effects" title="Effets d'Entité" subtitle="Effets de statut avec visuels" >}}
{{< /cards >}}
## Aperçu
Hytale fournit trois systèmes d'effets visuels complémentaires :
| Système | Objectif | Cas d'Usage |
|---------|----------|-------------|
| **Particules** | Atmosphère visuelle et feedback | Explosions, traînées, effets ambiants |
| **Lumières Dynamiques** | Effets de lueur sur les entités | Objets lumineux, auras magiques |
| **Effets d'Entité** | Effets de statut avec visuels | Buffs, debuffs, indicateurs de statut |
## Démarrage Rapide
### Générer des Particules
```java
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;
// Générer des particules à une position
ParticleUtil.spawnParticleEffect(
"explosion_small", // ID du système de particules
position, // Position Vector3d
componentAccessor
);
```
### Ajouter une Lumière Dynamique
```java
import com.hypixel.hytale.protocol.ColorLight;
import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight;
// Créer une lumière colorée (rayon, R, V, B)
ColorLight light = new ColorLight(
(byte) 15, // Rayon
(byte) 255, // Rouge
(byte) 100, // Vert
(byte) 50 // Bleu
);
// Ajouter à l'entité
DynamicLight dynamicLight = new DynamicLight(light);
componentAccessor.putComponent(entityRef, DynamicLight.getComponentType(), dynamicLight);
```
### Appliquer des Effets d'Entité
```java
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
// Obtenir le contrôleur d'effets
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
// Appliquer l'effet
EntityEffect effect = EntityEffect.getAssetMap().getAsset("fire_resistance");
controller.addEffect(entityRef, effect, componentAccessor);
```
## Classes Principales
| Classe | Package | Objectif |
|--------|---------|----------|
| `ParticleUtil` | server.core.universe.world | Générer des particules |
| `ParticleSystem` | asset.type.particle.config | Configuration du système |
| `DynamicLight` | modules.entity.component | Effets de lueur d'entité |
| `ColorLight` | protocol | Données couleur/rayon de lumière |
| `EntityEffect` | asset.type.entityeffect.config | Définition d'effet de statut |
## Considérations Réseau
{{< callout type="info" >}}
**Détails Réseau :**
- Les particules sont envoyées dans un rayon de 75 blocs par défaut (`DEFAULT_PARTICLE_DISTANCE`)
- Les changements de DynamicLight se synchronisent automatiquement quand marqués obsolètes
- Les effets d'entité se synchronisent via `EffectControllerComponent`
{{< /callout >}}

View File

@@ -0,0 +1,268 @@
---
title: Dynamic Lights
type: docs
weight: 2
---
Dynamic lights add glow effects to entities using the `DynamicLight` component.
## ColorLight Structure
The `ColorLight` class defines light properties:
```java
import com.hypixel.hytale.protocol.ColorLight;
public class ColorLight {
public byte radius; // 0-255 (light radius/intensity)
public byte red; // 0-255 (red channel)
public byte green; // 0-255 (green channel)
public byte blue; // 0-255 (blue channel)
public ColorLight(byte radius, byte red, byte green, byte blue) {
this.radius = radius;
this.red = red;
this.green = green;
this.blue = blue;
}
}
```
## DynamicLight Component
Add temporary glow effects to entities:
```java
import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight;
import com.hypixel.hytale.protocol.ColorLight;
// Create colored light (radius, R, G, B)
ColorLight light = new ColorLight(
(byte) 15, // Radius (0-255)
(byte) 255, // Red (0-255)
(byte) 100, // Green (0-255)
(byte) 50 // Blue (0-255)
);
// Add DynamicLight component
DynamicLight dynamicLight = new DynamicLight(light);
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
dynamicLight
);
```
## PersistentDynamicLight Component
Use this component when light should be saved with entity data:
```java
import com.hypixel.hytale.server.core.modules.entity.component.PersistentDynamicLight;
// Create persistent light
PersistentDynamicLight persistentLight = new PersistentDynamicLight(light);
componentAccessor.putComponent(
entityRef,
PersistentDynamicLight.getComponentType(),
persistentLight
);
```
## Updating Light Properties
```java
// Get existing light component
DynamicLight dynamicLight = store.getComponent(
entityRef,
DynamicLight.getComponentType()
);
if (dynamicLight != null) {
// Create new light properties
ColorLight newLight = new ColorLight(
(byte) 20, // New radius
(byte) 0, // No red
(byte) 255, // Full green
(byte) 100 // Some blue
);
// Update and mark for network sync
dynamicLight.setColorLight(newLight);
// isNetworkOutdated is set automatically
}
```
## Removing Light
```java
// Remove the component to disable light
componentAccessor.removeComponent(
entityRef,
DynamicLight.getComponentType()
);
```
## Common Light Configurations
### Fire/Warm Glow
```java
ColorLight fireLight = new ColorLight(
(byte) 12,
(byte) 255, // High red
(byte) 150, // Medium orange
(byte) 50 // Low blue
);
```
### Ice/Cold Glow
```java
ColorLight iceLight = new ColorLight(
(byte) 10,
(byte) 100, // Low red
(byte) 200, // Medium green
(byte) 255 // High blue
);
```
### Magic/Purple Glow
```java
ColorLight magicLight = new ColorLight(
(byte) 15,
(byte) 200, // High red
(byte) 50, // Low green
(byte) 255 // High blue
);
```
### Healing/Green Glow
```java
ColorLight healLight = new ColorLight(
(byte) 10,
(byte) 50, // Low red
(byte) 255, // High green
(byte) 100 // Medium blue
);
```
## Practical Examples
### Adding a Glow Effect to a Player
```java
// Add temporary glow effect to a player
public void addGlowEffect(Player player, ComponentAccessor<EntityStore> componentAccessor,
ScheduledExecutorService scheduler) {
World world = player.getWorld();
// Add temporary glow
ColorLight glow = new ColorLight(
(byte) 8,
(byte) 255,
(byte) 215,
(byte) 0 // Gold color
);
DynamicLight light = new DynamicLight(glow);
Ref<EntityStore> entityRef = player.getReference();
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
light
);
// Remove after 3 seconds using standard Java scheduling
scheduler.schedule(() -> {
world.execute(() -> {
if (entityRef.isValid()) {
componentAccessor.removeComponent(
entityRef,
DynamicLight.getComponentType()
);
}
});
}, 3, TimeUnit.SECONDS);
}
```
{{< callout type="warning" >}}
**Note:** Use `player.getReference()` to get the entity reference, not `player.getEntityRef()`. Use standard Java `ScheduledExecutorService` for delayed tasks.
{{< /callout >}}
### Pulsing Light Effect
Implement pulsing effects by updating the light in a tick handler:
```java
// Track pulsing entities
private final Map<Ref<EntityStore>, Integer> pulsingEntities = new ConcurrentHashMap<>();
// Call this method each tick
public void onTick(float deltaTime) {
for (Map.Entry<Ref<EntityStore>, Integer> entry : pulsingEntities.entrySet()) {
Ref<EntityStore> entityRef = entry.getKey();
int tick = entry.getValue() + 1;
entry.setValue(tick);
// Calculate pulsing intensity
double pulse = Math.sin(tick * 0.1) * 0.5 + 0.5;
byte radius = (byte) (5 + pulse * 10);
ColorLight light = new ColorLight(
radius,
(byte) 255,
(byte) (int)(100 + pulse * 100),
(byte) 50
);
DynamicLight dynamicLight = store.getComponent(
entityRef,
DynamicLight.getComponentType()
);
if (dynamicLight != null) {
dynamicLight.setColorLight(light);
}
}
}
public void startPulsingLight(Ref<EntityStore> entityRef) {
pulsingEntities.put(entityRef, 0);
}
public void stopPulsingLight(Ref<EntityStore> entityRef) {
pulsingEntities.remove(entityRef);
}
```
## Network Synchronization
{{< callout type="info" >}}
**Sync Details:**
- `DynamicLight` changes are automatically marked for sync when using `setColorLight()`
- The `isNetworkOutdated` flag triggers client updates
- Use `consumeNetworkOutdated()` before manual sync operations
{{< /callout >}}
## DynamicLight vs PersistentDynamicLight
| Feature | DynamicLight | PersistentDynamicLight |
|---------|--------------|------------------------|
| Saved to disk | No | Yes |
| Survives restart | No | Yes |
| Use case | Temporary effects | Permanent glows |
| Performance | Lighter | Slightly heavier |
## Best Practices
{{< callout type="warning" >}}
**Light Guidelines:**
- Keep radius values reasonable (5-20 for most effects)
- Don't add lights to too many entities simultaneously
- Remove temporary lights when no longer needed
- Use `PersistentDynamicLight` only when persistence is required
{{< /callout >}}

View File

@@ -0,0 +1,268 @@
---
title: Lumières Dynamiques
type: docs
weight: 2
---
Les lumières dynamiques ajoutent des effets de lueur aux entités en utilisant le composant `DynamicLight`.
## Structure ColorLight
La classe `ColorLight` définit les propriétés de la lumière :
```java
import com.hypixel.hytale.protocol.ColorLight;
public class ColorLight {
public byte radius; // 0-255 (rayon/intensité de la lumière)
public byte red; // 0-255 (canal rouge)
public byte green; // 0-255 (canal vert)
public byte blue; // 0-255 (canal bleu)
public ColorLight(byte radius, byte red, byte green, byte blue) {
this.radius = radius;
this.red = red;
this.green = green;
this.blue = blue;
}
}
```
## Composant DynamicLight
Ajoutez des effets de lueur temporaires aux entités :
```java
import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight;
import com.hypixel.hytale.protocol.ColorLight;
// Créer une lumière colorée (rayon, R, V, B)
ColorLight light = new ColorLight(
(byte) 15, // Rayon (0-255)
(byte) 255, // Rouge (0-255)
(byte) 100, // Vert (0-255)
(byte) 50 // Bleu (0-255)
);
// Ajouter le composant DynamicLight
DynamicLight dynamicLight = new DynamicLight(light);
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
dynamicLight
);
```
## Composant PersistentDynamicLight
Utilisez ce composant quand la lumière doit être sauvegardée avec les données de l'entité :
```java
import com.hypixel.hytale.server.core.modules.entity.component.PersistentDynamicLight;
// Créer une lumière persistante
PersistentDynamicLight persistentLight = new PersistentDynamicLight(light);
componentAccessor.putComponent(
entityRef,
PersistentDynamicLight.getComponentType(),
persistentLight
);
```
## Mettre à Jour les Propriétés de Lumière
```java
// Obtenir le composant de lumière existant
DynamicLight dynamicLight = store.getComponent(
entityRef,
DynamicLight.getComponentType()
);
if (dynamicLight != null) {
// Créer de nouvelles propriétés de lumière
ColorLight newLight = new ColorLight(
(byte) 20, // Nouveau rayon
(byte) 0, // Pas de rouge
(byte) 255, // Vert maximum
(byte) 100 // Un peu de bleu
);
// Mettre à jour et marquer pour sync réseau
dynamicLight.setColorLight(newLight);
// isNetworkOutdated est défini automatiquement
}
```
## Supprimer la Lumière
```java
// Supprimer le composant pour désactiver la lumière
componentAccessor.removeComponent(
entityRef,
DynamicLight.getComponentType()
);
```
## Configurations de Lumière Courantes
### Lueur Feu/Chaleur
```java
ColorLight fireLight = new ColorLight(
(byte) 12,
(byte) 255, // Rouge élevé
(byte) 150, // Orange moyen
(byte) 50 // Bleu faible
);
```
### Lueur Glace/Froid
```java
ColorLight iceLight = new ColorLight(
(byte) 10,
(byte) 100, // Rouge faible
(byte) 200, // Vert moyen
(byte) 255 // Bleu élevé
);
```
### Lueur Magie/Violet
```java
ColorLight magicLight = new ColorLight(
(byte) 15,
(byte) 200, // Rouge élevé
(byte) 50, // Vert faible
(byte) 255 // Bleu élevé
);
```
### Lueur Soin/Vert
```java
ColorLight healLight = new ColorLight(
(byte) 10,
(byte) 50, // Rouge faible
(byte) 255, // Vert élevé
(byte) 100 // Bleu moyen
);
```
## Exemples Pratiques
### Ajouter un Effet de Lueur à un Joueur
```java
// Ajouter un effet de lueur temporaire à un joueur
public void addGlowEffect(Player player, ComponentAccessor<EntityStore> componentAccessor,
ScheduledExecutorService scheduler) {
World world = player.getWorld();
// Ajouter une lueur temporaire
ColorLight glow = new ColorLight(
(byte) 8,
(byte) 255,
(byte) 215,
(byte) 0 // Couleur dorée
);
DynamicLight light = new DynamicLight(glow);
Ref<EntityStore> entityRef = player.getReference();
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
light
);
// Supprimer après 3 secondes avec le scheduling Java standard
scheduler.schedule(() -> {
world.execute(() -> {
if (entityRef.isValid()) {
componentAccessor.removeComponent(
entityRef,
DynamicLight.getComponentType()
);
}
});
}, 3, TimeUnit.SECONDS);
}
```
{{< callout type="warning" >}}
**Note :** Utilisez `player.getReference()` pour obtenir la référence d'entité, pas `player.getEntityRef()`. Utilisez le `ScheduledExecutorService` Java standard pour les tâches différées.
{{< /callout >}}
### Effet de Lumière Pulsante
Implémentez les effets pulsants en mettant à jour la lumière dans un gestionnaire de tick :
```java
// Suivre les entités pulsantes
private final Map<Ref<EntityStore>, Integer> pulsingEntities = new ConcurrentHashMap<>();
// Appeler cette méthode à chaque tick
public void onTick(float deltaTime) {
for (Map.Entry<Ref<EntityStore>, Integer> entry : pulsingEntities.entrySet()) {
Ref<EntityStore> entityRef = entry.getKey();
int tick = entry.getValue() + 1;
entry.setValue(tick);
// Calculer l'intensité pulsante
double pulse = Math.sin(tick * 0.1) * 0.5 + 0.5;
byte radius = (byte) (5 + pulse * 10);
ColorLight light = new ColorLight(
radius,
(byte) 255,
(byte) (int)(100 + pulse * 100),
(byte) 50
);
DynamicLight dynamicLight = store.getComponent(
entityRef,
DynamicLight.getComponentType()
);
if (dynamicLight != null) {
dynamicLight.setColorLight(light);
}
}
}
public void startPulsingLight(Ref<EntityStore> entityRef) {
pulsingEntities.put(entityRef, 0);
}
public void stopPulsingLight(Ref<EntityStore> entityRef) {
pulsingEntities.remove(entityRef);
}
```
## Synchronisation Réseau
{{< callout type="info" >}}
**Détails de Sync :**
- Les changements de `DynamicLight` sont automatiquement marqués pour sync lors de l'utilisation de `setColorLight()`
- Le flag `isNetworkOutdated` déclenche les mises à jour client
- Utilisez `consumeNetworkOutdated()` avant les opérations de sync manuelles
{{< /callout >}}
## DynamicLight vs PersistentDynamicLight
| Fonctionnalité | DynamicLight | PersistentDynamicLight |
|----------------|--------------|------------------------|
| Sauvegardé sur disque | Non | Oui |
| Survit au redémarrage | Non | Oui |
| Cas d'usage | Effets temporaires | Lueurs permanentes |
| Performance | Plus léger | Légèrement plus lourd |
## Bonnes Pratiques
{{< callout type="warning" >}}
**Directives pour les Lumières :**
- Gardez des valeurs de rayon raisonnables (5-20 pour la plupart des effets)
- N'ajoutez pas de lumières à trop d'entités simultanément
- Supprimez les lumières temporaires quand plus nécessaires
- Utilisez `PersistentDynamicLight` seulement quand la persistance est requise
{{< /callout >}}

View File

@@ -0,0 +1,369 @@
---
title: Entity Effects
type: docs
weight: 3
---
Entity effects are status effects that can include visual components, stat modifiers, and duration-based behaviors.
## EntityEffect Configuration
```java
public class EntityEffect {
protected String id;
protected String name; // Localization key
protected ApplicationEffects applicationEffects; // Visual/audio effects
protected String modelChange; // Change entity model
protected float duration; // Default duration (seconds)
protected boolean infinite; // Never expires?
protected boolean debuff; // Is a negative effect?
protected String statusEffectIcon; // UI icon
// Damage and stats
protected Int2FloatMap entityStats; // Stat modifiers
protected ValueType valueType; // Absolute or Percent
// Behavior
protected OverlapBehavior overlapBehavior; // EXTEND, OVERWRITE, IGNORE
protected RemovalBehavior removalBehavior; // COMPLETE, DURATION, INFINITE
protected boolean invulnerable; // Grant invulnerability
}
```
## Accessing Effect Assets
```java
// Get effect by ID
EntityEffect effect = EntityEffect.getAssetMap().getAsset("fire_resistance");
// Get effect index for operations
int effectIndex = EntityEffect.getAssetMap().getIndex(effect.getId());
```
## EffectControllerComponent
The `EffectControllerComponent` manages active effects on an entity:
```java
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
// Get effect controller from entity
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
```
## Adding Effects
### Basic Addition
```java
EntityEffect effect = EntityEffect.getAssetMap().getAsset("speed_boost");
// Add with default parameters
controller.addEffect(
entityRef,
effect,
componentAccessor
);
```
### With Custom Duration
```java
// Add with custom duration and overlap behavior
controller.addEffect(
entityRef,
effect,
100.0f, // Duration in seconds
OverlapBehavior.EXTEND, // How to handle overlap
componentAccessor
);
```
### Infinite Effects
```java
// Add an effect that never expires
controller.addInfiniteEffect(
entityRef,
EntityEffect.getAssetMap().getIndex(effect.getId()),
effect,
componentAccessor
);
```
## Removing Effects
```java
// Remove specific effect
controller.removeEffect(
entityRef,
EntityEffect.getAssetMap().getIndex(effect.getId()),
componentAccessor
);
```
## Overlap Behaviors
```java
public enum OverlapBehavior {
EXTEND, // Add duration to existing effect
OVERWRITE, // Replace existing effect
IGNORE // Don't apply if already active
}
```
### Usage Examples
```java
// Extend duration if already applied
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.EXTEND,
componentAccessor
);
// Replace with fresh duration
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.OVERWRITE,
componentAccessor
);
// Only apply if not already active
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.IGNORE,
componentAccessor
);
```
## Removal Behaviors
```java
public enum RemovalBehavior {
COMPLETE, // Remove when complete
DURATION, // Remove after duration
INFINITE // Never remove automatically
}
```
## Value Types for Stats
```java
public enum ValueType {
Absolute, // Add/subtract flat value
Percent // Multiply by percentage
}
```
## Practical Examples
### Buff on Command
```java
public class BuffCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final RequiredArg<String> effectArg;
private final RequiredArg<Integer> durationArg;
// Would be set during plugin initialization
private Store<EntityStore> store;
private ComponentAccessor<EntityStore> componentAccessor;
public BuffCommand() {
super("buff", "effects.command.buff.description");
this.playerArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
this.effectArg = withRequiredArg("effect", "Effect ID", ArgTypes.STRING);
this.durationArg = withRequiredArg("duration", "Duration in seconds", ArgTypes.INTEGER);
}
@Override
protected CompletableFuture<Void> execute(CommandContext ctx) {
PlayerRef target = playerArg.get(ctx);
String effectId = effectArg.get(ctx);
int duration = durationArg.get(ctx);
Ref<EntityStore> entityRef = target.getReference();
if (entityRef == null || !entityRef.isValid()) {
ctx.sender().sendMessage(Message.raw("Player not found"));
return null;
}
EntityEffect effect = EntityEffect.getAssetMap().getAsset(effectId);
if (effect == null) {
ctx.sender().sendMessage(Message.raw("Unknown effect: " + effectId));
return null;
}
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null) {
controller.addEffect(
entityRef,
effect,
(float) duration,
OverlapBehavior.EXTEND,
componentAccessor
);
}
ctx.sender().sendMessage(Message.raw("Applied " + effectId + " to " + target.getUsername()));
return null;
}
}
```
{{< callout type="warning" >}}
**API Notes:**
- Use `withRequiredArg()` to define arguments, not `addArgument()`
- Use `arg.get(ctx)` to retrieve values, not `ctx.getArg()`
- Use `ctx.sender()` not `ctx.getSender()`
- Use `target.getUsername()` not `target.getName()`
- Use `target.getReference()` to get entity reference (PlayerRef doesn't have `getPlayer()`)
{{< /callout >}}
### Damage Over Time Effect
```java
public void applyPoison(Player player, float duration, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
EntityEffect poison = EntityEffect.getAssetMap().getAsset("poison");
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && poison != null) {
controller.addEffect(
entityRef,
poison,
duration,
OverlapBehavior.EXTEND,
componentAccessor
);
}
player.sendMessage(Message.raw("You have been poisoned!"));
}
```
### Temporary Invulnerability
```java
public void grantInvulnerability(Player player, float seconds, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
EntityEffect invuln = EntityEffect.getAssetMap().getAsset("invulnerability");
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && invuln != null) {
controller.addEffect(
entityRef,
invuln,
seconds,
OverlapBehavior.OVERWRITE, // Fresh duration
componentAccessor
);
}
}
```
### Clear All Effects
```java
public void clearAllEffects(Player player, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null) {
// Use the built-in clearEffects method
controller.clearEffects(
entityRef,
componentAccessor
);
}
player.sendMessage(Message.raw("All effects cleared!"));
}
```
## Combining with Other Effects
Entity effects work well with particles and dynamic lights:
```java
public void applyMagicBuff(Player player, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
Ref<EntityStore> entityRef = player.getReference();
// Apply status effect
EntityEffect buff = EntityEffect.getAssetMap().getAsset("magic_power");
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && buff != null) {
controller.addEffect(entityRef, buff, componentAccessor);
}
// Add visual glow
ColorLight glow = new ColorLight((byte) 10, (byte) 200, (byte) 50, (byte) 255);
DynamicLight light = new DynamicLight(glow);
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
light
);
// Get position from TransformComponent for particle spawn
TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType());
if (transform != null) {
ParticleUtil.spawnParticleEffect(
"magic_aura",
transform.getPosition(),
componentAccessor
);
}
}
```
{{< callout type="info" >}}
**Note:** Use `player.getReference()` to get the entity reference. Position must be obtained from `TransformComponent`, not directly from Player.
{{< /callout >}}
## Best Practices
{{< callout type="info" >}}
**Effect Guidelines:**
- Use `EXTEND` for stackable buffs to reward repeated application
- Use `OVERWRITE` for effects that should reset duration
- Use `IGNORE` to prevent effect stacking when undesired
- Always check if effect exists before applying
- Consider performance with many simultaneous effects
{{< /callout >}}

View File

@@ -0,0 +1,369 @@
---
title: Effets d'Entité
type: docs
weight: 3
---
Les effets d'entité sont des effets de statut qui peuvent inclure des composants visuels, des modificateurs de stats et des comportements basés sur la durée.
## Configuration EntityEffect
```java
public class EntityEffect {
protected String id;
protected String name; // Clé de localisation
protected ApplicationEffects applicationEffects; // Effets visuels/audio
protected String modelChange; // Changer le modèle de l'entité
protected float duration; // Durée par défaut (secondes)
protected boolean infinite; // N'expire jamais ?
protected boolean debuff; // Est un effet négatif ?
protected String statusEffectIcon; // Icône UI
// Dégâts et stats
protected Int2FloatMap entityStats; // Modificateurs de stats
protected ValueType valueType; // Absolute ou Percent
// Comportement
protected OverlapBehavior overlapBehavior; // EXTEND, OVERWRITE, IGNORE
protected RemovalBehavior removalBehavior; // COMPLETE, DURATION, INFINITE
protected boolean invulnerable; // Accorder l'invulnérabilité
}
```
## Accéder aux Assets d'Effets
```java
// Obtenir un effet par ID
EntityEffect effect = EntityEffect.getAssetMap().getAsset("fire_resistance");
// Obtenir l'index de l'effet pour les opérations
int effectIndex = EntityEffect.getAssetMap().getIndex(effect.getId());
```
## EffectControllerComponent
Le `EffectControllerComponent` gère les effets actifs sur une entité :
```java
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
// Obtenir le contrôleur d'effets de l'entité
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
```
## Ajouter des Effets
### Ajout Basique
```java
EntityEffect effect = EntityEffect.getAssetMap().getAsset("speed_boost");
// Ajouter avec les paramètres par défaut
controller.addEffect(
entityRef,
effect,
componentAccessor
);
```
### Avec Durée Personnalisée
```java
// Ajouter avec durée personnalisée et comportement de superposition
controller.addEffect(
entityRef,
effect,
100.0f, // Durée en secondes
OverlapBehavior.EXTEND, // Comment gérer la superposition
componentAccessor
);
```
### Effets Infinis
```java
// Ajouter un effet qui n'expire jamais
controller.addInfiniteEffect(
entityRef,
EntityEffect.getAssetMap().getIndex(effect.getId()),
effect,
componentAccessor
);
```
## Supprimer des Effets
```java
// Supprimer un effet spécifique
controller.removeEffect(
entityRef,
EntityEffect.getAssetMap().getIndex(effect.getId()),
componentAccessor
);
```
## Comportements de Superposition
```java
public enum OverlapBehavior {
EXTEND, // Ajouter la durée à l'effet existant
OVERWRITE, // Remplacer l'effet existant
IGNORE // Ne pas appliquer si déjà actif
}
```
### Exemples d'Utilisation
```java
// Étendre la durée si déjà appliqué
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.EXTEND,
componentAccessor
);
// Remplacer avec une durée fraîche
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.OVERWRITE,
componentAccessor
);
// Appliquer seulement si pas déjà actif
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.IGNORE,
componentAccessor
);
```
## Comportements de Suppression
```java
public enum RemovalBehavior {
COMPLETE, // Supprimer quand terminé
DURATION, // Supprimer après la durée
INFINITE // Ne jamais supprimer automatiquement
}
```
## Types de Valeurs pour les Stats
```java
public enum ValueType {
Absolute, // Ajouter/soustraire une valeur fixe
Percent // Multiplier par un pourcentage
}
```
## Exemples Pratiques
### Buff par Commande
```java
public class BuffCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final RequiredArg<String> effectArg;
private final RequiredArg<Integer> durationArg;
// Serait défini lors de l'initialisation du plugin
private Store<EntityStore> store;
private ComponentAccessor<EntityStore> componentAccessor;
public BuffCommand() {
super("buff", "effects.command.buff.description");
this.playerArg = withRequiredArg("player", "Joueur cible", ArgTypes.PLAYER_REF);
this.effectArg = withRequiredArg("effect", "ID de l'effet", ArgTypes.STRING);
this.durationArg = withRequiredArg("duration", "Durée en secondes", ArgTypes.INTEGER);
}
@Override
protected CompletableFuture<Void> execute(CommandContext ctx) {
PlayerRef target = playerArg.get(ctx);
String effectId = effectArg.get(ctx);
int duration = durationArg.get(ctx);
Ref<EntityStore> entityRef = target.getReference();
if (entityRef == null || !entityRef.isValid()) {
ctx.sender().sendMessage(Message.raw("Joueur non trouvé"));
return null;
}
EntityEffect effect = EntityEffect.getAssetMap().getAsset(effectId);
if (effect == null) {
ctx.sender().sendMessage(Message.raw("Effet inconnu : " + effectId));
return null;
}
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null) {
controller.addEffect(
entityRef,
effect,
(float) duration,
OverlapBehavior.EXTEND,
componentAccessor
);
}
ctx.sender().sendMessage(Message.raw("Appliqué " + effectId + " à " + target.getUsername()));
return null;
}
}
```
{{< callout type="warning" >}}
**Notes API :**
- Utilisez `withRequiredArg()` pour définir les arguments, pas `addArgument()`
- Utilisez `arg.get(ctx)` pour récupérer les valeurs, pas `ctx.getArg()`
- Utilisez `ctx.sender()` pas `ctx.getSender()`
- Utilisez `target.getUsername()` pas `target.getName()`
- Utilisez `target.getReference()` pour obtenir la référence d'entité (PlayerRef n'a pas `getPlayer()`)
{{< /callout >}}
### Effet de Dégâts dans le Temps
```java
public void applyPoison(Player player, float duration, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
EntityEffect poison = EntityEffect.getAssetMap().getAsset("poison");
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && poison != null) {
controller.addEffect(
entityRef,
poison,
duration,
OverlapBehavior.EXTEND,
componentAccessor
);
}
player.sendMessage(Message.raw("Vous avez été empoisonné !"));
}
```
### Invulnérabilité Temporaire
```java
public void grantInvulnerability(Player player, float seconds, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
EntityEffect invuln = EntityEffect.getAssetMap().getAsset("invulnerability");
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && invuln != null) {
controller.addEffect(
entityRef,
invuln,
seconds,
OverlapBehavior.OVERWRITE, // Durée fraîche
componentAccessor
);
}
}
```
### Effacer Tous les Effets
```java
public void clearAllEffects(Player player, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null) {
// Utiliser la méthode clearEffects intégrée
controller.clearEffects(
entityRef,
componentAccessor
);
}
player.sendMessage(Message.raw("Tous les effets effacés !"));
}
```
## Combiner avec d'Autres Effets
Les effets d'entité fonctionnent bien avec les particules et les lumières dynamiques :
```java
public void applyMagicBuff(Player player, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
Ref<EntityStore> entityRef = player.getReference();
// Appliquer l'effet de statut
EntityEffect buff = EntityEffect.getAssetMap().getAsset("magic_power");
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && buff != null) {
controller.addEffect(entityRef, buff, componentAccessor);
}
// Ajouter une lueur visuelle
ColorLight glow = new ColorLight((byte) 10, (byte) 200, (byte) 50, (byte) 255);
DynamicLight light = new DynamicLight(glow);
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
light
);
// Obtenir la position via TransformComponent pour les particules
TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType());
if (transform != null) {
ParticleUtil.spawnParticleEffect(
"magic_aura",
transform.getPosition(),
componentAccessor
);
}
}
```
{{< callout type="info" >}}
**Note :** Utilisez `player.getReference()` pour obtenir la référence d'entité. La position doit être obtenue via `TransformComponent`, pas directement depuis Player.
{{< /callout >}}
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives pour les Effets :**
- Utilisez `EXTEND` pour les buffs cumulables afin de récompenser l'application répétée
- Utilisez `OVERWRITE` pour les effets qui doivent réinitialiser leur durée
- Utilisez `IGNORE` pour empêcher l'empilement d'effets quand indésirable
- Vérifiez toujours si l'effet existe avant de l'appliquer
- Considérez les performances avec de nombreux effets simultanés
{{< /callout >}}

View File

@@ -0,0 +1,788 @@
---
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<Ref<EntityStore>>
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<Integer, ParticleAnimationFrame> 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<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
if (hasTrailEffect(player)) {
// Get position from TransformComponent
Ref<EntityStore> 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<UUID, Boolean> activeAuras = new HashMap<>();
private Store<EntityStore> store;
private ComponentAccessor<EntityStore> 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<UUID, Boolean> entry : activeAuras.entrySet()) {
if (entry.getValue()) {
Player player = getPlayer(entry.getKey());
if (player != null) {
// Get position from TransformComponent
Ref<EntityStore> 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<PlayerRef> 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

View File

@@ -0,0 +1,788 @@
---
title: Particules
type: docs
weight: 1
---
Le système de particules de Hytale fournit des effets visuels riches à travers des émetteurs et renderers configurables. Créez des explosions, traînées, auras et effets visuels complexes avec un contrôle précis sur le comportement et l'apparence.
**Package :** `com.hypixel.hytale.server.core.asset.type.particle`
{{< cards cols="3" >}}
{{< card link="#générer-des-particules" title="Génération" subtitle="Créer des effets de particules" icon="sparkles" >}}
{{< card link="#configuration-du-système-de-particules" title="Configuration" subtitle="Config système et spawner" icon="adjustments" >}}
{{< card link="#attracteurs-de-particules" title="Attracteurs" subtitle="Forces et mouvement" icon="arrow-circle-right" >}}
{{< /cards >}}
---
## Générer des Particules
### Méthodes ParticleUtil
{{< tabs items="Basique,Avancé,WorldParticle" >}}
{{< tab >}}
```java
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;
// Génération basique à une position (trouve auto les joueurs proches)
ParticleUtil.spawnParticleEffect(
"explosion_small", // ID du système de particules
position, // Position Vector3d
componentAccessor
);
// Générer pour des joueurs spécifiques
ParticleUtil.spawnParticleEffect(
"magic_trail",
position,
playerRefs, // List<Ref<EntityStore>>
componentAccessor
);
```
**Distance de Diffusion :** `DEFAULT_PARTICLE_DISTANCE = 75` blocs
{{< /tab >}}
{{< tab >}}
```java
// Avec référence à l'entité source
ParticleUtil.spawnParticleEffect(
"attack_swing",
position,
sourceEntityRef, // Entité qui a généré ceci
playerRefs,
componentAccessor
);
// Avec contrôle de rotation
ParticleUtil.spawnParticleEffect(
"directional_beam",
position,
yaw, pitch, roll, // Angles de rotation
sourceEntityRef,
playerRefs,
componentAccessor
);
// Avec échelle et couleur
ParticleUtil.spawnParticleEffect(
"colored_burst",
position,
yaw, pitch, roll,
2.0f, // Multiplicateur d'échelle
new Color(255, 100, 50, 255), // Couleur RGBA
playerRefs,
componentAccessor
);
```
{{< /tab >}}
{{< tab >}}
```java
import com.hypixel.hytale.server.core.asset.type.particle.config.WorldParticle;
// Contrôle complet avec le wrapper WorldParticle
WorldParticle worldParticle = new WorldParticle(
"my_particle_system",
new Color(255, 100, 50, 255), // Override couleur RGBA
1.5f, // Multiplicateur d'échelle
new Vector3f(0, 1, 0), // Offset de position
new Direction(0, 0, 0) // Offset de rotation
);
ParticleUtil.spawnParticleEffect(
worldParticle,
position,
sourceRef,
playerRefs,
componentAccessor
);
```
{{< /tab >}}
{{< /tabs >}}
---
## Configuration du Système de Particules
### Classe ParticleSystem
La configuration racine pour un effet de particules :
```java
public class ParticleSystem {
protected String id; // Identifiant unique
protected ParticleSpawnerGroup[] spawners; // Configurations de spawners
protected float lifeSpan; // Durée de vie du système (secondes)
protected float cullDistance; // Distance avant culling
protected float boundingRadius; // Boîte englobante de collision
protected boolean isImportant; // Flag de priorité réseau
}
```
| Champ | Type | Description |
|-------|------|-------------|
| `id` | String | Identifiant unique du système de particules |
| `spawners` | ParticleSpawnerGroup[] | Tableau de configurations de spawners |
| `lifeSpan` | float | Durée de vie du système (secondes) |
| `cullDistance` | float | Distance à laquelle les particules sont culled |
| `boundingRadius` | float | Sphère englobante pour les calculs de culling |
| `isImportant` | boolean | Si vrai, prioritisé dans la sync réseau |
### Configuration ParticleSpawner
Contrôle comment les particules individuelles sont émises :
{{< tabs items="Propriétés,Émission,Mouvement,Rendu" >}}
{{< 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 >}}
**Propriétés d'Émission :**
| Propriété | Type | Description |
|-----------|------|-------------|
| `shape` | EmitShape | Forme d'émission Sphere ou Cube |
| `emitOffset` | RangeVector3f | Plage d'offset aléatoire depuis l'origine |
| `useEmitDirection` | boolean | Utiliser la direction de spawn pour la vélocité |
| `totalParticles` | Range | Nombre min/max total de particules à émettre |
| `spawnRate` | Rangef | Particules par seconde |
| `spawnBurst` | boolean | Émettre tout d'un coup vs sur la durée |
**Exemple de config d'émission :**
```java
// Explosion burst - tout d'un coup
spawner.setSpawnBurst(true);
spawner.setTotalParticles(new Range(50, 100));
// Flux continu
spawner.setSpawnBurst(false);
spawner.setSpawnRate(new Rangef(10, 20)); // 10-20 par seconde
spawner.setLifeSpan(5.0f); // Émettre pendant 5 secondes
```
{{< /tab >}}
{{< tab >}}
**Propriétés de Mouvement :**
| Propriété | Type | Description |
|-----------|------|-------------|
| `initialVelocity` | InitialVelocity | Configuration de vélocité initiale |
| `attractors` | ParticleAttractor[] | Forces appliquées aux particules |
**Vélocité Initiale :**
```java
public class InitialVelocity {
protected float speed; // Vitesse de base
protected float speedVariance; // Variance aléatoire
protected Vector3f direction; // Direction de base
protected float coneAngle; // Angle de dispersion (degrés)
}
```
{{< /tab >}}
{{< tab >}}
**Propriétés de Rendu :**
| Propriété | Type | Description |
|-----------|------|-------------|
| `renderMode` | FXRenderMode | Comment les particules sont blendées |
| `lightInfluence` | float | Influence de l'éclairage sur les particules |
| `particle` | Particle | Config texture et animation |
{{< /tab >}}
{{< /tabs >}}
---
## Formes d'Émission
Contrôle le volume depuis lequel les particules apparaissent :
```java
public enum EmitShape {
Sphere, // Émettre depuis un volume sphérique
Cube // Émettre depuis un volume cubique
}
```
{{< tabs items="Sphere,Cube" >}}
{{< tab >}}
**Émission Sphérique :**
Les particules apparaissent dans un volume sphérique, avec la direction pointant vers l'extérieur du centre :
```java
// Configurer l'émission sphérique
spawner.setShape(EmitShape.Sphere);
spawner.setEmitOffset(new RangeVector3f(
new Rangef(-1, 1), // Plage X (rayon)
new Rangef(-1, 1), // Plage Y (rayon)
new Rangef(-1, 1) // Plage Z (rayon)
));
// Utiliser la direction d'émission pour la vélocité vers l'extérieur
spawner.setUseEmitDirection(true);
```
Idéal pour : Explosions, bursts, effets radiaux
{{< /tab >}}
{{< tab >}}
**Émission Cubique :**
Les particules apparaissent dans un volume de boîte aligné sur les axes :
```java
// Configurer l'émission cubique
spawner.setShape(EmitShape.Cube);
spawner.setEmitOffset(new RangeVector3f(
new Rangef(-2, 2), // Plage X
new Rangef(0, 3), // Plage Y (au-dessus du sol)
new Rangef(-2, 2) // Plage Z
));
```
Idéal pour : Effets de zone, pluie, effets au sol
{{< /tab >}}
{{< /tabs >}}
---
## Modes de Rendu
Détermine comment les particules se mélangent avec la scène :
```java
public enum FXRenderMode {
BlendLinear, // Blending de transparence standard
BlendAdd, // Blending additif (brillant/lumineux)
Erosion, // Effet d'érosion/dissolution
Distortion // Effet de distorsion/réfraction
}
```
{{< tabs items="BlendLinear,BlendAdd,Erosion,Distortion" >}}
{{< tab >}}
### BlendLinear
Blending alpha standard. Idéal pour :
- Fumée
- Poussière
- Nuages
- Particules d'apparence solide
```java
spawner.setRenderMode(FXRenderMode.BlendLinear);
```
{{< /tab >}}
{{< tab >}}
### BlendAdd
Blending additif - les particules ajoutent de la lumière à la scène. Idéal pour :
- Feu
- Étincelles
- Effets magiques
- Particules lumineuses
- Rayons de lumière
```java
spawner.setRenderMode(FXRenderMode.BlendAdd);
```
{{< callout type="info" >}}
Utilisez `BlendAdd` pour tout effet lumineux ou brillant. Plusieurs particules superposées créeront des zones plus lumineuses.
{{< /callout >}}
{{< /tab >}}
{{< tab >}}
### Erosion
Crée un effet visuel de dissolution/érosion. Idéal pour :
- Désintégration
- Transitions de dissolution
- Dissipation d'énergie
```java
spawner.setRenderMode(FXRenderMode.Erosion);
```
{{< /tab >}}
{{< tab >}}
### Distortion
Réfracte l'arrière-plan, créant des effets de miroitement de chaleur. Idéal pour :
- Vagues de chaleur
- Portails
- Champs d'énergie
- Effets sous-marins
```java
spawner.setRenderMode(FXRenderMode.Distortion);
```
{{< callout type="warning" >}}
Les effets de distorsion sont plus intensifs en GPU. Utilisez avec parcimonie.
{{< /callout >}}
{{< /tab >}}
{{< /tabs >}}
---
## Attracteurs de Particules
Applique des forces aux particules pour un mouvement dynamique :
```java
public class ParticleAttractor {
protected Vector3f position; // Position locale de l'attracteur
protected Vector3f radialAxis; // Direction de la force radiale
protected float radius; // Rayon d'influence
// Accélérations (forces continues)
protected float radialAcceleration; // Accélération vers l'extérieur/intérieur
protected float radialTangentAcceleration; // Accélération tangentielle (orbite)
protected Vector3f linearAcceleration; // Accélération linéaire directe
// Impulsions (forces ponctuelles)
protected float radialImpulse; // Impulsion vers l'extérieur/intérieur
protected float radialTangentImpulse; // Impulsion tangentielle
protected Vector3f linearImpulse; // Impulsion linéaire directe
// Amortissement
protected Vector3f dampingMultiplier; // Réduction de vélocité par frame
}
```
### Types de Forces
{{< tabs items="Radiale,Tangentielle,Linéaire,Amortissement" >}}
{{< tab >}}
**Forces Radiales :**
Pousse les particules vers ou loin de la position de l'attracteur :
```java
ParticleAttractor attractor = new ParticleAttractor();
attractor.setPosition(new Vector3f(0, 0, 0)); // Centre du système
// Explosion vers l'extérieur
attractor.setRadialAcceleration(10.0f); // Positif = vers l'extérieur
// Attraction vers l'intérieur (effet trou noir)
attractor.setRadialAcceleration(-5.0f); // Négatif = vers l'intérieur
// Burst instantané vers l'extérieur
attractor.setRadialImpulse(20.0f);
```
{{< /tab >}}
{{< tab >}}
**Forces Tangentielles :**
Crée un mouvement tourbillonnant/orbital autour de l'attracteur :
```java
ParticleAttractor attractor = new ParticleAttractor();
attractor.setPosition(new Vector3f(0, 0, 0));
attractor.setRadialAxis(new Vector3f(0, 1, 0)); // Orbite autour de l'axe Y
// Tourbillon horaire
attractor.setRadialTangentAcceleration(5.0f);
// Anti-horaire
attractor.setRadialTangentAcceleration(-5.0f);
```
{{< /tab >}}
{{< tab >}}
**Forces Linéaires :**
Applique une force directionnelle constante (comme la gravité ou le vent) :
```java
ParticleAttractor attractor = new ParticleAttractor();
// Gravité (vers le bas)
attractor.setLinearAcceleration(new Vector3f(0, -9.8f, 0));
// Vent (horizontal)
attractor.setLinearAcceleration(new Vector3f(2.0f, 0, 0));
// Flottabilité vers le haut
attractor.setLinearAcceleration(new Vector3f(0, 3.0f, 0));
```
{{< /tab >}}
{{< tab >}}
**Amortissement :**
Ralentit les particules au fil du temps :
```java
ParticleAttractor attractor = new ParticleAttractor();
// Amortissement uniforme (résistance de l'air)
attractor.setDampingMultiplier(new Vector3f(0.98f, 0.98f, 0.98f));
// Fort amortissement horizontal, faible vertical
attractor.setDampingMultiplier(new Vector3f(0.9f, 0.99f, 0.9f));
// Pas d'amortissement (particules maintiennent leur vélocité)
attractor.setDampingMultiplier(new Vector3f(1.0f, 1.0f, 1.0f));
```
{{< callout type="info" >}}
Les valeurs d'amortissement < 1.0 ralentissent les particules. Des valeurs de 0.98-0.99 donnent une résistance de l'air réaliste.
{{< /callout >}}
{{< /tab >}}
{{< /tabs >}}
---
## Configuration Visuelle des Particules
Configure l'apparence et l'animation des particules :
```java
public class Particle {
protected String texture; // Chemin de l'atlas de texture
protected Size frameSize; // Dimensions du frame
protected ParticleUVOption uvOption; // None, Animated, Random
protected SoftParticle softParticle; // Blending soft particle
protected float softParticlesFadeFactor; // 0.1 à 2.0
// Animation
protected ParticleAnimationFrame initialAnimationFrame;
protected Map<Integer, ParticleAnimationFrame> animation;
}
```
### Options d'Animation
```java
public enum ParticleUVOption {
None, // Frame unique statique
Animated, // Jouer les frames en séquence
Random // Frame aléatoire par particule
}
```
### Frames d'Animation
```java
public class ParticleAnimationFrame {
protected int frame; // Numéro de frame dans l'atlas
protected Rangef scale; // Plage de taille
protected Rangef alpha; // Plage de transparence (0-1)
protected Color color; // Teinte de couleur
protected Rangef rotation; // Plage de rotation (degrés)
}
```
---
## Accéder aux Assets de Particules
```java
// Obtenir un système de particules par ID
ParticleSystem system = ParticleSystem.getAssetMap().getAsset("explosion_large");
// Obtenir la configuration d'un spawner
ParticleSpawner spawner = ParticleSpawner.getAssetMap().getAsset("fire_spawner");
// Utiliser dans les arguments de commande
// ArgTypes.PARTICLE_SYSTEM pour les paramètres de commande
```
---
## Patterns Courants
### Particules Basées sur les Événements
{{< callout type="warning" >}}
**Note :** Les exemples ci-dessous sont simplifiés. Entity n'a pas de méthode `getPosition()` directe. Dans le code réel, obtenez la position via `TransformComponent` depuis le store de l'entité. Exemple :
```java
TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType());
Vector3d position = transform.getPosition();
```
{{< /callout >}}
{{< tabs items="Destruction Bloc,Combat,Mort" >}}
{{< tab >}}
```java
@Subscribe
public void onBlockBreak(BreakBlockEvent event) {
// Utiliser getTargetBlock() - pas getPosition()
Vector3i blockPos = event.getTargetBlock();
Vector3d pos = new Vector3d(
blockPos.x + 0.5,
blockPos.y + 0.5,
blockPos.z + 0.5
);
// Générer des particules de destruction au centre du bloc
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);
// Effet de sang/dégâts
ParticleUtil.spawnParticleEffect(
"damage_hit",
hitPos,
componentAccessor
);
// Effet spécial coup critique
if (event.isCritical()) {
ParticleUtil.spawnParticleEffect(
"critical_hit_sparks",
hitPos,
0, 0, 0, // rotation
1.5f, // échelle plus grande
new Color(255, 215, 0, 255), // couleur or
getNearbyPlayers(hitPos, 50),
componentAccessor
);
}
}
```
{{< /tab >}}
{{< tab >}}
```java
@Subscribe
public void onEntityDeath(EntityDeathEvent event) {
Entity entity = event.getEntity();
Vector3d deathPos = entity.getPosition();
// Particules de mort
ParticleUtil.spawnParticleEffect(
"entity_death_poof",
deathPos,
componentAccessor
);
// Effet d'âme qui monte pour les joueurs
if (entity instanceof Player) {
ParticleUtil.spawnParticleEffect(
"soul_ascend",
deathPos.add(0, 0.5, 0),
componentAccessor
);
}
}
```
{{< /tab >}}
{{< /tabs >}}
### Effets Continus
{{< tabs items="Traînée Joueur,Aura,Effet de Zone" >}}
{{< tab >}}
```java
// Générer une traînée de particules à la position du joueur
public void spawnTrailEffect(Player player, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
if (hasTrailEffect(player)) {
// Obtenir la position via TransformComponent
Ref<EntityStore> 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 n'a pas de méthode `getPosition()`. Obtenez la position via `TransformComponent` depuis le store d'entités.
{{< /callout >}}
{{< /tab >}}
{{< tab >}}
```java
// Aura persistante autour du joueur
public class AuraManager {
private final Map<UUID, Boolean> activeAuras = new HashMap<>();
private Store<EntityStore> store;
private ComponentAccessor<EntityStore> componentAccessor;
public void enableAura(Player player) {
// Utiliser player.getPlayerRef().getUuid() - pas player.getUuid()
activeAuras.put(player.getPlayerRef().getUuid(), true);
}
@Subscribe
public void onTick(ServerTickEvent event) {
for (Map.Entry<UUID, Boolean> entry : activeAuras.entrySet()) {
if (entry.getValue()) {
Player player = getPlayer(entry.getKey());
if (player != null) {
// Obtenir la position via TransformComponent
Ref<EntityStore> 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
// Effet de zone de soin
public void createHealingZone(Vector3d center, double radius, int durationTicks) {
// Générer des particules en anneau
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
);
}
// Effet pilier central
ParticleUtil.spawnParticleEffect(
"healing_pillar",
center,
componentAccessor
);
}
```
{{< /tab >}}
{{< /tabs >}}
---
## Directives de Performance
{{< callout type="warning" >}}
**Notes de Performance :**
- Distance de diffusion par défaut : **75 blocs** (`DEFAULT_PARTICLE_DISTANCE`)
- Les particules utilisent le paquet `SpawnParticleSystem` (ID : 152)
- Définissez `isImportant = true` pour les feedbacks visuels critiques
- Limitez le nombre de particules pour les performances client
- Utilisez `maxConcurrentParticles` pour limiter les particules actives
{{< /callout >}}
### Conseils d'Optimisation
| Conseil | Description |
|---------|-------------|
| Limiter le total de particules | Gardez sous 100-200 pour les effets burst |
| Utiliser une `cullDistance` appropriée | Ne pas rendre les particules trop éloignées |
| Grouper les spawns | Générer plusieurs particules dans le même tick |
| Utiliser `spawnBurst` judicieusement | Les bursts sont moins coûteux que le continu |
| Considérer le nombre de joueurs | Plus de joueurs = plus de trafic réseau |
```java
// Bon : Génération de particules efficace
public void spawnEfficiently(Vector3d position) {
// Générer seulement pour les joueurs proches
List<PlayerRef> nearbyPlayers = getNearbyPlayers(position, 50);
if (!nearbyPlayers.isEmpty()) {
ParticleUtil.spawnParticleEffect(
"my_effect",
position,
nearbyPlayers, // Audience limitée
componentAccessor
);
}
}
```
---
## Sujets Connexes
- [Effets d'Entité]({{< relref "entity-effects" >}}) - Effets de statut avec composants visuels
- [Réseau]({{< relref "/advanced/networking" >}}) - Comment les particules sont diffusées
- [Événements]({{< relref "/core-concepts/events" >}}) - Déclencher des particules depuis les événements