Here is code to make a static picture a wavy background.
A video of what I mean:
I don’t know if there’s a better name for it, but basically, it’s an effect used in various old video games. The effect worked either scanline based if the hardware supported it (e.g. Game Boy) or simply by sliding the tile rows back and forth. It was often used in lava scenes (to show heat) or underwater scenes.
I wanted to use that effect in my game, so I came up with this code. I have a small feeling that this effect is actually really easy to code, but I was a bit proud when I did it. It may be useful to someone, so I’m sharing it.
Usage:
- Instantiate the BGWave with the file name of the background pic you want to wave (pic should be close to the same resolution as the screen)
- update() every frame.
- render() every draw.
- Remember to dispose() the background texture when you’re finished with it.
You may change the field variables to tweak the wave to your liking. Here is a picture describing what the field variables do:
The code itself:
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
public class BGWave {
//The background texture
Texture bg;
//the screen resolution. Update this every frame if you allow screen resizing
int hres = 640;
int vres = 480;
//the number of segments on the screen. The higher the number, the better quality
//the wave looks, but the more polys drawn. Max value is the vertical screen resolution.
int segments = 32;
//distance wave will travel (be pushed) left/right
float cycstrength = 16;
//number of complete wave cycles on screen
float numcycles = 2;
//speed wave will cycle
float cyclespeed = 1/numcycles;
//the current position of the wave cycle
float cycoffset = 0;
/** Creates the wave background, loading the background texture into memory */
BGWave(String filename) {
bg = new Texture(Gdx.files.internal(filename));
}
/** Update the wave by increasing the position of the wave cycle by the speed */
void update() {
//make sure you update the resolution (hres/vres) every frame
// if you expect the screen to be resized
cycoffset += cyclespeed;
}
/** Render the wavy background */
void render() {
//scale the image so that the background image touches the window with no black borders
float bgscale = hres/(float)bg.getWidth();
float temp = vres/(float)bg.getHeight();
if (temp > bgscale) bgscale = temp;
//For reference, here's how you'd draw the background normally, no wave
/*
batch.draw(bg,
GameMain.hres/2-(bg.getWidth()*bgscale)/2,
GameMain.vres/2-(bg.getHeight()*bgscale)/2,
bg.getWidth()*bgscale,
bg.getHeight()*bgscale);
*/
//size of segment on screen
float segmentSize = vres/(float)segments;
//size of segment in the picture to draw
float picSegSize = (segmentSize/bgscale);
float picYBorder = ((((bg.getHeight()*bgscale)-vres)/2)/bgscale)*1.5f;
for (int i = 0; i < segments; i++) {
batch.draw(bg,
hres/2-(bg.getWidth()*bgscale)/2 + (pos(i*segmentSize)), //x
i*segmentSize, //y
bg.getWidth()*bgscale, //width
segmentSize, //height
0, //picture x
(int) (bg.getHeight()-picYBorder-(picSegSize*i)-picSegSize), //pic y
bg.getWidth(), //pic wid
(int)picSegSize, //pic hei
false,false);
}
}
/** returns the amount the wave should be pushed left/right in the given position */
float pos(float num) {
num += cycoffset;
return (float) (Math.sin(num*(Math.PI*2/vres)*numcycles)) * cycstrength;
}
}
Enjoy! Use it as you like.
Room for improvement:
- don’t use quads for each segment, use triangles to make parallelograms (it still looks good with quads, though).