Pixel Dash

by MusicBar, progress bar

A little hooded pixel runner with a long scarf speedruns to the playhead, building the level under its feet and hopping on every beat.

15 downloads Download file, 7.6 KB
How it installs, its SHA-256 and its code

Click Apply: MusicBar downloads it, checks its SHA-256 and puts it on the taskbar, or just puts it on if it already has it. Needs MusicBar 1.8 or newer.

SHA-256 f793554eada912cec6e8352ca794a2d59774cb33344a5c2b1e547fcedb0fa6fc

//! name: Pixel Dash
//! author: MusicBar
//! kind: progress
//! description: A little hooded pixel runner with a long scarf speedruns to the playhead, building the level under its feet and hopping on every beat.

float hash1(float n) { return frac(sin(n * 91.7 + 13.1) * 43758.5453); }

// One 2-bit pixel (0 empty, 1 outline, 2 cloth, 3 shade) from a sprite row.
float pix(float rowBits, float x) { return floor(frac(rowBits / exp2(2.0 * x + 2.0)) * 4.0); }

void layer(inout float3 acc, inout float cover, float3 rgb, float a)
{
    a = saturate(a);
    acc = rgb * a + acc * (1.0 - a);
    cover = a + cover * (1.0 - a);
}

float4 main(float2 uv : TEXCOORD) : COLOR
{
    // Chunky pixels: c counts them along the bar and up from the screen edge (16 rows at any bar height).
    float p = max(1.0, floor(size.y / 16.0));
    float2 c = floor(float2(uv.x * size.x, rise(uv) * size.y) / p);
    float head = floor(progress * size.x / p);
    float scroll = floor(time * 16.0);                 // the world runs left under the hero
    float wx = c.x + scroll;
    float built = step(c.x, head + 3.0);
    float3 acc = float3(0, 0, 0);
    float cover = 0;

    // Far away (played part only): slow clouds, then hills that bounce with the music.
    float cx = c.x + floor(time * 2.5);
    float ccell = floor(cx / 30.0);
    float2 cl = float2(cx - ccell * 30.0 - 10.0, c.y - 11.0 - floor(hash1(ccell) * 3.0));
    float cloud = step(abs(cl.x), 4.0) * step(abs(cl.y), 0.0) + step(abs(cl.x + 1.0), 2.0) * step(abs(cl.y - 1.0), 0.0);
    layer(acc, cover, float3(0.85, 0.9, 1.0), cloud * step(0.3, hash1(ccell + 4.0)) * built * 0.28);
    float hx = c.x + floor(time * 6.0);
    float hill = 4.0 + floor(1.6 * sin(hx * 0.08) + 1.2 * sin(hx * 0.19 + 1.3) + 2.6 * spectrum(frac(hx / 40.0) * 0.6) + 0.5);
    float inHill = step(c.y, hill) * step(2.0, c.y);
    layer(acc, cover, lerp(colorB.rgb * 0.5, lerp(colorB.rgb, float3(1, 1, 1), 0.2), step(hill, c.y)), inHill * built * lerp(0.2, 0.42, step(hill, c.y)));

    // The ground: grass on top and a row of dirt tiles where the level is built, a dotted outline ahead.
    float tuft = step(0.72, hash1(wx)) * step(abs(c.y - 2.0), 0.0);
    float3 grass = lerp(colorB.rgb, lerp(colorB.rgb, float3(1, 1, 0.8), 0.45), step(0.8, hash1(wx + 7.0)));
    float seam = step(frac(wx / 4.0), 0.1);
    float3 dirt = float3(0.45, 0.28, 0.18) * (1.0 - 0.4 * seam) * (0.9 + 0.25 * step(0.85, hash1(wx + 3.0)));
    layer(acc, cover, grass, built * (step(abs(c.y - 1.0), 0.0) + tuft * 0.8));
    layer(acc, cover, dirt, built * step(c.y, 0.0));
    float dots = step(frac(wx / 2.0), 0.1) * step(abs(c.y - 1.0), 0.0) + step(frac(wx / 4.0), 0.1) * step(c.y, 0.0) * 0.5;
    layer(acc, cover, colorB.rgb, (1.0 - built) * dots * (0.35 + 0.3 * level));
    float spark = step(abs(c.x - head - 4.0), 0.0) * step(abs(c.y - 1.0), 0.0) * step(0.5, frac(time * 6.0));
    layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.5), spark);

    // Obstacles every 40 world columns, a crate or a pair of spikes; the hero jumps each one.
    float n = floor(wx / 40.0);
    float ox = wx - n * 40.0 - 20.0;
    float crate = step(hash1(n + 2.0), 0.5);
    float2 cr = float2(ox + 2.0, c.y - 2.0);
    float inCrate = crate * step(0.0, cr.x) * step(cr.x, 3.0) * step(0.0, cr.y) * step(cr.y, 2.0);
    float edge = 1.0 - step(abs(cr.x - 1.5), 1.0) * step(abs(cr.y - 1.0), 0.5);
    float3 wood = lerp(float3(0.98, 0.66, 0.3), float3(0.4, 0.2, 0.08), edge);
    wood = lerp(wood, float3(1.0, 0.86, 0.55), step(abs(cr.y - 2.0), 0.0) * step(abs(cr.x - 1.5), 1.0));   // lit top edge
    float sx = ox + 3.0;
    float sk = sx - floor(sx / 3.0) * 3.0;
    float inSpike = (1.0 - crate) * step(0.0, sx) * step(sx, 5.0) * (step(abs(c.y - 2.0), 0.0) + step(abs(c.y - 3.0), 0.0) * step(abs(sk - 1.0), 0.0));
    float3 steel = lerp(float3(0.95, 0.97, 1.0), float3(0.55, 0.6, 0.75), step(1.5, sk) * step(c.y, 2.0));
    float obsA = lerp(0.7, 1.0, built);
    layer(acc, cover, wood, inCrate * obsA);
    layer(acc, cover, steel, inSpike * obsA);

    // Coins float ahead in an arc over each obstacle, spinning (their width squashes) and glinting on the beat.
    float j = floor((ox + 15.0) / 6.0);
    float jc = j * 6.0 - 12.0;
    float spin = abs(cos(time * 5.0 + (n * 5.0 + j) * 0.9));
    float2 cd = float2(ox - jc, c.y - (7.0 + floor(3.0 * (1.0 - jc * jc / 300.0) + sin(time * 2.0 + j) + 0.5)));
    float cw = floor(2.0 * spin + 0.5);
    float rowW = cw - step(1.5, abs(cd.y)) * step(0.5, cw);   // round: the top and bottom rows are narrower
    float coin = step(abs(cd.x), rowW) * step(abs(cd.y), 2.0) * step(head + 4.0, c.x) * step(0.0, j) * step(j, 4.0);
    float rim = max(step(rowW - 0.5, abs(cd.x)), step(1.5, abs(cd.y)));
    float3 coinCol = lerp(colorC.rgb, colorC.rgb * 0.6, rim * step(0.5, cw));
    coinCol = lerp(coinCol, float3(1, 1, 1), max(step(abs(cd.x + 1.0), 0.0) * step(abs(cd.y - 1.0), 0.0), beat * 0.6));
    layer(acc, cover, coinCol, coin);

    // The hero's jump: up over every obstacle it meets, plus a little hop on the beat.
    float hw = head - 2.0 + scroll;
    float jx = (hw - 20.0 - floor((hw - 20.0) / 40.0 + 0.5) * 40.0) / 9.0;
    float jump = floor(3.0 * min(1.0, 1.5 * saturate(1.0 - jx * jx)) + 0.5);
    float hop = floor(2.0 * 4.0 * beat * (1.0 - beat) + 0.5);
    float base = 2.0 + max(jump, hop);
    float air = step(0.5, base - 2.0);
    float hx0 = head - 8.0;

    // Dust kicked up behind the feet while running.
    float dt = frac(time * 6.4);
    float2 dust = float2(hx0 + 2.0 - floor(dt * 4.0), 2.0 + floor(dt * 2.0));
    layer(acc, cover, float3(0.85, 0.82, 0.75), (1.0 - air) * (1.0 - dt) * 0.7 * step(abs(c.x - dust.x), 0.0) * step(abs(c.y - dust.y), 0.0));

    // The scarf: from the neck it streams back and flutters, longer when the song is loud.
    float neck = base + 5.0;
    float d = hx0 + 4.0 - c.x;
    float len = 7.0 + floor(5.0 * level);
    float sy = neck + floor(1.3 * sin(d * 0.55 - time * 13.0) * min(d / 4.0, 1.0) + d * 0.12 + 0.5);
    float tail = step(len - 1.5, d);
    float inScarf = step(0.0, d) * step(d, len) * step(sy - 1.0 + tail, c.y) * step(c.y, sy);
    float3 scarf = lerp(colorA.rgb * 0.6, colorA.rgb, step(sy, c.y));
    layer(acc, cover, scarf, inScarf);

    // The hero sprite: a 12 x 12 hooded runner with three running frames and a tucked jump pose.
    float hxl = c.x - hx0;
    float r = base + 11.0 - c.y;
    float bodyRow = r < 0.5 ? 87040.0 : r < 1.5 ? 1485120.0 : r < 2.5 ? 6990480.0 : r < 3.5 ? 6642340.0 : r < 4.5 ? 6641321.0 : r < 5.5 ? 1398676.0 : r < 6.5 ? 87872.0 : r < 7.5 ? 2009664.0 : 2075280.0;
    float f = floor(frac(time * 3.2) * 4.0);
    float frame = air > 0.5 ? 3.0 : (f > 2.5 ? 1.0 : f);
    float legA = r < 9.5 ? 1721808.0 : r < 10.5 ? 6881396.0 : 5505045.0;
    float legB = r < 9.5 ? 129280.0 : r < 10.5 ? 30976.0 : 87296.0;
    float legC = r < 9.5 ? 2049424.0 : r < 10.5 ? 8192100.0 : 5505045.0;
    float legD = r < 9.5 ? 129600.0 : r < 10.5 ? 87296.0 : 0.0;
    float rowBits = r < 8.5 ? bodyRow : (frame < 0.5 ? legA : (frame < 1.5 ? legB : (frame < 2.5 ? legC : legD)));
    float inside = step(0.0, hxl) * step(hxl, 11.0) * step(0.0, r) * step(r, 11.0);
    float k = pix(rowBits, clamp(hxl, 0.0, 11.0)) * inside;
    float3 heroCol = k < 1.5 ? float3(0.08, 0.07, 0.15) : (k < 2.5 ? float3(0.97, 0.95, 0.9) : float3(0.62, 0.64, 0.8));
    heroCol = lerp(heroCol, colorA.rgb, step(abs(r - 6.0), 0.0) * step(3.0, hxl) * step(hxl, 6.0));
    float eye = step(abs(r - 4.0), 0.0) * (step(abs(hxl - 7.0), 0.0) + step(abs(hxl - 9.0), 0.0)) * step(0.05, frac(time / 2.7));
    heroCol = lerp(heroCol, lerp(colorC.rgb, float3(1, 1, 1), 0.55), eye);
    layer(acc, cover, heroCol, step(0.5, k));

    return paint(acc / max(cover, 0.001), cover);
}