in development

the good, the bad, and the tragic

I’ve spent the last few days reworking how I generate caves. That canvas-based map object I mentioned in the last entry really helped me focus on the algorithms, rather than the mechanics of getting data into the map. I’m able to create distinct structures by drunk-walking around a set of randomly-chosen points.

I create each path by making incremental steps and doing a fillRect at each point to plot a small square with a miniscule alpha.

// closure function for drawing a meandering path
function drawPath(x, y, tx, ty) {
	var dx, dy, d;
	do {

		dx = rng.get() - rng.get();
		dy = rng.get() - rng.get();
		d = Math.sqrt(dx * dx + dy * dy);
		x += 0.5 * dx / d;
		y += 0.5 * dy / d;

		dx = tx - x;
		dy = ty - y;
		d = Math.sqrt(dx * dx + dy * dy);
		x += 0.05 * dx / d;
		y += 0.05 * dy / d;

		x = SOAR.clamp(x, 0, l);
		y = SOAR.clamp(y, 0, l);

		map.context.fillRect(x - 2, y - 2, 4, 4);
	} while (Math.abs(dx) > 1 || Math.abs(dy) > 1);
}

...

// generate paths
map.context.fillStyle = "rgba(255, 0, 0, 0.05)";
for	(i = 1, il = this.area.length; i < il; i++) {
	drawPath(this.area[i - 1].x, this.area[i - 1].y,
		this.area[i].x, this.area[i].y);
}

Premultiplied alpha insures that I’ll get a nice cumulative effect as the rectangles overlap each other. Instead of the sheer walls I often get with noise algorithms, I can get tunnels, and other natural-looking structures.

The canvas performs alpha blending and color accumulation much faster than if I tried to do it in raw Javascript. It’s a win-win, except when something like this happens.

Oh, dear. Good luck getting through that.

I’m still trying to figure out how to prevent this. A rare occurance, but it only has to happen once to bring a game to a halt. If I make the alpha too large, I wind up with dull flat cave floors instead of exciting curvy tunnels. If I make the rectangle size too big, I’ll get vast open spaces with little suspense about what’s around the next corner.

And this in fact leads to the sole issue I have with using the canvas to generate maps. I have to call getImageData to see the actual pixel data, and the object it returns doesn’t track canvas changes, so if I wanted to use it to fix paths I’d have to generate additional data objects. Wasteful!

Naturally, I could operate on the data object itself, checking and correcting each path by flipping its bits, though that makes me wonder why I’m using the canvas in the first place. I will exhaust other options first.

UPDATE: Worked out a nice solution. In the path drawing loop, I draw a filled circle with a heavier alpha (0.25) and a radius of 2 every time the distance between the current (x, y) and the last time I drew my circle exceeds 1. It looks like this.

dx = lx - x;
dy = ly - y;
d = Math.sqrt(dx * dx + dy * dy);
if (d > 1) {
	f = map.context.fillStyle;
	map.context.fillStyle = "rgba(255, 0, 0, 0.25)";
	map.context.beginPath();
	map.context.arc(x, y, 2, 0, SOAR.PIMUL2, false);
	map.context.fill();
	map.context.fillStyle = f;
	lx = x;
	ly = y;
}

Stick that in just after the start of the do..while loop. It insures that the path between points is always open just enough to squeeze through.