Sundays are for rest, so this day of work has been an easy one. Some code refactors, like changing the default blending mode, but most importantly I added an important feature I wanted to have in the game.
It is expected, that when a group of bubbles pop, depending on their number, some “soul lines” would be added. These are the white lines in the gif.

These lines will fed up a power-up. Pop bubbles are the energy necessary to power one special effect that would randomly pop a predefined amount of bubbles. These lines are governed by an specific shader. They can have different color, size and intensity, and travel by a random cubic bezier curve. The curve describes the whole traverse path, and the segment is calculated by making use of a fancy easing scheme.
...
this.easingStart = EasingEaseIn({exponent: 1.5});
this.easingEnd = EasingEaseOut({exponent: 1.5});
...
// nt stands for normalized time.
// If the soul animation takes 2000 ms, nt=.5 means we
// are at half the animation time.
// Since I use a bezier curve, this does not correlate
// to be the half point in the curve.
// t0 and t1 will be two solved values over the bezier curve
let t0 = this.easingStart(nt);
const t1 = this.easingEnd(nt);
...
// calculate a cubic segment ranging from t0..t1 on
// the bezier curve
for(let i = 0; i<Segments; i++) {
bezier.getValueAt(t0, p0);
bezier.getValueAt(t0+.001, p1);
// normal vector as point p0 in the curve
let nx = p1.y - p0.y;
let ny = p1.x - p0.x;
// normalized vector
const l = Math.sqrt(nx*nx + ny*ny);
nx/=l;
ny/=l;
...
// generate shader points:
// p0 + nx * lineWidth
// p0 - nx * lineWidth
let yy0 = p0.y + p0y;
let yy1 = p0.y - p0y;
// avoid twisted lines
if (yy0>yy1) {
[yy0, yy1] = [yy1, yy0];
}
this.lineData[ptr++] = p0.x + p0x;
this.lineData[ptr++] = yy0;
this.lineData[ptr++] = tint;
this.lineData[ptr++] = 1;
this.lineData[ptr++] = p0.x - p0x;
this.lineData[ptr++] = yy1;
this.lineData[ptr++] = tint;
this.lineData[ptr++] = -1;
...
}
The shader fragment is as simple as
vec4 f = vec4(uIntensity*smoothstep( .9, .0, abs(vY))); // vY -1 or 1
gl_FragColor = f*uColor*tint;
This line shader does not use tessellation. It does not benefit from any charming line join/cap process either, but i just want it to be a simple flowing line. It is current state, it does not batch lines. I should duplicate some shader data for that, which I will definitely do if I have the time or should I need to start optimising code.
In day 3 I will start coding in-game model + logic. Instead of an animation, I’d start to resemble more a whole game.
If you think you could contribute some graphics, just lemme know 😀
Comments are also welcome.