At its most basic, displaying field of vision in a roguelike is simply a matter of whether you see a cell or don’t. These cells are in view--draw them; those cells aren’t--don’t draw them :P
But there can be more to it than that!
There are plenty of ways to get better-looking FOV, though before recently I hadn’t spent any time exploring them in the context of Cogmind. By contrast, this was something I did early on with X@COM, because FOV plays an even more central role in that game, being directional and having control of multiple units each with their own viewpoint. With Cogmind it was never a high priority or even something I seriously considered since the FOV system’s original iteration couldn’t handle additional processing, and the central algorithm had already been highly optimized as part of the pre-Cogmind engine’s code base.
However, last week while playing around I realized there’s now a lot more breathing room in the system, enough to make some experiments worthwhile.
These experiments have paid off in the form of new FOV features described below :D
To clarify, this article is concerned with the aesthetic side of FOV, rather than how to determine what is visible from a given position. For the technical side you can see an earlier post from 2013, and also check out this FAQ for how myself and others calculate it.
Some roguelikes allow for dynamic FOV-affecting elements within the player’s own FOV. This includes features like light sources, or fading/darkening objects that are further from the player.
Dynamic lighting is great for realistic environments. I use it in X@COM, where it’s quite an important part of both the realism and mechanics, so there are lamps, light posts, flares etc. that can light up areas of the FOVs around them when ambient light is otherwise low.
Cogmind intentionally doesn’t do any of this, but I’m including this feature in the discussion for completion’s sake, and to explain why :). Unless a roguelike is specifically designed around this (say, a pure stealth roguelike where lighting is a core mechanic, or a world in which light plays an important role in creating the atmosphere), it can easily become an unwanted barrier, obscuring tactical information in a way that brings out biological differences between players (e.g. eyesight), something best avoided where possible in game design.
The FOV area is where a player needs to be especially aware of what objects can be seen, and just as importantly, where those objects might not be seen. The latter can be difficult to discern within an FOV of variable (non-uniform) brightness. Clarity of tactical information is of utmost importance to a roguelike given the emphasis on gameplay over visual appeal. This is also why Cogmind’s map view is generally quite static, and contains a lot of black (few to no background colors except when something important is happening/displaying, like combat or object labels and other UI feedback).
So I’m glad this isn’t a necessary part of Cogmind’s world (that way it doesn’t interfere with other goals of the design), though I’ll likely want to put more work into the FOV design when continuing the X@COM project (yeah, “it’ll happen one day”).
How to display what’s outside the player’s FOV is pretty important, too, though of course for different reasons. Players need to be able to scan previously explored areas for reference, and those areas need to be sufficiently distinguishable from currently visible areas (especially the floors so that it’s possible to easily locate the FOV edge in open areas). At the same time objects outside FOV ideally must be distinguishable from each other as well!
From the outset, Cogmind has technically included four different “extra-FOV display modes” that it inherited from X@COM. These alternative modes make sense in a game like X@COM, but are pointless in Cogmind, as ambient light levels are static and uniform, and there’s no dynamic lighting, so Cogmind has only ever used one of them (of course it’s the most processing intensive one :/). But while we’re here, might as well look at what they are:
For Cogmind, to fit the theme an additional green overlay was layered over method #4 (unlit grayscale):
Note that because the original colors of terrain and objects outside the FOV are different, and that helps distinguish them under normal circumstances (while in view), the grayscale method is best because it reflects those differences. The simpler alternative, using some static darker color for everything outside FOV, is harder to parse because objects lose that relative brightness and then shape takes over as the only way to tell objects apart. Not very helpful:
Conclusion: Grayscale conversion is awesome.
I use the luminance method to convert, taking the original RGB components of a cell’s foreground color and setting each value to a new value created by combining 30% of the original color’s red component, 59% of its green, and 11% of its blue (e.g. r = g = b = 0.3*r + 0.59*g + 0.11*b).
So most of the above is background/supplementary to the meat of what I’ve really been working on lately. While I haven’t made any changes to the appearance of FOV, inside or out, when the player is just sitting around thinking about what to do next, a lot of time is also spent moving, and that now involves some interesting new display options.
Since 2012 (!), when any obstructing terrain was destroyed, FOV would not be updated until the current action completed. This could delay an update until a 1-second explosion fully plays out, for example.
There are multiple drawbacks to this method: We can’t always instantly see all the results of an action (feedback delay), and explosions appear overly constrained in some situations despite having technically already blown through obstructions.
The main advantage is speed: FOV is only calculated once no matter how many doors, walls, or machines an explosion blows through. The locations of every FOV-affecting change to the environment are stored in a list while the effects of an action are being carried out, and at the end of that action the engine checks if the player can currently see any of those locations. If they can then it updates FOV.
This “delayed update” option is still available, because as the least processing-intensive method it might be useful on an extremely slow old CPU. Otherwise there’s no reason to not go with method #2: real-time updating.
Last week I realized that Cogmind doesn’t really calculate much in the way of FOVs, not like its parent game X@COM (dozens of units each calculating FOVs in a 3D environment, on top of omnidirectional FOV-based light sources). Cogmind has hundreds of AIs in a single map, but they use a non-FOV visual detection method, so all we have to calculate is the player FOV and possibly those of their active drones.
The “instant update” option also checks the same list of environmental changes, but rather than waiting until the action is complete, it’s allowed to recalculate FOV every single frame as long as there has been some change.
This is the common and natural way to handle it in a terminal roguelike, but large-scale turn-based games with other CPU-intensive routines to carry out might delay this kind of update until the end of an action (or they’ll delay it for design reasons). Cogmind’s original FOV system, together with the game’s heavy use of animations in some cases, was that kind of barrier to instant updates when it was first developed, which is why instant updates weren’t a thing to begin with.
Even with instant updates, when Cogmind moves or obstructions are removed, newly revealed FOV areas just *pop* into existence like you see in most roguelikes.
Why not have the new area fade in? Some players will appreciate the extra flair, and it does perhaps introduce a slight benefit in that for a split second you can differentiate between what’s new and what isn’t.
Doing this isn’t too hard: When calculating a new FOV, if a location is visible but wasn’t visible on the previous calculation, store its coordinates in a list; then, when rendering the map, fade in the cells at those coordinates.
At first the fade-in code wasn’t working, so as part of my tests I ran this debugging mode that showed FOV changes in red. It looks kinda cool so I’ll just show that here :D
After confirming that FOV changes were being properly recorded, it was just a matter of tweaking the fade formula to actually render those changes dynamically.
Specifically, when each location is stored by the FOV algorithm it must also store the time when that particular change took place, and while rendering the engine uses the time elapsed since that point to calculate the amount of fade, which in this case is not actually a simple linear fade in from black to the target color. Instead it uses a 400-millisecond linear interpolation along a sine curve from the RGB color (0,48,0) to the target color. (0,48,0) was chosen to approximate the dark green colors used to display known areas outside FOV, to give a smoother transition from outside to inside FOV. Each cell’s record is removed after its 400 millisecond timer has expired.
400 milliseconds isn’t very long, but it must be responsive so that fast-moving players can clearly see what’s ahead. Besides, the best secondary animation effects are subtle--in my own work I most often use fade timers in the 200-400ms range.
Performance-wise this is another step slower than the instant update, but it’s not like we’re updating a ton of units here, so it shouldn’t be an issue.
The biggest issue I did run into, and one I initially thought might keep me from enabling this feature, was how to reconcile the differences between revealing unknown areas compared to previously known areas. Unknown areas are black, and previously known areas are, as per the grayscale-with-overlay method, displayed in various shades of green. So in revealing new FOV areas, the ideal linear interpolation would shift from black or the relevant green shade to the new color, but it’s impossible to achieve a perfectly smooth transition without knowing the actual color from each source cell. Unfortunately there would be a lot more code (and processing) involved in figuring that out, so the easiest route here was to pick a single source color that was dark enough to work for black-to-light transitions, and also compatible with the various shades of possible green from known areas.
Before coming to that conclusion, in the search for a perfect transition I experimented with another alternative: using a static uniform color for everything outside FOV. As described earlier, however, failing to account for relative brightness of individual objects is a a pretty poor way to handle non-FOV display, being difficult to parse.
So, back to the chosen color, (0,48,0). Using a static color to transition from a variable source causes a bit of flicker at the FOV edges depending on what color the walls are, most noticeable while racing down a narrow corridor where the majority of your FOV is walls.
And with that we have the final results of our new FOV updating features: See explosions in all their glory!
I think I might prefer the Instant method for its traditional feel and instant feedback, though technically the coloration only applies to the map itself, and not animations nor the most important feedback mechanisms like labels and indicators--those still appear all the same regardless of the fade-in effect. Either way, for now I’m setting the game’s default to Fade In, because it’s “better.” What do you think?