Let's do a classic Amiga effect.
Take a copy of the previous chapter, resulting in ch11.
Now, I wasn't there, nor do I know the exact history, but this effect went something like this. The Amiga hardware, like its contemporary PC hardware, was relatively low-powered and just going ahead and drawing the whole screen every frame was something you didn't do.
But you could trick the hardware in various ways. In the case of this effect, you could reset the address the screen was read on every scanline, creating a single-line framebuffer. Anything on it would be copied down the screen. While the display was updated you had a little bit of time to update said framebuffer, and every change you did would then get copied downwards.
Let's start with this render()
function:
void render(Uint64 aTicks)
{
for (int i = 0; i < WINDOW_WIDTH * WINDOW_HEIGHT; i++)
gFrameBuffer[i] = 0xff000000;
int* prev_line = gFrameBuffer;
for (int i = 0; i < WINDOW_HEIGHT; i++)
{
int* curr_line = gFrameBuffer + i * WINDOW_WIDTH;
memcpy(curr_line, prev_line, WINDOW_WIDTH * sizeof(int));
curr_line[i] = 0xff0000ff;
prev_line = curr_line;
}
}
Compile and run.
Here we first clear the screen to black, and then, line by line, first take a copy of the previous line, and then alter the current line a bit. In our case, we plot a single pixel every time.
The way the prev_line
an curr_line
work is that in the first iteration, we just copy the first line over itself, and after that on each iteration we copy the previous one.
Even though we only plot a single pixel each line, the result looks like this:
How do we leverage this to make it look nicer? By drawing a few pixels we can create columns.
Let's first replace the single pixel change with this:
for (int j = 0; j < 16; j++)
{
curr_line[(j) + WINDOW_WIDTH / 2] = j * 15 | 0xff7f0000;
curr_line[(j + 16) + WINDOW_WIDTH / 2] = (16-j) * 15 | 0xff7f0000;
}
Compile and run. Okay, that's one column. Next we'll need to adjust the horizontal offset on every line to make use of it. Replace the code you just pasted with this:
int xoffset = WINDOW_WIDTH / 2 + sin((i + aTicks * 0.01) * 0.03) * (WINDOW_WIDTH / 2 - 32);
for (int j = 0; j < 16; j++)
{
curr_line[(j) + xoffset] = j * 15 | 0xff7f0000;
curr_line[(j + 16) + xoffset] = (16-j) * 15 | 0xff7f0000;
}
Compile and run. That's more like it. All we have left to do is to complicate the xoffset curve through a few more sine curves:
int xoffset = sin((i + aTicks * 0.01) * 0.03) *
sin((i + aTicks * 0.02) * 0.04) *
sin((i + aTicks * 0.03) * 0.05) *
(WINDOW_WIDTH / 2 - 32) +
WINDOW_WIDTH / 2;
for (int j = 0; j < 16; j++)
{
curr_line[(j) + xoffset] = j * 15 | 0xff7f0000;
curr_line[(j + 16) + xoffset] = (16-j) * 15 | 0xff7f0000;
}
Compile and run. We're getting somewhere! And finally, to draw a few different colored columns:
for (int k = 0; k < 3; k++)
{
int xoffset = sin((i + aTicks * 0.01 * k) * 0.03) *
sin((i + aTicks * 0.005 + k * 100) * 0.04) *
sin((i + aTicks * 0.0025) * 0.05) *
(WINDOW_WIDTH / 3 - 32) +
k * WINDOW_WIDTH / 6 +
WINDOW_WIDTH / 3;
#define ROT(a,b) ((((a) << (b)) | ((a) >> (24 - (b)))) & 0xffffff)
for (int j = 0; j < 16; j++)
{
curr_line[(j) + xoffset] = ROT(j * 15 | 0x3f0000, k*8) | 0xff000000;
curr_line[(j + 16) + xoffset] = ROT((16-j) * 15 | 0x3f0000, k*8) | 0xff000000;
}
}
Compile and run. The various values and multiplications are there as a result of a few minutes of experimentation, and don't have any special significance. Feel free to tweak the values and see the result.
The only interesting bit here is the ROT macro. It rotates the bottom 24 bits of a 32 bit value by given number of bits. In this case we rotate BGR to GRB to RBG for our three wiggly walls.
And that's it for this bit.
Next up: 12 - Infinite Sprites
Any comments etc. can be emailed to me.