Saturday, April 25, 2009

Playing around


I've been getting rather far afield with what I initially planned for this thing, but that is typical for me. Here is a new image. Couple things about it.

1) It's a fully 3D version of the procedurally generated mountains I made for the 2D top down overmap. Same exact mountains, slightly modified so that the generation routine exports a mesh geometry as well as the color map.

2) I'm playing with the idea of doing the overmap in this style: 3D, with perspective.

3) The above test was done with the Irrlicht engine. I looked at the engine several years ago and it roused my curiosity. I've been thinking of dumping a good part of my code base in favor of a 'finished' engine such as Irrlicht, due to the rather incomplete nature of many of my systems. When it comes to the boring details, I'm not a very thorough implementer. I'll code just enough of a skeleton framework to have it come back and bit me in the ass when it's time to refine the project and make it more robust.

However, I do need to implement some things if I decide to use Irrlicht. Due to the nature of my levels (randomly generated, pieced-together meshes) the existing scene node types (particularly their Octree implementation, which is weird) are not suitable I'm working on a quad tree structure which should be sufficient for my needs, since it will essentially be a 2D game in that it won't have Z levels or fully explorable structures or anything like that.

Still not 100% decided about anything, though.

Tuesday, April 21, 2009

3D Test

evolutional is sort of talking me around to doing it the fully 3D way. It is a lot simpler, although there are things I don't like about it. My modeling style tends to be sloppy: lots of verts, probably way more than is needed to structure a scene. Which is why I usually render a structure then map the render to a crude, low-poly shape (like the sides of an isometric box or cell, as I've been doing). With that method, the detail of your geometry doesn't matter since it's pre-rendered, but getting seams and edges to line up, etc... takes some work.

Doing the objects fully 3D, though, I don't have to worry about a lot of the things, but I do have to worry about the complexity of my geometry. I also have to completely overhaul my lighting system. In the attached screenshot you can see a render test in progress, a section of (placeholder) cliff on a nasty looking background. Just working out the kinks and details of the Blender->Game pipeline, and playing with some lighting options. The above is statically lit, no dynamic lights in play. In fact, in-game lighting has been disable since the 2D grid system I was using doesn't play well with 3D geometry. It looks bad. There is also some cracking between a few of the wall segments. That was my foul up. 0.9999999999 does not equal 1.0, and the GPU will prove it to you every time.

The good news is, though, I have a Wavefront .OBJ loader up and running, so exporting from Blender is a bit easier.

Sunday, April 19, 2009

Graphics Asset Pipeline

I'm in the midst of hammering out the asset delivery pipeline and storage system for the submap isometric graphics. Part of the reason I had initially elected to do a straight top-down game was the trouble I have with creating the graphical assets in isometric games. I've worked out a few techniques, though.

The games I make are typically always randomly generated, so I need to structure my graphics in sets of tiles that can be combined together to create any arbitrary layout or section. Hence, the creation process has some pretty tight restrictions. In this screenshot, you can see a typical view of Blender as I work on a particular graphic set.



I construct a special staging .blend file with planes to represent the tile nodes of the map, then create the objects to be rendered on top of the planes, using the planes as guides for spacing and positioning. In order to create the seamless effect, the objects need to be aligned with copies of themselves in adjacent tiles. Then, a tall sliver or slice of the scene is rendered:



If I did everything correctly, the slice can be used as a piece in the larger set, tiling next to itself as many times as are needed:



Of course, this is an extremely simple example and in reality it gets more complicated. The above example renders a narrow (64 pixel) slice. However, not all structures will repeat that frequently, so different sets need to be made with more pieces to cover a larger area. Also, corner pieces need to be made to tie in all the flat side sections. And with simple, regularly repeating geometric shapes like these arches, there are few gotchas. However, when doing irregular shapes like a cliff face or jumbles of boulders, you need to watch out and be very careful with your positioning, lighting, and rendering or ugly seams can result which destroy the illusion.

Thursday, April 16, 2009

Isometric

The switch to isometric, engine-wise, is as simple as changing a camera setting. There are some minor complications with texture filtering, easily solved by rearranging the way the assets are stored, not a big deal. I am considering doing the submaps in isometric. They are where all the action takes place, and things aren't as weird as a top down.

It's either that, or switch the story and setting of the game itself to one that favors a top down perspective more. The biggest gripe I have is that humanoid figures are so hard to distinguish in top down, since most of the distinguishing features are hidden. However, if I were to switch to focus on a different set of races/civilizations/whatever rather than humanoid, I could pull off the top-down much more stylishly.

