#125: slow the prologue down, and give the breach a real black hole
Five things, all of them "this happens too fast or looks too thin". PACING. Every beat had a completion test that could be satisfied while the guide was still mid-sentence, which is why everything landed seconds after the thing before it. Beats now have a FLOOR as well as a deadline - a minimum length - and a beat whose point is the guide talking will not end while a line is still on screen. One rule in one place rather than tuning each condition. The run is 3400px instead of 1500, which at cruise was about four seconds, and a held line is 6.5s instead of 4.5. THE CONVOY. Escorts at the four corners of a box, holds packed in the middle of it, roughly three times the spread of the fleet's own wedge - this is freight under guard, not a fighting formation. CONVOY and CONVOY_FORMATION pair index-for-index, which is all it takes for spawn_ships to seat the guns on the outside, and the test pins that every hold sits further in than the furthest-out escort. THE DIALOG. Cut corners, brackets holding the short edges open, a tick strip along the bottom and a caret in the corner - geometry rather than art, so it scales with the banner and costs nothing. THE BREACH. It was a few lines turning around a point. It is now two halves driven by one number: DimensionalBreach, a growing black hole out in the world built on BlackHoleVisual's idiom - empty core, counter-rotating arc segments hotter toward the centre - with infalling streamers it does not have, because a planet's disc is steady and this one has to say "now"; and breach.gdshader over the top, bending the ACTUAL SCENE around it. Ships, rocks and the starfield all get dragged in, which is most of what makes a hole in space read as a hole rather than as a decal drawn on one. Inside the horizon the shader samples nothing at all - the same decision BlackHoleVisual makes, for the same reason: a black hole with something visible in the middle stops being one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7d344b2a0a
commit
d455361328
8 changed files with 527 additions and 57 deletions
72
shaders/breach.gdshader
Normal file
72
shaders/breach.gdshader
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
// #125: the dimensional breach, as a screen-space lens.
|
||||
//
|
||||
// The prologue's warp used to be a handful of lines turning around a point.
|
||||
// This bends the ACTUAL SCENE around the breach instead - the ships, the
|
||||
// rocks and the starfield all get dragged into it - which is most of what
|
||||
// makes a hole in space read as a hole rather than as a decal drawn on top
|
||||
// of one.
|
||||
//
|
||||
// Two effects, both radial and both fading to nothing at the edge of their
|
||||
// reach, so the shader is invisible outside its own radius and costs a
|
||||
// clamped texture fetch everywhere else:
|
||||
//
|
||||
// swirl angle offset that grows toward the centre, so the scene winds
|
||||
// around the hole rather than merely sliding toward it
|
||||
// pull inward displacement, strongest just outside the horizon, which
|
||||
// is the lensing itself
|
||||
//
|
||||
// Inside the horizon nothing is sampled at all: the core is drawn as empty,
|
||||
// the same decision BlackHoleVisual makes for the black-hole planet, and
|
||||
// for the same reason - a "black hole" with something visible in the middle
|
||||
// stops reading as one.
|
||||
|
||||
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_linear;
|
||||
|
||||
// Where the breach is, in SCREEN pixels, and how big it has grown.
|
||||
uniform vec2 breach_center = vec2(360.0, 640.0);
|
||||
uniform vec2 viewport_size = vec2(720.0, 1280.0);
|
||||
// The event horizon: everything inside is void.
|
||||
uniform float horizon_radius = 0.0;
|
||||
// How far the lens reaches past the horizon. Beyond this the scene is
|
||||
// untouched.
|
||||
uniform float lens_radius = 1.0;
|
||||
// 0 while the breach is opening, 1 at full strength. Scales both effects
|
||||
// together so one knob fades the whole thing in.
|
||||
uniform float strength = 0.0;
|
||||
uniform float swirl_turns = 2.4;
|
||||
uniform vec4 rim_color : source_color = vec4(0.72, 0.45, 1.0, 1.0);
|
||||
|
||||
void fragment() {
|
||||
vec2 pixel = UV * viewport_size;
|
||||
vec2 offset = pixel - breach_center;
|
||||
float distance_to_centre = length(offset);
|
||||
|
||||
if (distance_to_centre >= lens_radius || strength <= 0.001) {
|
||||
COLOR = texture(screen_texture, SCREEN_UV);
|
||||
} else if (distance_to_centre <= horizon_radius) {
|
||||
// The core. Not black-with-alpha: genuinely nothing, so whatever is
|
||||
// behind the breach is gone rather than dimmed.
|
||||
COLOR = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
} else {
|
||||
// 1 at the horizon, 0 at the edge of the lens. Squared so the bend
|
||||
// is concentrated near the hole instead of smeared evenly outward.
|
||||
float falloff = 1.0 - clamp(
|
||||
(distance_to_centre - horizon_radius) / max(lens_radius - horizon_radius, 1.0),
|
||||
0.0, 1.0);
|
||||
float bend = falloff * falloff * strength;
|
||||
|
||||
float angle = atan(offset.y, offset.x) + bend * swirl_turns;
|
||||
float pulled = distance_to_centre - bend * (lens_radius - horizon_radius) * 0.55;
|
||||
pulled = max(pulled, horizon_radius);
|
||||
vec2 sampled = breach_center + vec2(cos(angle), sin(angle)) * pulled;
|
||||
|
||||
COLOR = texture(screen_texture, sampled / viewport_size);
|
||||
|
||||
// A hot rim where the lensing is fiercest, which is what gives the
|
||||
// horizon an edge rather than a fade.
|
||||
float rim = smoothstep(0.72, 1.0, falloff) * strength;
|
||||
COLOR.rgb = mix(COLOR.rgb, rim_color.rgb, rim * 0.65);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue