Every effect is a .yml file in plugins/VEngine/effects/. The effect name is the file name
(or the id: field inside). Edit a file, run /ve reload, play it with /ve play <name>.
Broken files never crash the plugin - they are skipped with a warning in the console.
There are two kinds of effect files:
- Formula atom - a shape described by math (
curve:orsurface:block) - Scripted effect - a show composed of
layers:, each layer playing an atom with its own timing, particle and modifiers
Formula atoms
A curve samples one variable (t), a surface samples two (theta, phi). Each sample becomes
one particle per frame.
id: my_ring
curve:
variables: [t]
samples: 96 # particles per frame
range: [{min: 0, max: 6.2831853}] # 0 to tau (full circle)
x: "R * cos(t)"
y: "0"
z: "R * sin(t)"
parameters: # your own constants, usable in x/y/z and overridable at play time
R: 2.0
angularSpeed: 0.05 # how fast the shape internally animates, per tick
particle: FLAME # Bukkit particle name
duration: 120 # ticks (20 = 1 second); default 60
Quick reference:
| Key | Default | Notes |
|---|---|---|
variables |
[t] (curve) / [theta, phi] (surface) |
sampling variable names |
samples |
64 (curve) / 40,20 (surface) |
per-frame particle count, scalar or list |
range |
0..tau |
list of {min, max} per variable |
x, y, z |
0 |
formulas, may use t/theta/phi, your parameters, math functions, and the magic time variable (current tick of the effect) |
parameters |
- | named constants, also overridable via /ve play or the DSL |
particle, duration |
FLAME, 60 |
file-level defaults |
Multiplication is always explicit: write R * cos(t), not R cos(t). Full function list and
surface examples: Parametric Effects.
Other kinds of atoms: implicit surfaces and L-system plants (SDF surfaces and
L-systems), and pictures, text, OBJ models and SVG outlines (Imported
shapes). Atoms built into the plugin - sphere, physics, strange attractors,
fractals and Chladni figures - are listed with all their parameters in Built-in
primitives.
Scripted effects
A scripted effect layers atoms into a show:
id: my_show
duration: 200
layers:
- effect: helix # any atom or scripted effect name
start: 0 # first tick this layer appears (default 0)
duration: 160 # how long this layer runs (default: rest of the show)
particle: END_ROD
color: {r: 120, g: 80, b: 255} # or "#7850FF"; used by DUST-type particles
count: 1
scale: 1.2
rotationYaw: 0.0
rotationPitch: 1.5708 # radians: tilts the whole layer
rotationRoll: 0.0
parameters:
turns: 5
modifiers: [] # see below
animation: {} # see below
display: {} # optional: models instead of particles, see below
Colors accept {r, g, b} maps or hex strings ("#FF8800", FF8800).
Add a top-level follow: block and the effect sticks to the player or
mob that plays it.
Layer timing
start and duration place a layer on the show’s timeline in ticks. Instead of counting ticks,
a layer can start after another one ends: give that layer an id and refer to it with after.
duration: 200
layers:
- id: charge
effect: sphere
duration: 60
- id: burst
effect: torus
after: charge # starts at tick 60
duration: 40
- effect: helix
after: burst
delay: 10 # starts 10 ticks after burst ends, at tick 110
A layer without its own duration runs to the end of the show, so after: such a layer starts
when the show ends and never appears. Unknown ids and after: cycles are reported in the console
and those layers are skipped. With duplicate ids, after: refers to the first layer.
Gradients
gradient gives every particle of a layer its own color, sampled from a list of stops:
particle: DUST
dustSize: 0.6
gradient: ["#3a0ca3", "#f72585", "#ffbe0b"]
Where a particle falls on the gradient depends on the atom: fractals color by depth or position
along the boundary, L-systems from root to tips, SDF surfaces by their value formula or height,
and Chladni figures by vibration. Atoms that assign nothing color their particles in drawing order,
first stop to last. Gradients work with DUST, DUST_COLOR_TRANSITION and ENTITY_EFFECT
particles and with pixel displays. Image shapes use the picture’s colors unless they set
colors: gradient.
Display entities
Add a display: block to a layer and it places an item, block or text model at every point
instead of a particle:
display:
type: item # item | block | text
material: AMETHYST_CLUSTER
size: 0.7
spin: {x: 0, y: 0.12, z: 0} # radians per tick
glow: "#B26BFF"
All keys, animation targets and limits: Display entities.
Particle budget
The server thins particles out with distance and under lag (see particle-budget in
Configuration). Two keys opt an effect out, either at the
top of the file (every layer) or on one layer:
budget: exempt # ignore the particle limits and auto-quality (boss telegraphs, cutscenes)
lod: false # full density at any distance up to view-distance
Use exempt sparingly: exempt particles are sent even when a player or the server is over budget.
Modifiers
Post-processing applied to every particle of a layer, each frame:
modifiers:
- type: turbulence
strength: 0.15 # default 0.12
frequency: 0.35
octaves: 4
- type: rotation # spins the shape around an axis
axis: {x: 0, y: 1, z: 0}
angularVelocity: 0.08 # radians per tick
- type: vortex # swirling pull with a soft core
coreRadius: 1.2
strength: 0.4
- type: motion # moves the whole layer along waypoints
mode: hold # hold | loop | ping_pong
easing: ease_in_out_sine # default easing for legs without their own
path:
- {x: 10, y: 2, z: 0, ticks: 60}
- {x: 20, y: 0, z: 0, ticks: 40, easing: ease_out_cubic}
Waypoint offsets are relative to the effect’s spawn point: at tick 0 the layer is exactly where
it was played. To give several layers the same path, write it once and reuse it with a YAML
anchor (&name / *name) - see traveling_comet.yml for a working example.
Keyframe animation
Number tracks animate layer values over time:
animation:
easing: ease_in_out_sine # track-level default
tracks:
- target: rotationRoll
loop: true # repeat after the last keyframe
keyframes:
- {tick: 0, value: 0}
- {tick: 100, value: 3.1416}
- target: scale
keyframes:
- {tick: 0, value: 0.5}
- {tick: 20, value: 1.5, easing: ease_out_bounce}
Track targets: scale, rotationYaw, rotationPitch, rotationRoll, tiltAngle,
parameters.<name> (your formula parameter), color.r, color.g, color.b, and - on layers with
a display: block - display.size, display.rotation.x, display.rotation.y, display.rotation.z.
Spawning other effects
A scripted effect can start other effects on its timeline. emitters: fire while the show runs,
then: fires after its layers end:
id: firework
duration: 40
layers:
- effect: helix
duration: 40
emitters:
- effect: sphere
start: 10 # first emission at tick 10
every: 5 # then every 5 ticks...
count: 4 # ...4 times in total
offset: {x: 0, y: 3, z: 0}
scale: 0.5
duration: 20
then:
- effect: cosmic_gate
delay: 5 # 5 ticks after the layers end
| Key | Default | Description |
|---|---|---|
effect |
required | Any registered effect, scripted ones included |
start (emitters) / delay (then) |
0 |
Tick of the first emission, from the start or from the end of the layers |
every |
- | Repeat period in ticks. Without it the emitter fires once, count alone repeats every tick |
count |
unlimited | Number of emissions. Without it, repeating emitters fire until the layers end |
offset |
0, 0, 0 |
Position relative to the effect, scaled with it |
scale |
1 |
Multiplies the parent’s scale |
duration |
effect’s own | Ticks the spawned effect plays. Built-in primitives have no duration of their own and play for 60 ticks |
A shorthand emitters: sphere (or a list of names) fires each effect once at tick 0.
- The parent counts as running until everything it spawned has finished, and
/ve stopon the parent stops its children as well. Spawned effects are not listed separately in/ve list. - Children inherit the parent’s receivers and placeholder player. When the parent follows an entity, the children follow it too.
- Effects that spawn themselves, directly or through other effects, are rejected at load time.
max-emitted-effectsinconfig.ymlcaps how many spawned effects play at once on the server, and emissions beyond it are skipped.
Placeholders in values
With PlaceholderAPI installed, a few values can be formulas that contain placeholders. They are evaluated once, when the effect starts:
duration: "40 + %player_level% * 2" # top-level duration of a scripted effect
layers:
- effect: helix
scale: "0.5 + %player_level% / 50"
parameters:
turns: "2 + %statistic_deaths% % 5"
The keys are the top-level duration of a scripted effect, a layer’s scale and a layer’s
parameters.<name>. Placeholders are read for the player who played the effect: the sender of
/ve play, the followed player, the actor of a trigger or the target of a
MythicMobs mechanic. Server-wide placeholders such as %server_online% work
without a player. Values that are not numbers count as 0, with one warning in the console.
% between numbers is still the modulo operator.
Orientation cheat sheet
Angles are in radians. Common values: 30 deg = 0.524, 45 deg = 0.785, 90 deg = 1.5708,
180 deg = 3.1416, 270 deg = 4.7124. Rotation order: roll, then pitch, then yaw -
the layer tilts in its own frame first, then turns around the world’s vertical axis.
A complete example
traveling_comet.yml (bundled) is the reference build: a sphere head and a helix trail share a
three-leg flight path through a YAML anchor, while a rosette gate ping-pongs at the spawn point.
Open it in plugins/VEngine/effects/ and read it top to bottom - it uses nearly every feature on
this page. For display entities, read crystal_ward.yml next.
Troubleshooting
| Symptom | Meaning |
|---|---|
Skipping effect '<file>': no valid layers were found |
Every layer failed to parse, look for the layer warnings just above |
Skipping layer for unknown primitive effect '<name>' |
Layer references an effect name that does not exist (typo, or the atom file failed to load) |
Skipping parametric effect '<file>': one of the x/y/z formulas is invalid |
Formula syntax error - often a missing * or unbalanced parenthesis |
| Nothing visible, no warnings | duration too short (YAML default is 60 ticks, /ve play lets you override), particle typo, or you are beyond view-distance in config.yml |
Unknown display type '<name>' |
display.type must be item, block or text, the layer falls back to particles |
| Display layer shows fewer models than points | A layer is capped at 256 display entities |
| Effect plays but does not move | Motion legs need ticks > 0, and the layer must live long enough to reach the waypoint |