Awhile back I had the idea for a game called Drone, wherein you play a drone member of a hive of genetically altered insects. You advance in rank within the hive, and correspondingly advance in power and self-mastery until you could at last break away from the hive mind and start your own hive.

The thing I like about insects is that they look good from top-down, and are easily distinguishable from one another. The thing I don't like about insects is that making graphics for their hives and nests is very difficult for me. All the dripping ichor and ooze, all the gristly legs and antenna, etc... I don't do so well with that sort of thing. My dripping ichor tends to look like grey blobbish things, and not ichor at all. Also, so far I've held somewhat to the old-school RPG look and feel (mountains/forest/grass/water, fantasy-setting type stuff), and I'm not sure how well it would jive with the whole insect thing.

I could always resurrect my old Golem story. Many years back I was working on an isometric game called Golem, wherein you play the part of a magical construct working to save the last remnant of the human race from extinction by other golems running wild. The beauty of that idea was that the constructs were often bizarre, and typically not humanoid in nature. Many of the golems I conceptualized would work as well in top-down as in isometric.

At any rate, I have begun work on the 'action' parts of the game. To crudely paraphrase Gamasutra's post-mortem on Diablo 2, I need to get a guy moving around on the screen and hacking at monsters. Whether in top-down or isometric, monster hacking is monster hacking.

Wednesday, April 15, 2009

Mobs

I've made top-down retro-style games/demos before, and there always comes a point when I question the choice to go with top down. And that point is always the same: NPCs and characters, also known as mobs. Or, more generally, any game object that makes the world interesting. The problem, you see, is one of perspective. Here is Stickman.


Stickman was born a couple seconds ago in Blender. Pretty leet, yeah? That image is Stickman from an isometric perspective, and from that viewpoint you can see all his sticky awesomeness. However, here is Stickman in the top-down perspective.



Ugh. While Stickman still has awesomeness, so much of it is hidden when viewed from this perspective.

It's when I face the limitations of the top-down perspective that I get frustrated. So often, I spend hours making a character look just right in Blender, only to have it end up appearing to be an indeterminate blob when rendered top-down.

So right now, I'm doing the old, familiar wishy-wash. Do I continue investing time and resources into the top-down perspective, or do I switch the submap view to an isometric perspective? Each format has it's drawbacks. Even 3D has drawbacks. I've tackled the isometric format before, so I understand how much more work it is than the simpler top-down format, and while the results make the extra work worthwhile, I started this project to avoid some of the complexity.

I hate making decisions.

Tuesday, April 14, 2009

Tweaks

Just tweaking the colors a little bit, for a bit more vibrancy and warmth. The submap water layer has partial transparency so that I can use the water bed terrain type to darken or lighten the water, and distinguish deep water from shallow water. I'll be able to use that later in implementing shallow water river fords and such.

I did an experiment earlier today, replacing the procedurally generated submap grass layer with one I edited from a photograph of real grass, and while the results were pretty good I had the wife tell me she liked the original stuff better. I tend to agree with her. Textures from real-life sources tend to have a lot of detail that a simple procedural texture does not, and while in many applications this is desirable, for the updated-old-school look I've been working for, a simpler and less detailed look seems to suit better. Maybe in the next day or two, I'll post screens of both somewhere and take a vote on it.

Monday, April 13, 2009

More Wilderness Generation

Wrote a couple quick scripts to generate some types for the wilderness submaps. Got a sand, dirt, grass and water type. They're derivative of the overmap types, but done in 128x128 tiles sizes, rather than the 64x64 tile size of the overworld. I decided to go with a larger tile for the submaps, to allow for greater detail in the tiles.

