Rhythm Lane
của MusicBar, thanh tiến trình
Nốt nhạc trượt vào vòng ở chỗ đang phát rồi nổ lấp lánh y như game nhịp điệu, nhạc càng to thì nốt càng dày.
Cách cài, mã SHA-256 và mã nguồn
Bấm Áp dụng: MusicBar tự tải về, kiểm tra mã SHA-256 rồi mặc lên taskbar; có rồi thì mặc luôn. Cần MusicBar 1.8 trở lên.
SHA-256 9bd0eef3acd5ab83bc208fe3181c83b3781ba9fb966fd3a4cbf347ab08619cc4
//! name: Rhythm Lane
//! author: MusicBar
//! kind: progress
//! description: A rhythm-game highway runs along the bar: note gems slide into the hit ring at the playhead and burst into a perfect-hit sparkle, more of them when the music gets loud.
// A steady random number.
float hash1(float n) { return frac(sin(n * 91.345) * 43758.5453); }
// A rhombus (a cut gem seen from the front), b its half width and half height. Negative inside.
float gem(float2 p, float2 b)
{
p = abs(p);
return (p.x * b.y + p.y * b.x - b.x * b.y) / length(b);
}
// The note a lane holds in cell c: where it sits in note space, and how likely it is to be there.
float2 noteIn(float c, float seed, float cw)
{
return float2(c * cw + (0.22 + 0.56 * hash1(c * 7.7 + seed + 1.3)) * cw, hash1(c * 3.1 + seed));
}
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
{
float2 px = float2(uv.x * size.x, rise(uv) * size.y); // pixels: along the bar, up from the screen edge
float H = size.y;
float head = progress * size.x;
float v = 3.0 * H; // how fast the notes scroll, pixels per second
float cw = 1.1 * H; // each lane holds at most one note per cell
float density = 0.22 + 0.62 * level; // louder music, more notes
// Three lanes, bottom to top in the accent, second and main colors.
float gapL = 0.28 * H;
float li = clamp(floor((px.y - 0.5 * H) / gapL + 0.5), -1.0, 1.0);
float laneY = 0.5 * H + li * gapL;
float3 lc = li < -0.5 ? colorC.rgb : (li < 0.5 ? colorB.rgb : colorA.rgb);
float dyL = px.y - laneY;
float seed = li * 17.0 + 5.0;
float ahead = smoothstep(-0.5, 0.5, px.x - head);
// This pixel's note, in note space that scrolls toward the ring.
float n = px.x + time * v;
float2 nt = noteIn(floor(n / cw), seed, cw);
float nx = nt.x - time * v;
float has = smoothstep(nt.y - 0.02, nt.y + 0.02, density);
float2 gp = px - float2(nx, laneY);
// The newest hit on this lane: the notes that most recently slid past the ring.
float nR = head + time * v;
float2 h0 = noteIn(floor(nR / cw), seed, cw);
float2 h1 = noteIn(floor(nR / cw) - 1.0, seed, cw);
float2 h2 = noteIn(floor(nR / cw) - 2.0, seed, cw);
float t0 = (nR - h0.x) / v;
float ok0 = step(0.0, t0) * step(h0.y, density);
float age = h2.y < density ? (nR - h2.x) / v : 9.0;
age = h1.y < density ? (nR - h1.x) / v : age;
age = ok0 > 0.5 ? t0 : age;
float burst = pow(saturate(1.0 - age / 0.6), 1.5);
float3 acc = float3(0, 0, 0);
float cover = 0;
// The highway ahead: dashed lane guides and measure lines, scrolling in with the notes.
float dash = step(frac(n / (0.32 * H)), 0.45) * smoothstep(1.0, 0.2, abs(dyL));
float dm = abs(n - floor(n / (4.0 * cw) + 0.5) * 4.0 * cw);
float measure = smoothstep(1.0, 0.2, dm) * step(abs(px.y - 0.5 * H), 0.4 * H);
layer(acc, cover, lerp(float3(0.6, 0.62, 0.7), lc, 0.5), ahead * (dash * 0.3 + measure * (0.14 + 0.2 * beat)));
// The played part: a glowing combo rail per lane, with the hit notes drifting off it as afterimages.
float rail = exp(-abs(dyL) / (0.05 * H)) * 0.5 + smoothstep(1.1, 0.3, abs(dyL)) * 0.55;
float fresh = exp(-max(head - px.x, 0.0) / (9.0 * H));
layer(acc, cover, lc, (1.0 - ahead) * rail * (0.45 + 0.35 * fresh + 0.2 * level));
float ghostAge = (head - nx) / v;
float ghost = has * step(nx, head) * exp(-ghostAge / 1.6);
float dGhost = gem(gp, float2(0.11 * H, 0.075 * H));
float ghostHalo = pow(saturate(1.0 - max(dGhost, 0.0) / (0.08 * H)), 2.0) * smoothstep(0.135 * H, 0.1 * H, abs(dyL));
layer(acc, cover, lerp(lc, float3(1, 1, 1), 0.35), ghost * (ghostHalo * 0.45 + smoothstep(0.5, -0.5, dGhost) * 0.6));
// The judgment bar through the lanes, then the hit rings, which pulse on the beat and flash on a hit.
float dBar = abs(px.x - head);
float span = smoothstep(gapL + 0.14 * H, gapL, abs(px.y - 0.5 * H));
layer(acc, cover, lerp(colorB.rgb, float3(1, 1, 1), 0.5), exp(-dBar / (0.12 * H)) * span * (0.14 + 0.12 * beat));
layer(acc, cover, float3(1, 1, 1), (exp(-dBar / (0.04 * H + 0.8)) * 0.35 + smoothstep(0.9, 0.3, dBar) * 0.5) * step(abs(px.y - 0.5 * H), gapL));
float2 bp = px - float2(head, laneY);
float rr = 0.1 * H * (1.0 + 0.1 * beat + 0.12 * burst);
float dRing = abs(length(bp) - rr) - 0.02 * H;
float inLane = smoothstep(0.14 * H, 0.12 * H, abs(dyL));
layer(acc, cover, lc * 0.6, smoothstep(rr, rr - 1.0, length(bp)) * (0.25 + 0.35 * burst));
layer(acc, cover, lc, exp(-max(dRing, 0.0) / (0.04 * H)) * (0.35 + 0.4 * beat) * inLane);
layer(acc, cover, lerp(lc, float3(1, 1, 1), 0.45 + 0.5 * burst), smoothstep(0.5, -0.5, dRing));
// The incoming gems: cut facets lit from above, a white rim and glint, glowing as they near the ring.
// The glow fades out inside the gem's own cell and lane, so it never shows a seam.
float dG = gem(gp, float2(0.15 * H, 0.1 * H));
float near = exp(-max(nx - head, 0.0) / (3.0 * H));
float facet = gp.y > 0.0 ? 1.15 - 0.2 * step(0.0, gp.x) : 0.62 + 0.15 * step(gp.x, 0.0);
float table = smoothstep(0.5, -0.5, gem(gp, float2(0.065 * H, 0.043 * H)));
float3 gemColor = lc * lerp(facet, 1.35, table);
gemColor = lerp(gemColor, float3(1, 1, 1), smoothstep(-0.03 * H, 0.0, dG) * 0.6);
gemColor = lerp(gemColor, float3(1, 1, 1), exp(-length(gp - float2(-0.04 * H, 0.03 * H)) / (0.016 * H + 0.4)));
float show = has * step(head, nx);
float halo = pow(saturate(1.0 - max(dG, 0.0) / (0.075 * H)), 2.0) * smoothstep(0.135 * H, 0.1 * H, abs(dyL));
layer(acc, cover, lc, show * halo * (0.3 + 0.5 * near));
layer(acc, cover, gemColor, show * smoothstep(0.5, -0.5, dG));
// The perfect-hit sparkle: a white flash, a shock ring spreading along the lane, a four-point star
// and three sparks thrown out along the lane.
float shock = exp(-abs(abs(bp.x) - (0.15 * H + age * 2.2 * H)) / (0.025 * H + 0.5)) * smoothstep(0.11 * H, 0.02 * H, abs(bp.y)) * burst;
float rays = exp(-abs(bp.y) / (0.012 * H + 0.4)) * exp(-abs(bp.x) / (0.2 * H + 1.2 * H * burst))
+ exp(-abs(bp.x) / (0.012 * H + 0.4)) * exp(-abs(bp.y) / (0.09 * H)) * inLane;
float flash = exp(-length(bp) / (0.04 * H + 0.08 * H * burst)) * smoothstep(0.14 * H, 0.05 * H, abs(dyL));
float2 s0 = bp - age * float2(-2.6 * H, 0.18 * H);
float2 s1 = bp - age * float2(1.9 * H, -0.15 * H);
float2 s2 = bp - age * float2(-1.3 * H, -0.2 * H);
float sparks = exp(-length(s0) / (0.02 * H + 0.5)) + exp(-length(s1) / (0.02 * H + 0.5)) + exp(-length(s2) / (0.02 * H + 0.5));
layer(acc, cover, lerp(lc, float3(1, 1, 1), 0.4), shock * 0.85);
layer(acc, cover, lerp(lc, float3(1, 1, 1), 0.55), saturate(sparks * 1.6) * burst * step(0.02, age));
layer(acc, cover, lerp(lc, float3(1, 1, 1), 0.8), saturate(rays * burst * 1.6 + flash * burst * 1.3));
return paint(acc / max(cover, 0.001), cover);
}