Breath Weapon

This is a library/api mod to render geckolib models as particles

45

Quick challenge

How far can you run before the mobs catch you?

Breath Weapon

Using BreathWeapon

This guide explains how to integrate the BreathWeapon particle system into your Minecraft mod to create custom particles without writing complex rendering code.

Setup

1. Add Dependency

In your `build.gradle`, add BreathWeapon to your dependencies:

```gradle mavenCentral() exclusiveContent { forRepository { maven { name = "Modrinth" url = "https://api.modrinth.com/maven" } } forRepositories(fg.repository) filter { includeGroup "maven.modrinth" } }

dependencies { implementation fg.deobf("maven.modrinth:breath-weapon:0.0.1") } ```

2. Import the API

In your code, import the spawning utilities:

```java import com.dracolich777.breathweapon.util.ParticleSpawner; import com.dracolich777.breathweapon.api.particle.ParticleRegistry; import net.minecraft.resources.ResourceLocation; ```

---

Quick Start

Minimal Setup

1. Create a particle definition JSON:

``` src/main/resources/data/yourmodid/particles/my_effect.json ```

```json { "type": "sprite", "lifetime": 50 } ```

2. Spawn it in code:

```java ParticleSpawner.spawn(level, new ResourceLocation("yourmodid", "my_effect"), x, y, z, vx, vy, vz); ```

That's it! The particle will render with default texture and physics.

---

Creating Particle Definitions

Particle definitions are JSON files placed in your mod's datapacks. Create them at:

``` src/main/resources/data/yourmodid/particles/particle_name.json ```

File Structure

``` src/main/resources/ ├── assets/yourmodid/ │ └── textures/ │ └── particle/ │ ├── beam.png │ ├── spark.png │ └── smoke.png └── data/yourmodid/ └── particles/ ├── my_beam.json ├── my_spark.json └── my_smoke.json ```

Basic JSON Structure

Every particle definition has this structure:

```json { "type": "sprite | beam | geo", "texture": "yourmodid:textures/particle/name", "lifetime": 200, "scale": 1.0, "gravity": 0.0, "drag": 1.0, "light": { ... }, "animation": { ... } } ```

Common Properties

All particle types support:

| Property | Type | Default | Description | |----------|------|---------|-------------| | `type` | string | "sprite" | Particle type: sprite, beam, or geo | | `lifetime` | int | 200 | Duration in ticks (1 tick = 50ms) | | `scale` | float | 1.0 | Size multiplier (0.1 - 10.0) | | `gravity` | float | 0.0 | Downward acceleration (negative = upward) | | `drag` | float | 1.0 | Velocity damping (0.9 = 10% slowdown/tick) | | `texture` | string | null | Texture path in format `modid:path` |

Light Configuration

Add glow/dynamic lighting to particles:

```json { "type": "sprite", "light": { "mode": "glow", "color": [1.0, 0.5, 0.0], "intensity": 1.0 } } ```

Light Modes: - `"none"` - No special lighting - `"glow"` - Constant glow effect - `"pulse"` - Pulsing brightness

For Glow/Pulse: ```json "light": { "mode": "glow", "color": [r, g, b], "intensity": brightness_0_to_2 } ```

For Pulse: ```json "light": { "mode": "pulse", "color": [r, g, b], "intensity": 1.0, "pulse_min": 0.3, "pulse_max": 1.0, "pulse_speed": 0.1 } ```

---

Spawning Particles

Basic Spawning

Single particle:

```java ParticleSpawner.spawn(level, new ResourceLocation("yourmodid", "my_particle"), x, y, z, velocityX, velocityY, velocityZ); ```

Pattern Spawning

Burst (random directions):

```java ParticleSpawner.burst(level, new ResourceLocation("yourmodid", "spark"), center, // Vec3 position count, // Number of particles (e.g., 10) speed); // Speed in blocks/tick (e.g., 0.5) ```

Stream (directional):

