Wednesday, April 8, 2009

World Save(2)

Got the basics of a world save in place. Right now it's crude, dumping the various generated buffers into a collection of files inside a .ZIP archive. A lot of things are hacked or hard coded at present, but the guts are there and functioning. Need to do a small bit of cleanup, and I've added some things to my TODO list:

Integration of PhysFS into the asset pipeline:
Currently, assets are loaded from the base directory, but making assumptions about where save files should be stored, where assets should be loaded, etc... can be bad, especially if you plan to deploy on different operating systems. For instance, sure, the user's folder or Document folder might be a good place on Windows to dump a save file, but on Linux it would be in /home/user and so forth. A virtual filesystem like PhysFS adds a layer of abstraction between the game and the filesystem. I don't have to worry about where the user's home directory is, where the root directory of the game is, etc... I can just query PhysFS to get all of that. The drawback is that I have to re-write all of my various asset loading and saving routines to use PhysFS file handles. TGA loaders, WAV loaders, etc... I've written a lot of code over the years to save data to disk, and I'll need to selectively re-write implementations of all of them to use PhysFS. That's, like, a half-dozen of TODO list items right there.

File versioning and re-structuring of world saves:
As the game matures, the format of world saves will change. I need to settle on a version numbering system for the various save formats, including the world save file.

Cleanup and re-structuring of .ZIP archive reading/writing for world saves:
I use minizip from the /contrib folder of the zlib compression library to read and write the world save archive, but I've hard-coded some bits, and I need to abstract things just a tad better, to make updating and expanding the world save format easier. The library code in minizip does make it a lot easier, though, so this should be no problem.

There are a few other items that I really should tick off the list before I get too deeply involved in the sub-map generation stuff. I've written some spaghetti code this day, and I don't want to leave it in place.

Keepin' it old school

I slept on it last night, and I decided to keep the old school tile block look for the sub-maps.

Tuesday, April 7, 2009

World Save

Working on a first version of a savefile format for a generated world.

I've shoehorned in PhysFS support for virtual filesystem abilities, and I'm writing support for .ZIP archive creation. When the world is saved out to file it is saved to a .ZIP archive for convenience of passing around world files.

On the slate once I've implemented world save/load: I plan to start work on sub-map generation, which means an all new set of graphics since the sub-maps represent a much lower level of zoom. I'm still mulling over some ideas for how I want to handle it. Possiblity 1 is to handle it in the same manner as the overmap, ie a tile-based world with blended transition tiles and object sprites scattered across it. Possiblity 2, though, is to implement it using the smoothly-blended terrain scheme I came up with in an earlier project, visible in this old screenshot:


As you can see, gone are the square, blocky tile transitions of the overmap. I like the scheme, but the algorithm is clunky and I would need to update it. My biggest worry, though, is about the contrast between styles. I'm not sure how it would work, or how it would jar the expectations of the player. Most likely, I will stick with "pure" tiles even for the sub-maps. (Ignore the 3D terrain; I have no plans to do any 3D at all for this game. The terrain blending would be done on a simple 2D tile board. The tiles would still exist, they would just be overlaid with the more complex terrain blending.)

Monday, April 6, 2009

The Base Landmass

The continent begins life as this shape right here:


It is a cross-section of a sphere at the center. The sphere begins at 0 (black) at the outside surface, and gradually fades to 1 (white) at the center. It's a pretty boring shape, though, and not at all realistic; in order to give it shape, I apply an effect known in the graphics world as turbulence. In effect, turbulence is a means of distorting the inputs to a function, by adding some randomized offset to the inputs before the function is evaluated.

Consider the above image of our sphere. It represents a function with 2 inputs: an X coordinate and a Y coordinate. The output of the function for a given XY pair is the pixel at that location in the image. Now, if we were to copy the image to another image, only this time we add/subtract some sort of offset to the input coordinates each time, we would get a distorted version of the image.

pixel(x,y) = pixel(x+randomvalue1, y+randomvalue2)

The nature of the distortion depends on the nature of the random numbers we use to distort the inputs. In the case of Archipelago's island/continent generator, I use something called continuous noise. This is a generalized case of what is commonly called Perlin noise. Perlin noise is a function composed of several octaves, or layers, of a given noise function. The noise function creates a pattern that is randomized and chaotic-appearing, and yet continuous: ie, the function varies by very small amounts at small intervals, but varies by very random (possibly large, possibly small) amounts at large intervals.

