Fancy lighting for texture and effect

When I put together tiles as I started building the game, it was very exciting. Initially there were only a few (stone walls, stone floors, torches and orcs), but it was amazing to see them come together on the screen. After the initial euphoria faded, however, I was left feeling that the experience was a bit flat. I randomly rotated the tiles to create a little variety in the layouts, but the lighting left things feeling a little too uniform. The solution was to add some variation in intensity with a little flickers to simulate illumination from a torch.

How is it done? Every tile is an object with a local illumination variable which range from 0 (pitch dark) to 100 (maximum light). Every source of light, such as a torch, will then increment the values of the tiles within its range (lumensRange) by an amount relative to the inverse of the square of the distance to that tile, as stipulated by the Inverse Square Law. To make things feel a little more dynamic and “real,” I added a random amount of illumination (randomLumens) to simulate a flicker. This effect is primarily visible near the edge of the illumination radius.

for (int x = -lumensRange; x <= lumensRange; x++) {
	for (int y = -lumensRange; y <= lumensRange; y++) {
		int randomLumens = (flickerAmt != 0) ? rand.nextInt(flickerAmt) : 0;
		if (x == 0 && y == 0) lumensFlickerCache[x + lumensRange][y + lumensRange] = (lumens + randomLumens) / 4;
		else lumensFlickerCache[x + lumensRange][y + lumensRange] = (lumens + randomLumens) / 4 / ((x*x) + (y*y));
	}
}

To save compute, as well as control the frequency of the flicker, these values are not computed at every update, hence the lumensFlickerCache.

The next step is, of course, is to adjust the color effects (brightness, hue, contrast, saturation) of the tiles as a function of the level of illumination. As is frequently the case, someone on Stack Overflow put together a class with a collection of methods to accomplish all of this. To get the desired effect, I experimented with adjusting all of the effects as a function of the level of illumination, but in the end settled on brightness and contrast. As it turned out, adjusting the brightness was not sufficient to dim the tiles with low illumination.

// method from ColorFilterGenerator class
public static ColorFilter adjustColor(int brightness, int contrast, int saturation, int hue){
    cm.reset();
    adjustHue(cm, hue);
    adjustContrast(cm, contrast);
    adjustBrightness(cm, brightness);
    adjustSaturation(cm, saturation);
    return new ColorMatrixColorFilter(cm);
}

// method that adjusts the tile's color filter during rendering
public OverlayImage(Context context, Entities e) {
    super(context);
    entity = e;
    if (!entity.equals(hero)) { // normal brightness for hero
        int brightness = map.getBrightness(entity.getPos());
        p.setColorFilter(ColorFilterGenerator.adjustColor(brightness, brightness, 0, 0));
    }
}

Here is a short video from the early days of development. You can see the illumination radius and, with careful attention, should be able to see the flicker at the edges. It’s worth noting that I prioritized doing this over building the mechanics of the game, such as combat. At the end of the video you can notice that the orc does not hit back.

While this may seem rudimentary, it’s actually an aspect of the game for which I am the most proud. Not only do I love the way it looks, and the feeling it gives to the experience, but it actually took a lot of time and experimentation to get it to the point where it worked and looked right.

Remnant from the past

Back when I started this blog, I mentioned how in high school I wrote a simple 2D RPG on the Apple ][+. The story is naturally a little more complicated. At the time I only knew how to program in BASIC. This, however, was much too slow for rendering graphics. So I convinced my parents to drive me all over town looking for a bookstore that sold a book describing how to write code in Apple assembly. At the time, books like this were extremely difficult to come by.

I eventually got the book and proceeded to teach myself how to program in assembly. The Apple ][+ processor didn’t have a multiplication operation, but the book offered a handy subroutine that enabled this through a series of bit-shift operations. I also recall finding a piece of software (no idea where) that enabled writing assembly by entering the operator codes, as opposed to having to do it with hex. Lastly, I figured out how to call assembly routines from BASIC so that I could write the game logic using a simple language while delegating just the graphics operations to assembly.

Worksheet from high school

The assembly routine was past a set of coordinates and then proceeded to move the appropriate 2D images directly to the graphics memory. On the Apple ][+ there was a range of memory somewhere in the 48 Kb that was used to put pixels on the monitor. To complicate matters, the lines on the monitor were interleaved, ostensibly to make rendering more fluid. In any event, using the multiplication routine above, I figure out how to do it.

The happy news is that it worked! I even enabled a version of sprites so that water and humanoids looked like they were moving. I had mountains, trees, cities and oceans. Using the keyboard, characters were able to move up, down, left or right. Naturally, there were encounters with monsters. The battles were turn-based and took place in text. In the end, I had a simple game which vaguely resembled my beloved Ultima.

The sad news is that, with the passage of time, the magnetic materials on my cheap floppies degraded and the game was lost. To my regret, I never saved a printout of the code. That would have been cool to look at today. The only thing left is, regrettably, my memory.

With one exception! Recently my best friend from high school sent me a box with some of my D&D papers in it. I have no idea why he had this box, or why he kept it, but I was pretty delighted to see it. In there, I found a single sheet of paper from the game (inset).

As you can see, these are mountains. The 2D images where 3 bytes across (I can’t recall why they’re 7 bits, but I’m sure there was a good reason) and 34 rows, making the final image 21 by 34 pixels. My guess is that these dimensions were even multiples of the screen size. Anyway, while it’s difficult to tell, on the sheet I’m converting the pixels into hex which I then convert into decimal. I don’t recall why I wanted decimal.

Perhaps one day I’ll find a printout or more worksheets. I know that I had copious amounts of both, but they’re probably lost forever. Until then, however, I’ll have to be satisfied with this.