the healer’s cave (closed for renovations)

 I’ve created a new object that should be going into Soar soon. It’s a twist on the Noise2D object I use for generating heightmaps. Instead of interpolating across an array of random numbers, I interpolate across a Canvas.

Why is this a big deal? Well, I tend to use the Noise2D object to generate caves–the setting for my current game, as a matter of fact–and I wanted to generate a cave as a set of chambers. I started by creating a Noise2D object, then modifying the underlying source array to generate specific features. Want a wall? Change a couple of cells of the array to 1. How about an opening in a wall? Set a cell to 0.

Soon, I was trying to implement the whole map this way, and thinking: it would be great if I could just use drawing functions to do this. Need a room? Draw a rectangle, or a circle. A passage? Draw a line.

Then I remembered that Canvas has (a) drawing functions and (b) getImageData which allows you to retrieve whatever you’ve drawn as a bitmap. An hour later, I had my new Canvasser object all coded up.

/**
	provides an interpolated 2D function over a canvas

	adapted from the 2D noise function. draw over the
	context as usual, call the map() function to make
	a bitmap of the canvas, then call get() with the
	channel number.

	@namespace EASY
	@class canvasser
**/

EASY.canvasser = {

	RED: 0,
	GREEN: 1,
	BLUE: 2,

	/**
		create the canvasser object

		@method create
		@param ampl maximum amplitude of function
		@param width width of canvas
		@param xscale scale of output in x
		@param height height of canvas (defaults to width)
		@param yscale scale of output in y (defaults to xscale)
		@return new object
	**/

	create: function(ampl, width, xscale, height, yscale) {
		var o = Object.create(EASY.canvasser);
		o.canvas = document.createElement("canvas");
		o.context = o.canvas.getContext("2d");

		o.canvas.width = width;
		o.canvas.height = height || width;

		o.scale = {
			x: xscale,
			y: yscale || xscale
		};

		o.interpolate = SOAR.interpolator.cosine;
		o.amplitude = ampl;

		return o;
	},

	/**
		generate an addressable map from the canvas

		you must call this function before using any of the
		get functions, but AFTER drawing on the canvas.

		@method build
		@x left of area to map (defaults to 0)
		@y top of area to map (defaults to 0)
		@w width of area to map (defaults to canvas width)
		@h height of area to map (defaults to canvas height)
	**/

	build: function(x, y, w, h) {
		this.map = this.context.getImageData(
			x || 0,
			y || 0,
			w || this.canvas.width,
			h || this.canvas.height
		);
	},

	/**
		get the function value at (x, y)

		@method get
		@param c channel to address (0, 1, 2)
		@param x any real number
		@param y any real number
		@return value of function at (x, y)
	**/

	get: function(c, x, y) {
		var xf = this.scale.x * Math.abs(x);
		var xi = Math.floor(xf);
		var mux = xf - xi;

		var yf = this.scale.y * Math.abs(y);
		var yi = Math.floor(yf);
		var muy = yf - yi;

		var xi0 = xi % this.map.width;
		var yi0 = yi % this.map.height;
		var xi1 = (xi + 1) % this.map.width;
		var yi1 = (yi + 1) % this.map.height;

		var v1, v2, v3, v4;
		var i1, i2;

		v1 = this.map.data[4 * (xi0 + yi0 * this.map.width) + c] / 256;
		v2 = this.map.data[4 * (xi0 + yi1 * this.map.width) + c] / 256;
		i1 = this.interpolate(v1, v2, muy);

		v3 = this.map.data[4 * (xi1 + yi0 * this.map.width) + c] / 256;
		v4 = this.map.data[4 * (xi1 + yi1 * this.map.width) + c] / 256;
		i2 = this.interpolate(v3, v4, muy);

		return this.amplitude * this.interpolate(i1, i2, mux);
	}
};

 

It works quite nicely. A simple and obvious adaptation, but hey, I’m learning.

Last entry, I mentioned that I’d be working on game mechanics, and I have done. Yesterday, however, I threw away most of what I’d worked out. Why?

The game was to be called The Healer’s Cave, and it was a folk medicine RPG. The player would run around the cave collecting and mixing ingredients according to herbal folklore, and go to the cave entrances to diagnose and treat incoming patients. The RPG elements included various skills (identifying ingredients, preparation, diagnosis, etc.) that improve with use, allowing the player to level up, and a trust mechanism that describes your relationship with the game world.

I dropped it once I realized two things. One, that the identification, collection, and mixing of ingredients rips off generic fantasy RPG alchemy mechanics. This isn’t bad in itself, as most games reimplement stuff other games do. The more interesting half of the game, in which you have to diagnose and treat a patient using folklore, would have made up for it. Lacking time to generate human 3D models, I decided to implement that more interesting half as a set of screens and dialogs.

So: the really interesting part of the game wouldn’t have had anything to do with the game environment at all. That’s point two, and that’s where I said, well, maybe I can think of something else.

just redecorating the cave

The paddlers are swimming around the cave, glaring at everything with their googly eyes. I’ve altered the cave colors and made the space a little roomier.

The NPC manager I wrote this weekend handles tens of thousands of paddlers while maintaining a framerate of nearly 60 fps, so I’m pretty happy about that. (Naturally, the secret is not to display all of them at the same time, and I do a lot of GL resource swapping  to keep the GPU memory size sane.)

Next week, I tackle the game mechanism.

paddlers ahoy

Here’s a couple of improvements to the creatures I’m now calling “paddlers”. I’ve altered the skin texture generator to provide greater variation across the skin itself. Eye spots are also in evidence now.

They’re a bit cute, I think.

tentacles of doom

Some results from my creature creator.

The two differ significantly in structure and coloration while retaining a standard base body shape and color pattern. Extensions from the central body can resemble tentacles, wings, or ray-like tissue flaps. They beat at the air in a sinusoidal rhythm (hard to see in a still photo, of course).

This week, I plan to get them moving through the cave. I also have plans to add at least one more type of creature before moving the project into another phase (namely, the “why are we here and what are we doing?” phase).

silhouette saturday

Been playing with this for creature creation in my latest game project.






I’ve written code to expand the silhouette into a 3D mesh, and now I’m working on code to generate a texture skin to wrap around the mesh. The idea is to generate creatures on the fly. We’ll see how well it works.

the great twenty-four hour thing

 I was just watching one of those videos where a game development team has to develop a game in 24 hours. It got me thinking, wow, I wonder if I could do that. Then I recalled that I already have. It was called “working at a startup”.

Chris! We need you to invent a completely new product offering over the weekend or we’ll lose a massive contract!

That happened more than once, actually. Good times.

caves are easy (and fun)

So, I’ve been working on my next gaming project. I’d planned to set it in a dark and tangled forest, but my first crack at the visuals didn’t inspire me. Next, I started hashing out an idea that was something like an RPG set on a roller coaster. I haven’t thrown that one out. However, I wanted something else to work on while I let it stew. What could I do?

I turned back to my forest prototype, which had quite a bit of useful terrain and physics code implemented, and realized that I could project the terrain through the xz plane to create a cave. The result?

I’m tentatively referring to it as “caves are easy”. Because they are. Not sure where this one wants to go but I’m cooking up a few odd things for it.