Init
This commit is contained in:
92
content/advanced/effects/_index.en.md
Normal file
92
content/advanced/effects/_index.en.md
Normal 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 >}}
|
||||
92
content/advanced/effects/_index.fr.md
Normal file
92
content/advanced/effects/_index.fr.md
Normal 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 >}}
|
||||
268
content/advanced/effects/dynamic-lights.en.md
Normal file
268
content/advanced/effects/dynamic-lights.en.md
Normal 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 >}}
|
||||
268
content/advanced/effects/dynamic-lights.fr.md
Normal file
268
content/advanced/effects/dynamic-lights.fr.md
Normal 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 >}}
|
||||
369
content/advanced/effects/entity-effects.en.md
Normal file
369
content/advanced/effects/entity-effects.en.md
Normal 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 >}}
|
||||
369
content/advanced/effects/entity-effects.fr.md
Normal file
369
content/advanced/effects/entity-effects.fr.md
Normal 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 >}}
|
||||
788
content/advanced/effects/particles.en.md
Normal file
788
content/advanced/effects/particles.en.md
Normal 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
|
||||
788
content/advanced/effects/particles.fr.md
Normal file
788
content/advanced/effects/particles.fr.md
Normal 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
|
||||
Reference in New Issue
Block a user