```java ParticleSpawner.stream(level, new ResourceLocation("yourmodid", "smoke"), entity, // Entity to spawn from count, // Number of particles speed, // Particle speed spread); // Angular spread in radians ```

Beam Spawning

Uniform beam:

```java ParticleSpawner.spawnBeam(level, new ResourceLocation("yourmodid", "laser"), from, // Vec3 start position to, // Vec3 end position width); // Beam width (e.g., 0.3f) ```

Tapered beam (width changes along length):

```java ParticleSpawner.spawnBeam(level, new ResourceLocation("yourmodid", "laser"), from, // Vec3 start to, // Vec3 end startWidth, // Width at start (e.g., 0.5f) endWidth); // Width at end (e.g., 0.1f) ```

---

Particle Types

Type 1: Sprite (Default)

Simple 2D particles. Fast and lightweight.

Definition:

```json { "type": "sprite", "texture": "yourmodid:particle/spark", "lifetime": 30, "scale": 0.5, "gravity": 0.05, "drag": 0.98, "light": { "mode": "glow", "color": [1.0, 1.0, 0.0], "intensity": 0.8 } } ```

Best for: Sparks, sparkles, dust, simple effects

Type 2: Beam

Billboard between two points. Supports dynamic endpoints.

Definition:

```json { "type": "beam", "texture": "yourmodid:particle/laser", "lifetime": 100, "scale": 1.0, "light": { "mode": "glow", "color": [1.0, 0.0, 0.0], "intensity": 1.5 } } ```

Best for: Lasers, energy blasts, directed attacks, tracers

Type 3: Geo (GeckoLib Model)

Full 3D models with animation support.

Definition:

```json { "type": "geo", "model": "yourmodid:geo/explosion.geo.json", "texture": "yourmodid:textures/particle/explosion.png", "animation": "yourmodid:animations/explosion.animation.json", "anim_name": "animation.explosion.burst", "lifetime": 50, "scale": 0.8, "gravity": -0.05, "drag": 0.95, "spin": { "yaw": 5.0, "pitch": 2.0, "roll": 3.0 } } ```

Properties: - `model` - Path to your GeckoLib `.geo.json` file - `texture` - Texture for the model - `animation` - Path to animation file (optional) - `anim_name` - Which animation to play (optional) - `spin` - Rotation speed in degrees/tick (optional)

Best for: Complex effects, explosions, magical effects, custom models

---

Advanced Features

Dynamic Beam Endpoints

Make beams that track targets or move over time:

```java import com.dracolich777.breathweapon.client.particle.SimpleBeamParticle;

// When spawning, you need to access the particle directly // This requires a custom spawning approach or event listener

SimpleBeamParticle.EndpointProvider provider = (age, lifetime) -> { // Calculate new position based on age float progress = age / (float) lifetime;

// Example: Spiral endpoint double angle = progress * Math.PI * 4; // 2 rotations double newX = centerX + Math.cos(angle) * radius; double newY = centerY + Math.sin(angle) * radius; double newZ = centerZ + Math.sin(progress * Math.PI) * 5;

return new SimpleBeamParticle.BeamEndpoint(newX, newY, newZ); };

// Set on particle (requires direct particle access via event or hook) particle.setEndpointProvider(provider); ```

Physics Customization

Control how particles move:

```json { "gravity": 0.1, // Fall speed (positive = down, negative = up) "drag": 0.95, // Slow down by 5% each tick "scale": 1.5 // Make it 1.5x bigger } ```

Examples:

- Fast zoom: `"gravity": -0.01, "drag": 1.0` - Fast zoom: `"gravity": 0.02, "drag": 0.99` - Fast zoom: Use short lifetime + high drag - Fast zoom: Use high initial velocity + low gravity

---

Complete Examples

Example 1: Energy Beam Attack

JSON Definition (`data/mymod/particles/energy_blast.json`):

```json { "type": "beam", "texture": "mymod:particle/energy_beam", "lifetime": 80, "scale": 0.9, "light": { "mode": "glow", "color": [0.2, 0.7, 1.0], "intensity": 1.3 } } ```