The types do need some tweaking (I especially don't like the grass) but they'll do for placeholders for now, while I tweak and tune the wilderness generator and start building up a stock of assets.

Wilderness Generator


Working on the wilderness generator. For now, I'm re-using the tile types from the overmap, although soon I'll whip up a new set that is more suitable for the lower level of zoom. I'm working out the 'look' I want for the object sprites that populate wilderness levels; in this shot you can see a few of the tree sprites I've created as a test. I plan to incorporate various vegetation sets depending on the geography of the submap; beach-type vegetation, deep forest, savannah, etc...

In the overmap, parameters are assigned not on a tile basis, but on a per-vertex basis. It's a bit hard to explain, but I assign a particular terrain type to each corner of a tile, then generate the tile graphics for a tile based on the vertex corner pattern. This has the effect that, at places where two or more types of terrain meet, that tile will represent a transitional region and the sub-map needs to be generated accordingly. Right now the wilderness generator just does a random scatter, but I am working on parameterizing it and splitting the submap into quadrants, with turbulence applied to the quadrants, then generating each quadrant according to the parameters of the associate closest vertex.

The wilderness generator is intended to be very generic. Nothing special is generated as a result of it; no caves, no towns, no encounters. Just terrain. The generator is called with a seed (derived from the world seed, plus an offset based on the tile coordinates) and the parameters for the four corners, and spits out a wilderness map to suit. In later iterations of the game, some tiles will be flagged as special, with special rules for generation. These will generate the areas where stuff happens, like towns and dungeon entrances.

Randomized encounters (also known as ambushes) are created based upon the underlying generic wilderness submap.

Last night I worked on the asset pipeline for creating submap sprite graphics. Objects are modeled and rendered in Blender. I do the rendering in 2 stages; first stage to generate the colormap for the sprite, on a transparent alpha background; and second stage to generate the shadow. I special Lua script combines a colormap and a shadow layer into a single alpha-mapped sprite that can be drawn in the game. I'm currently tweaking the lighting parameters for the best effect.

The trees in the above shot were procedurally generated using an L-system script for Blender. The script is old, but still works with my newer version of Blender+Python. It's a fun little script, and while the interface is confusing (all those buttons and sliders; crikey) the supplied .DEF files provide good starting points to work from. L-systems are pretty neat, if you haven't looked at them. A bit confusing, but once you've wrapped your mind around them, they are a fantastic tool for proceduralizing naturally occurring fractal-type shapes such as trees and other vegetation.

Saturday, April 11, 2009

Refactoring


Nice, big, productive day today. Not a whole lot to show for it visually, but I've been doing a HUGE amount of backend refactoring to remove hacks and make things 'official'. Got the fundamentals of the object system in place, to allow spawning of objects to populate the map. While the objects currently spawnable are all static, the groundwork is lain for extending to cities and areas, NPCs, the player avatar, etc... All in all, I'm pretty happy with the day so far.

I've gotten in the habit of never allowing the pointer returned by new to exist outside of a boost::shared_ptr. Awhile back, I came up with a scheme for resource loading that uses both boost::shared_ptr and boost::weak_ptr.

Often in a game, I will need to load a resource. Images, sound files, animations, etc... Some objects can share usage of a given resource, in which case I need to ensure that a given resource is only loaded once, regardless of how many objects request it. The most elegan solution I have come up with so far is a templated handler class that maintains a map of (std::string > boost::weak_ptr) mappings. The handler is given a string name of the resource to load, and it uses the name to search the map to find an existing weak_ptr to that resource. If no entry exists, the resource has not been requested yet, and the resource is loaded and inserted into the map. If an entry exists, but the pointer is expired, it means that the resource was loaded once but the objects using it have been deleted, so the resource is reloaded.

This has the effect of keeping a reference to a resource, but the resource itself is deleted when no objects are referencing it. The weak_ptr in the list doesn't count as a reference to the resource, so the entry in the map will not keep the resource from being dumped if all other objects that use it are deleted. It's a pretty simple scheme, but elegant.

I've begun the framework as well for generation of sub-region maps. At the moment, I am refactoring the world generation code now that I have a better idea of the kinds of values I need to track for wilderness area generation. I hope to have something to show in the sub-map department soon; today's refactoring should help there immensely, as part of the work I did was also to switch between world map and sub map at will.

I've also spent an hour or two working up some layouts in Blender, in preparation for building city/tower/cave/etc... location icons for the world map. Once upon a time, I was fairly decent with Blender. Those skills, alas, seem to have deserted me and I must get them back. Rest assured that when the missing skills are found, they shall be soundly punished.

Thursday, April 9, 2009

Lighting

It's kind of amazing how much code I have just laying around on my six or seven scattered hard drives. Took a couple minutes break from re-working all the file system stuff to implement tile-based lighting, based on some code in the Accidental project tree. I've got LoS disabled because I'm not 100% sure I even want it. I mean, what's the point of having a bird's eye view of things, if you can't use it to see beyond walls? I think that being able to see that hulking L99 badass on the other side of the wall, just itching to have a go at you, builds anticipation. But I'm leaving the LoS code in, just in case I decide, down the road, that I really do want it.

Gotten a lot of the refactoring done. Most of the PhysFS refactoring is complete, and I've done some tweaks to the world save/load system to make it more robust and less vulnerable to hacking the save file. Still need to shore up the cracks, though. Some corrupt save files could still cause problems.

I'm hammering out the design as I go, which might be a bad idea. Before I get too far ahead of myself, I might need to formalize the structure just a bit.

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.