R
B
code

Robert Tamayo's Code Blog

2D Procedural Graphics with WebGL – Tornado Attack from My Game RoboBot

After finishing my first game Bad Blaster in 2015, I started making another one called RoboBot. I had spent a lot of time on the artwork for Bad Blaster, and so for RoboBot I wanted a simpler graphical design that was still visually impressive. I then went on a rampage and created all kinds of really cool effects using nothing but triangles, circles, and squares.

One of my favorites was a Water Spout attack.

I created it using nothing but triangles, and today I'm going to recreate it using html canvas and WebGL.

1. Learn WebGL

The game was written in Java and used the LibGDX framework. For this post, I wanted to embed the working code onto the page. This required me to learn the basics (very basics) of WebGL. The end code I used relies heavily on the MDN tutorial on WebGL. The parts where it differs, however, are where I make it do what I want.

2. Define the problem

The original problem was that I wanted to make a cool water-based attack for a game and I didn't have the resources available to animate it. The attack I settled on was a water spout, because it can attack vertically. The constraints were that I had to do the entire attack using triangles, color gradients, and color shifting. Also, performance in games is always important.

A waterspout is basically a water tornado, and I had to examine the shape and motion of a tornado. We know that tornadoes seem to be basically warped triangles, in that they have a triangle shape whose center sways at it reches the ground. For gameplay purposes, I needed the center to remain at the player's center. But I still needed the swaying motion to keep it consistent with real-world tornados.

demo

The sine and cosine functions are perfect for swaying like this. What I ended up doing was having a linear equation moving away from the center, for example f(x) = 2x. Then I modified the result by having the distance away from the center changed by a sine function. I treated x and y separately. Y was always constant; x could change. The controlling factor was the number of points, or i. I looped through an array of length 24 and used the index to process the values necessary for x and y.

I had to do this for each of the four parts of the waterspout: top left, top right, bottom left, and bottom right. Other than x, y, and the index, the other factor was the stateTime. stateTime is the only part that changes; it was run through sine function to cause the tornado to move.

    function update(time){
        // console.log(delta);
        time *= 0.002;
        stateTime = time;
        var delta = stateTime - then;
        then = stateTime;

        for (var i = 0; i < count; i++) {
            // top left
            x = ox - width - 4*i - 24 * Math.sin(stateTime - Math.PI/6*i);
            y = oy + height*i;
            topLeft[i].x = x;
            topLeft[i].y = y;

            // top right
            x = ox + width + 4*i - 24 * Math.sin(stateTime - Math.PI/6*i);
            y = oy + height*i;
            topRight[i].x = x;
            topRight[i].y = y;

            // bottom left
            x = ox - width - 4*i - 24 * Math.sin(stateTime + Math.PI/6*i);
            y = oy - height*i;
            bottomLeft[i].x = x;
            bottomLeft[i].y = y;

            // bottom right
            x = ox + width + 4*i - 24 * Math.sin(stateTime + Math.PI/6*i);
            y = oy - height*i;
            bottomRight[i].x = x;
            bottomRight[i].y = y;
        }
        updateColors(delta);
        render();
        requestAnimationFrame(update);
    }
Comments:
Leave a Comment
Submit