Spawning Code (in an ability handler):

```java @SubscribeEvent public static void onPlayerAttack(LivingAttackEvent event) { if (event.getEntity() instanceof Player player && player.level() instanceof ServerLevel level) {

Vec3 from = player.getEyePosition(); Vec3 to = from.add(player.getLookAngle().scale(50));

ParticleSpawner.spawnBeam(level, new ResourceLocation("mymod", "energy_blast"), from, to, 0.2f); } } ```

Example 2: Explosion Effect

JSON Definition (`data/mymod/particles/explosion_burst.json`):

```json { "type": "sprite", "texture": "mymod:particle/explosion", "lifetime": 40, "scale": 1.2, "gravity": -0.02, "drag": 0.96, "light": { "mode": "pulse", "color": [1.0, 0.6, 0.0], "intensity": 1.5, "pulse_min": 0.5, "pulse_max": 1.5, "pulse_speed": 0.15 } } ```

Spawning Code:

```java public static void createExplosion(ServerLevel level, Vec3 center, int power) { ParticleSpawner.burst(level, new ResourceLocation("mymod", "explosion_burst"), center, power * 3, // More particles for bigger explosion power * 0.1); // Faster spread for bigger explosion } ```

Example 3: Magical Aura

JSON Definition (`data/mymod/particles/mana_dust.json`):

```json { "type": "sprite", "texture": "mymod:particle/mana", "lifetime": 100, "scale": 0.6, "gravity": -0.005, "drag": 0.98, "light": { "mode": "glow", "color": [0.7, 0.3, 1.0], "intensity": 0.9 } } ```

Spawning Code (in entity tick):

```java public void createManaAura(Entity entity, ServerLevel level) { Vec3 pos = entity.position().add(0, entity.getBbHeight() / 2, 0);

// Small burst of particles around entity ParticleSpawner.burst(level, new ResourceLocation("mymod", "mana_dust"), pos, 5, // 5 particles 0.3); // Slow spread } ```

Example 4: Status Effect Indicator

JSON Definition (`data/mymod/particles/poison_cloud.json`):

```json { "type": "sprite", "texture": "mymod:particle/poison", "lifetime": 60, "scale": 0.8, "gravity": 0.01, "drag": 0.99, "light": { "mode": "glow", "color": [0.3, 0.8, 0.2], "intensity": 0.7 } } ```

Spawning Code (while effect active):

```java @SubscribeEvent public static void onLivingUpdate(LivingTickEvent event) { LivingEntity entity = event.getEntity();

if (entity.hasEffect(MobEffects.POISON) && entity.level() instanceof ServerLevel level && entity.tickCount % 5 == 0) { // Every 5 ticks

Vec3 pos = entity.position().add( (Math.random() - 0.5) * 0.5, Math.random() * entity.getBbHeight(), (Math.random() - 0.5) * 0.5 );

ParticleSpawner.spawn(level, new ResourceLocation("mymod", "poison_cloud"), pos.x, pos.y, pos.z, 0, -0.05, 0); // Slight upward drift } } ```

---

Checking Particle Availability

Before spawning, verify the particle is registered:

```java import com.dracolich777.breathweapon.api.particle.ParticleRegistry;

ResourceLocation particleId = new ResourceLocation("yourmodid", "my_particle");

if (ParticleRegistry.has(particleId)) { // Safe to spawn ParticleSpawner.spawn(level, particleId, x, y, z, 0, 0, 0); } else { LOGGER.warn("Particle not found: {}", particleId); } ```

Get particle info:

```java var definition = ParticleRegistry.get("yourmodid:my_particle"); if (definition != null) { int lifetime = definition.getLifetime(); float scale = definition.getScale(); String type = definition.getType(); // "beam", "geo", "sprite" } ```

---

Best Practices

1. Organize Your Particles

