Documentation
¶
Overview ¶
Package effects is Linefire's particle system: one pool that drives every cosmetic spray in the game — explosions, sparks, exhaust, debris — and the presets that describe them.
It is deliberately free of any game state. A pool is handed its own randomness and is drawn through an explicit camera and scale, so the same explosion plays in the game, in the editors' previews, and in Linefire Skirmish, rather than each of them growing its own.
Everything here is LIGHT: particles are drawn additively, and a particle fades by dropping its additive gain, never by lowering its color's alpha. Additive blending ignores what lies under it, so a lower alpha alone would leave a dying spark exactly as bright as a fresh one.
Index ¶
Constants ¶
const ( // DefaultMax caps a pool: a hard backstop so a pile-up of explosions can never // grow the slice without bound. Particles are purely cosmetic, so emits past // the cap are dropped silently and the oldest keep aging out. DefaultMax = 1500 )
const DefaultMoteSize = 1.6
DefaultMoteSize is the radius of a portal mote in world units. It is exported so a caller asking for a different one can say what it is relative to.
Variables ¶
var ( SparkColor = color.RGBA{0xff, 0xc8, 0x60, 0xff} // warm explosion core DebrisColor = color.RGBA{0xff, 0x90, 0x40, 0xff} // hotter ember chunks ThrusterColor = color.RGBA{0x90, 0xe0, 0xff, 0xff} // pale blue engine exhaust )
Colors shared by the presets below.
var ( ExplosionStreaks = Burst{ N: 14, Col: SparkColor, Style: StyleStreak, SpeedMin: 2.0, SpeedMax: 6.0, LifeMin: 18, LifeMax: 34, Drag: 0.90, } ExplosionChunks = Burst{ N: 6, Col: DebrisColor, Style: StyleDot, SpeedMin: 1.0, SpeedMax: 3.5, LifeMin: 26, LifeMax: 46, Drag: 0.93, Size: 1.6, } )
ExplosionStreaks and ExplosionChunks together are a ship dying: warm streaks flung out fast, plus a few slower glowing chunks that linger. Play both with Pool.Explosion.
Functions ¶
func DrawPortal ¶ added in v0.0.7
DrawPortal renders a portal as a tunnel: a thin outer ring, and motes born ON that ring travelling STRAIGHT to the centre, where they die. The radial convergence — no spiral, no inner rings — is what reads as looking down a tunnel. Each mote takes a fresh random spoke every cycle, so the stream scatters instead of tracing a few thick arms.
Nothing is stored: the whole vortex is a function of Seconds, so a caller can draw one wherever it likes without owning any state.
func SpokeAngle ¶ added in v0.0.7
SpokeAngle is a stable pseudo-random spoke for mote i on cycle c: constant while the mote falls inward, fresh when it respawns, so the stream never repeats a fixed arm.
Types ¶
type Burst ¶
type Burst struct {
N int
Col color.RGBA
Style Style
SpeedMin, SpeedMax float64
LifeMin, LifeMax int
Drag float64
Size float64 // dot radius (StyleDot only)
}
Burst is a named emitter preset: a radial spray of particles sharing a look and a speed/lifetime range. A new effect should be a new preset, not new code.
type Particle ¶
type Particle struct {
X, Y float64
PX, PY float64 // previous point, for the streak style
VX, VY float64
Drag float64
Life int
MaxLife int
Size float64 // dot radius in world units (StyleDot only)
Col color.RGBA
Style Style
}
Particle is a short-lived element of an effect. It carries its own motion (velocity plus drag) and look (style, size, color) so one pool drives everything. Emit fills PX/PY; the rest is the caller's.
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool holds the live particles.
func New ¶
New returns an empty pool holding at most max particles and drawing its randomness from random, which must return values in [0, 1).
The caller supplies the source so the effects inherit whatever determinism it has: the game uses its global one, a seeded simulation its own.
func (*Pool) Particles ¶
Particles exposes the live pool for inspection. The slice is the pool's own: read it, do not keep it past the next Step.
func (*Pool) Step ¶
func (p *Pool) Step()
Step advances and ages every particle, dropping the dead ones. Pure motion, no rendering, so it is cheap and testable on its own.
func (*Pool) Thruster ¶ added in v0.0.7
Thruster trails exhaust from (x, y) — the ship's rear — for one frame of acceleration along the forward unit vector (fx, fy). Call it once per thrusting frame; the counts are small because of that, and the pool cap is the backstop.
The plume is a NARROW fan of streaks thrown fast out the tail. Fat discs were the wrong shape entirely: they smear a blob over the hull instead of a jet.
type Portal ¶ added in v0.0.7
type Portal struct {
X, Y float64 // centre, screen pixels
Radius float64 // rim, screen pixels
Col color.RGBA
// Seconds is the animation clock. It only has to advance smoothly: the motes
// are a function of it, so nothing is stored between frames.
Seconds float64
// Fade scales the whole thing out, 1 for a portal at full strength. A
// permanent portal leaves it at 1; a transient one — a ship arriving — rides
// it down as the vortex closes.
Fade float64
// MoteSize is the radius of one mote in world units. Zero uses DefaultMoteSize.
MoteSize float64
// Speed is how fast a mote travels rim -> centre, in cycles per second. Zero
// uses the package default. A portal that stands in the world can let them
// drift; a brief one has to pull them all the way in before it closes, or the
// convergence never arrives anywhere.
Speed float64
// HideRing drops the rim the motes are born on, leaving only the convergence.
// A portal that stands in the world wants the ring — it is a place you can fly
// into. One that exists for a moment to deliver a ship does not: without the
// rim the motes read as something materialising rather than a hole opening.
HideRing bool
DPR float64 // device pixels per logical px, for the ring stroke
Scale float64 // screen pixels per world unit, for the mote size
}
Portal is one frame of a vortex, in screen space.
type View ¶
type View struct {
Cam ebiten.GeoM // world -> screen
// DPR is device pixels per logical pixel, applied to stroke widths so a
// streak is the same apparent thickness on any display.
DPR float64
// PixelScale is device pixels per world unit, applied to dot radii so a chunk
// is the same size in the world however the camera is zoomed.
PixelScale float64
// Glow widens strokes and discs for the emissive pass that feeds the bloom.
Glow bool
}
View is how a pool is placed on screen: the camera transform plus the two scales the drawing needs.