The continuous nature of Perlin noise is what is crucial here. If we were to use simple random numbers to distort the inputs, the output would be a very random, noisy, and chaotic image like this:


Now, that is certainly randomized, but it looks nothing at all like a continent. That is because the noise applied was simple white noise (ie, fully chaotic random numbers in the range [-1,1]). We need continuous noise in order to distort the shape of the sphere out of round, but still retain some measure of cohesiveness or form.

In my personal library I have access to a large number of continuous noise types, many based on the noise fractals pioneered by Perlin. Two examples of these, which constitute the continuous noise functions we need, are fBm (standing for Fractional Brownian Motion, a fancy name for what can be thought of as 'standard' Perlin noise) and Ridged Multifractal noise. Here is an image of a section of fBm noise:


and here is an image of a section of Ridged Multifractal noise:


As you can see, both types of noise are fairly continuous, yet they differ greatly in character. By using these different functions to distort the inputs to our sphere function, we can achieve two different types of landmass results. Here is the sphere distorted by fBm:


and here is the sphere distorted by Ridged Multifractal noise:


You can see the difference in the shape, or outline, of the continent that would result from using either of these functions. I implemented the ability to select the type of turbulence function the world is generated from. Here are minimaps of continents generated using first fBm and second Ridged Multifractal noise:




The fBm one tends to produce smoother coastlines and fewer jagged areas and islands because the noise function itself is smoother and less jagged. I have several other options available to me besides these two in the game as well. By selecting the right noise function for distortion, I can radically alter the appearance and character of the generated island.

Minimap


Made a quick minimap generator. It simply takes all of the various buffers used to create the continent (forest map, mountain map, continent shape, rainfall map, and river map) and composites them together using a set of colors to represent the different map elements. Right now it uses a bump map generated from the eroded base continent shape to add relief shading; that may change in a future iteration.

The attached image is a composite; I haven't yet implemented in-game display of the minimap. Not 100% sure if I want to have an overlay, or a separate map screen for this purpose.

Rivers


Added river generation. The technique works by taking the perturbed sphere cross-section that is used to generate the base continent shape, and applying an erosion algorithm to it to erode it and create draws and valleys in a watershed pattern. Then a similar algorithm goes over the eroded terrain, modeling water flow and tracking the amount of water flow each cell receives. By taking all cells above an arbitrary cutoff value and setting them to water, I create a pattern of rivers across the land.

There are a few things wrong with this approach. First, it doesn't really model the accumulation of lakes in areas of local minima. Water flowing downhill will fill basins and spill over to continue toward the ocean in real life; but in this algorithm, water flows until it can't flow any further and then just disappears. Another problem this results in is the existence of many watershed patterns that do not empty into the ocean at all.

At some point I plan to switch river generation to a cellular automata approach, that can handle accumulation and spill-over to more accurately model true water flow.

At this point, I'm beginning work on a first version of a savefile format for the world, as it is getting clunky to re-generate the world each time the application runs. I envision a system somewhat like that used by Dwarf Fortress for generated worlds. Players can either go through the somewhat lengthy process of generating a new world, or download one of several existing pre-generated worlds.

Sunday, April 5, 2009

World Generation


Started working on the overmap world generator. Right now it's in a very basic state. It generates a single main continent by taking the cross-section across the center of a 3D sphere and perturbing the resulting 2D map with some noise turbulence to get the general shape of the continent. Additional fractals and various tweaks are applied to generate areas of mountain, forest, desert and grass. In the works is to add river generation.

Each cell of the map is parameterized so that it contains various values: water content, vegetation level, mountain-ness/rockiness, etc... In a later stage of development, I will construct a system for taking these parameters along with a seed generated based on the world's seed in conjunction with the XY cell location, to create a 'sub-map' location that is a smaller scale version of that map cell. The parameters as well as information about a cell's neighbors are used to determine the algorithm and contents of the sub map. During play, the player will wander about on the overmap. But in certain places, encounters may happen: entering a city, being ambushed by trolls in the wilderness, running across a traders' caravan, etc... These take place within the sub-map of the cell where the encounter is determined to happen. Any combat or interaction with other entities takes place within the sub-map regions.

I am also in the midst of applying the generated parameters in the construction of a mini-map, an image of the overworld where 1 pixel = 1 cell or tile of the map, for purposes of navigation.