So with this month's optional theme being "Snow Day," I decided to interpret that as a holiday from some of the usual constraints of making a game in a month. All of the basics that a game arguably needs to have to even be considered a playable game, I either ignored or implemented in a strictly bare-bones fashion. Instead, I decided to just give myself free rein to work on whatever little implementation details seemed interesting at the time.
The result, predictably, is a collection of random implementations of various game details on top of a super-simple "run away until you inevitably get caught" game (hopefully evocatively but certainly accurately) titled Being Hunted.
The Game, Such As It Is
Being Hunted is basically a single moment from one of my previous games, Thundersnow. Specifically, a moment when the player wanders in a sparse forest, hunted by some sort of wolf-man creatures, with only his bare hands and his speed to defend himself. In this incarnation, there's admittedly and unapologetically nothing more to it - you can fight them off if you see them coming in time, but there's no escape. You just survive until you don't anymore.
Like I said, not much to it this month. This time the focus was all on the details.
The Real Stuff
What I really spent my time on was all the various little fiddly details that I've thought of along the way in various projects but either didn't get to work on as much as I wanted, or never got to work on at all.
New FPS controller
Very simply, an attempt to create a new, minimal FPS controller setup with the knowledge of Unity I've picked up over the last two of years. It's missing a lot of the stuff that you have to worry about for a general-purpose controller, and which I didn't need for this project, which was part of the purpose. What I have is barely more than a few dozen lines of code and does exactly what I need.
There were, however, a couple of specific details I wanted to try out here, and I was pretty happy with how each of them worked out.
One little detail that isn't as big of a deal now as it used to be but is still a common quirk of FPS games is the (non)existence of a body. Many, many games have your character essentially as a floating eyeball, often with arms, but no other body to speak of. I decided early on to see what was so hard about having a body, and it really wasn't that bad. One required trick is just to use a special shader for the player's head, which doesn't render anything itself but does write to the z buffer, so no part of the player's head appears in the POV camera, but the player's shadow still has a head. So in Being Hunted, when you look down, you'll see your character from the chest down, and you'll see a proper shadow. The real challenge here for a larger game seems to be that it would take a lot of coordination between development and animation to make sure that the necessary animations can be made without ever moving any body parts through the near clip plane of the POV camera, so as the number of player animations increases, so does the complexity of the FPS body.
The other experiment involved head bob. For Delta g Arena, my game jam project last month, I put together my own quick and dirty head bob logic that I was pretty happy with, but it occurred to be that if I'm using a fully-animated player mesh anyway, why do I need a head bob at all? Can't I just attach the POV camera to a bone of the player mesh for realistic head movement? The answer, as it turns out, is yes, mostly. The trick is that to really do this, much like the FPS body, it would require having your animators on-board early to make it work. The animations I was using weren't made for this wacky notion, so the head bone had some rotation and instability that isn't obvious when watching the animation externally, but is distracting when the camera is attached directly to it. That said, all I really needed to do was add a script to the POV camera so it tracked its position and rotation to a target object that was parented to the player's head bone, then leveled the camera's rotation out to be parallel to the ground plane. So with no code written, my POV camera's movement is actually more accurate and realistic than anything I would have coded.
Custom Ground Shader
One problem that complicates making terrains when you have no budget, in terms of both time and money, is that a texture applied to a large terrain area either looks blurry up-close, or has obvious tiling at a distance. While one solution would be increasingly-large textures and more complex texture mapping and terrain, neither of those is practical for a quick project. So instead, using the indispensible and fantastic Shader Forge, I created a custom ground shader that takes two texture parameters as well as two distance parameters. Any point closer to the viewer than the Near distance uses one texture, anything further than the Far distance uses the other, and the area in between is progressively blended between the two textures. This way, the near ground uses a small and detailed texture, while the distant areas use a texture with a much larger tiling area, to reduce the repeating patterns. Each of those textures also has its own bump map, which is blended the same way.
While I was at it, I also added the ability to add a tint to the ground whenever the view angle approaches parallel. When the tint color is close to the sky/fog color, this reduces some of the harsh edges of the terrain where it meets the sky. I also added another feature, which really belongs in the next section...
One thing I wanted to revisit from Thundersnow was the snow system. Aside from the particle system, the terrain showed snow accumulation, but that system relied on a few tricks that only really worked in an overhead distant view. This time, I wanted something more robust.
My custom ground shader also includes a snowfield texture, which is just a greyscale pattern representing where the snow would accumulate, and two settings, one for the snow accumulation level and one for how patchy the snow is. The snowfield texture is multiplied by the snow level, and then the diffuse of the material is a blend of the lighter of the ground texture and the snow texture. The patchiness level is included as a separate threshold, which prevents the snow from showing up until the snow texture multiplied by the snow level exceeds the patchiness level. A low patchiness level makes the transitions between regular ground and snow very gradual, while a higher level makes the snow accumulate first in isolated, clearly-delineated areas before eventually filling in the gaps. The value on the snow level is not clamped, so gradually as the snow level increases, the details of the ground are lost entirely, leaving only a plain white diffuse with only a normal map to add definition. This gives the snow the "smoothing" effect you would see with real snow.
Once all that's done, all you need is some code to gradually increase the snow level value on the material over time, and a particle system to represent the snow fall. The snow particle system uses a real snowflake picture as its texture, and each snowflake uses a low gravity multiplier to fall naturally. They collide realistically, and fade away as they melt. The rate of snow is increased in step with the snow level of the ground shader, so as the snow gets heavier, the ground slowly turns white.
There was one more component of the snow system, which is part of another system...
One of the clearer examples of fiddly little details I decided to spend my time on, one of the more involved systems in Being Hunted is the footstep system. The player and the enemies generate footsteps as both visual effects and sounds.
Initially, each footstep can generate one of six footstep samples, which are played with a slight random pitch shift to make them sound more organic. As the snow level increases, each footstep can instead generate one of six different snow-appropriate samples, until eventually only snow samples are played. In addition, each footstep has a chance to also emit one of five noise samples, of leaves crunching or a twig snapping, in addition to the footstep sample.
Each footstep also places a small projector object to project a footprint onto the ground, which is simple enough. The only thing somewhat interesting there was that it gave me a good excuse to try out a different object pooling method, since those footprint projectors are obvious candidates for object pooling. Not a lot to say there except that it works.
Yes, even my trees are a series of experiments. In the editor, they ground themselves to a custom grounding mask and optionally scale and rotate randomly within ranges. In-game, they turn their shadow projectors on and off at a distance to reduce draw calls, and at a further distance switch to billboarded quads. They also use the vector dot product of the player's view direction and the direction to the tree to decide whether the player can see it, and if not it turns itself off completely until the player can see it (unless it's close enough to potentially cast a shadow the player can see, in which case it remains on).
Various Other Stuff
Most of it probably doesn't really merit examination by anyone other than me, but I lost track of all the little experiments and details I worked on in this project. I probably spent a whole day just getting the thunder and lightning working the way I wanted them to, with a ton of variety in behavior, multiple thunder samples, etc. I didn't like the way the Unity 3D sound was working for me so I ended up implementing my own system of generating 2D positional sounds on temporary audio sources positioned around the player. Nothing makes you appreciate the amount of work that goes into polishing a full, complex game like just spending some time implementing little polish details.
Bonus Fiddly Things
In the end, since this is primarily an experiment, I also added some extra info and tools available in-game. Pressing Tab will show some additional information, including keybinds to manually adjust the game world and a minimap of the player and enemy locations.
Since I gave myself permission this month to barely produce a game at all as long as I implemented all the various experiments, I can only really evaluate this based on how those experiments worked out. In those terms, this was a good way to close out the second year of 1GAM, because there's a long list of things going on in this project that I wouldn't have known where to start on implementing back when I first got started on this little venture. And the fact that it uses an open source text rendering library that I am now a significant contributor to feels pretty good, too.
Note: the following builds are available for testing purposes but I don't have the ability to test them. These are Unity builds so I don't anticipate any serious issues, but I can't vouch for correctness or performance of these builds.