``` data/yourmodid/particles/ ├── abilities/ │ ├── fireball.json │ ├── lightning.json │ └── healing_circle.json ├── effects/ │ ├── hit_effect.json │ ├── death_burst.json │ └── buff_indicator.json └── ambient/ ├── magical_dust.json ├── fire_embers.json └── water_splash.json ```

2. Keep Lifetimes Short

Don't make particles last longer than needed: - Sparks: 20-60 ticks - Beams: 50-150 ticks - Explosions: 30-100 ticks - Ambient: 60-200 ticks

3. Test Physics

```json { "gravity": 0.0, // Start with no gravity "drag": 1.0 // Start with no drag } ```

Then adjust based on visual appearance.

4. Use Appropriate Types

- Geo - Most effects (fast) - Geo - Targeted effects, attacks - Geo - Special occasions (slower)

5. Reuse Definitions

Create one definition and spawn it multiple times:

```java for (int i = 0; i < 10; i++) { ParticleSpawner.spawn(level, new ResourceLocation("mymod", "explosion"), x, y, z, randomVelX, randomVelY, randomVelZ); } ```

6. Add Descriptions to JSONs

```json { "_comment": "Blue energy beam - used for mage spell attacks", "type": "beam", ... } ```

---

Troubleshooting

Particles Not Showing

Problem: I spawned a particle but it doesn't appear in-game.

Solutions: 1. Check particle definition is in correct folder: `data/yourmodid/particles/name.json` 2. Verify the particle ID matches the JSON filename 3. Use `/reload` to reload data 4. Check texture path exists: `assets/yourmodid/textures/particle/name.png` 5. Verify you're on the correct level (ServerLevel, not ClientLevel)

Debug code: ```java var def = ParticleRegistry.get("yourmodid:my_particle"); if (def == null) { LOGGER.error("Particle not found in registry!"); } else { LOGGER.info("Particle found: {}", def); } ```

Texture Not Loading

Problem: Particle appears but texture is wrong.

Solutions: 1. Verify texture path uses forward slashes: `"mymod:particle/name"` 2. Check texture file is PNG format 3. Texture path should NOT include `.png` extension 4. Texture should be in `assets/yourmodid/textures/`

Beam Position Wrong

Problem: Beam starts/ends at wrong position.

Solutions: 1. Use world space coordinates (not relative) 2. For entities: `entity.getEyePosition()` for eye level 3. For blocks: `new Vec3(x + 0.5, y + 0.5, z + 0.5)` for center 4. Verify `from` and `to` are not the same position

Particle Too Slow/Fast

Problem: Particle movement doesn't match expected velocity.

Solutions: 1. Increase `speed` parameter in `burst()` or `stream()` 2. Adjust `drag` - lower values = keeps velocity longer 3. Check particle spawn velocity is correct 4. Use `gravity` to add momentum over time

GeckoLib Model Not Loading

Problem: Type is "geo" but model doesn't render.

Solutions: 1. Verify model file path is correct 2. Check `.geo.json` file exists 3. Model path should NOT include `.geo.json` in JSON (just `path` without extension) 4. Texture path must match model's texture reference 5. Animation name must match exactly (case-sensitive)

---

API Reference

ParticleSpawner Methods

```java // Single particle ParticleSpawner.spawn(level, id, x, y, z, vx, vy, vz); ParticleSpawner.spawn(level, id, x, y, z, vx, vy, vz, extraData);

// Patterns ParticleSpawner.burst(level, id, center, count, speed); ParticleSpawner.stream(level, id, entity, count, speed, spread);

// Beams ParticleSpawner.spawnBeam(level, id, from, to, width); ParticleSpawner.spawnBeam(level, id, from, to, startWidth, endWidth); ```

ParticleRegistry Methods

```java ParticleRegistry.register(id, definition); ParticleRegistry.get(id); ParticleRegistry.has(id); ParticleRegistry.getAll(); ParticleRegistry.getAllFromMod(namespace); ```

---

Need Help?

- DM me on discord or join my discord server: - Discord Server - `.themrsuit`

Good luck creating amazing particles!

ADS