I said I’d write up a few notes about how I went about building Forska. Here is some information on various aspects of the project:
Currently the project just uses Worley (i.e. Voronoi) noise to generate the mountains – I found a good implementation here that I got working in Unity with minimal tweaking. As a single function I find it looks pretty good, although the prolific circular pools you see are a result of my relying on one function too much. Difference noise (i.e. subtracting two sets of octave’d “clouded” perlin noise) does a good job of making canyons and fjords. [Height = (1 – Worley)] for height makes a decent set of rolling hills, especially with careful use of further forms of noise to keep things from looking too mathematical. I plan on adding a series of different methods of generation, plus ways of combining them together – using, for instance, another noise function to blend between two or more functions. Mountains can blend into valleys and canyons, and you’ll see different forces at play on a single landscape. The main reason I did not prioritize diversifying the terrain was that generating the heightmaps were, by far, the slowest function to call in Forska and hence the longest to test and bug fix, and I wanted to get other features in once I had the basic system down. Adding to the lexicon of terrain generation will be pretty straightforward now that I’m not in a “Game Jam/Time Limit/Allocate Attention Sparingly To Any One Feature” mode.
Inscriptions, Agents, and Portals
The inscriptions are random selections from “Lyrical Ballads, With a Few Other Poems (1798) by Coleridge and Wordsworth.” I’ll just say that Project Gutenberg is a wonderful resource that, to me, embodies what the Internet should be. Like everything else, the inscriptions are based off of random seeds, and the resulting passages should be the same and persistent each time someone finds them.
Agents currently just alternate between moving randomly and standing still. They’re also just billboards – given the still-image approach and the post-processing effects, this approach seemed to work well enough for a game jam. The stand-in image for the agents is a woodcut of a Landsknecht – it was the first Googled woodcut image of a person standing that I found, and, once the filter is on, looks a bit like Picasso’s take on Don Quixote, so I felt I had the right shape I needed to suggest a fellow humanoid traveler in Forska.
Each world contains four portals. Each portal in turn contains the random seed for the world it leads to – in this case n+1, n-1, n+47, and n-47. The arithmetic symmetry guarantees the portal has a counterpart leading back to it, whatever destination it might send the player to. I cap the ends around +/- 2 billion, that being the limits of 2^32, the integer input for Random.Seed();
I’ll add an aside that, while Unity’s built-in Random() functions served me well, I do plan on making my own random number generator. It’s too important and crucial for any procedural project, and I want to have better control and understanding on how the keystone of this project would work. Organizing my own system to save and reload several parallel seeded random “tracks” of numbers would be extremely useful, for instance.
Adding bright lights around objects of interest made them easy (or at least possible) to find, while giving the player something to do when it gets dark. The magenta colors on the portal were chosen for similar reasons.
“Cities” being rather charitable – they’re really just an array layered over the heightmap, instancing a set of meshes when the world is generated. Even more than the terrain, this is basically just a system that is begging for expansion, and I wrote it with that in mind.
Currently the array has each cell look at its neighbors and then changes itself into one of 16 states, and then refers to the appropriate mesh. I plan on adding in compatible/incompatible cells, so you could have differing structures, styles and blocks meshing with each other.
After terrain heights are generated, I pick a few places to start building ruins, testing to see if the terrain isn’t too steep, and leveling it out if it is. The city grows outward randomly to point, so each ruin a rough blob shape before the meshes are applied. This can and should be more sophisticated, but served well enough as a way to interface with the terrain itself as a start.
The ruins could use a lot more expansion – even adding in interiors and underground spaces. It’s a whole other aspect of Forska that could become a huge entity in its own right.
I used earlier code from “WorldByFrame” and “Eye’s Incunabula” to have the paradoxical “free roaming point-and-click” interface. The idea behind it is quite simple – you click on the image, and you’re teleported to where you clicked. In many ways this calls back to adventure games like Myst, but instead of having a limited database of images, I take the appropriate image using a virtual camera and a 3D space. It’s a simple matter of raycasting from the mouse cursor. The devil’s in the details, of course. I have turning in increments of 45 degrees, but adding the 180 degree turn proves very useful, especially when you’re stuck on a hillside. The “small steps” movement is useful in specific situations, like when you’re close to something your size like an agent or inscription.
I do plan to add further interaction with objects, which, from a technical standpoint, is just more raycasting. Interaction with ruins and other structures will need a similar type of management to help the player know where they’re going, and if they’re going where they want to go.
As I’ve said before, this interface helped me get away from not worrying about a lot of details that I didn’t want to get stuck in, while focusing on the more procedural aspects of the project. Agents could be simple billboards, while still having velocity-based movement. I didn’t have to worry about things like meshing animations and joystick feedback, or particularly worry about computer performance rendering a large-scale landscape.
This idea of “click to move” came from a frustration I had with large open-world games where you have to walk everywhere. Hiking to a mountain a mile away can make your thumbs ache when using a gamepad – making parts of the experience a slog, especially when combined with backtracking or mandatory destinations. Making the journey pretty, and fun, can help a lot (and really should be done anyway!), but often only goes so far (especially if the player needs to do the same hike multiple times). “Slog” is the navigational equivalent of “grind,” and while most games have different ways to mitigate or avoid slog, it’s something that any open world experience needs to at least address. With the paradigm Forska uses, taking a single step or walking a mile can both be done in one click.
I also have watched far too many people struggle with a game controller while exploring a space at 24-60 frames per second. My countless hours of gaming in my youth have given me the dexterity to use a joystick – many people have not had this tacit education. I wanted to experiment with removing this barrier of entry to exploring a virtual space.
While the current implementation makes the player fairly omnipotent, it doesn’t have to. You can limit distance, slope, or materials (i.e. make it so the player can’t cross water or climb a mountain that’s too steep). I can easily imagine a puzzle where the player has to rock climb – finding the right hand or foothold to clamber slowly up a tall precipice. Vehicles, or other ways of changing how the player implicitly moves through the space, could allow other interfaces and movement styles while keeping the same basic rhythm and accessibility behind the navigation.
As I said in my earlier blog entries, I like to get a “look” defined early on in a game jam. The shader is a modified blur function, with a noise filter added randomize what pixel samples are chosen to “blur.” On its own, that function produces a wavy distortion, like looking at something under water, or the dreaded “shower door” effect that can plague a lot of post-processing shaders. Another problem is that the blur affects all parts of the image equally, which can result in far away details being utterly obliterated. What I added next was a call to the depthmap, and made the shader reduce the amount of blur depending on the distance sampled. With the right parameters this maintained some illusion of depth, while roughing up edges appropriately. Another noise filter added before the primary “blur” (now a “paint”) shader give more texture to otherwise solid colors, adding some more detail to the impressionistic image.
Animating the effect in realtime is not a technical problem as much as an artistic one, animation adding another dimension to consider, with several valid possibilities and choices to make. There are also many other things I plan to do, such as taking contrast and color value into consideration, or looking at strategic outlining, or any number of other approaches.
ProcJam 2014 was a ton of fun. I learned a lot from the talks, learned how to use twitter, saw some enlightening projects, and consolidated a lot of ideas I had simmering in my brain for a long time. The jam also gave me an opportunity to see what other folks in the community were working on and thinking about, particularly when given a 7-10 day span of time. This time of year seems to be filled with game jams, NaNoWriMo’s, NaNoGenMo’s, etc. etc. and I for one appreciate the trend.