Class FogOfWarMap
The part of a scene a character has explored, as a grid of chunks over the scene's bounds.
public sealed class FogOfWarMap
- Inheritance
-
FogOfWarMap
- Inherited Members
Remarks
A chunk is either explored or it is not, and walking into one explores the whole of it. That is the entire model: there is no coverage value, no radius, and no falloff. What the player gets is a map that opens up a block at a time as they travel, and a percentage that means exactly what it says — chunks visited out of chunks in the scene.
This replaced a per-cell radial reveal that stored a coverage byte for every four metres of ground: a thousand-metre scene was a 277 by 277 grid, seventy-seven thousand bytes held in memory, gzipped on every save, and uploaded to a texture through a dirty-rectangle tracker. The percentage it produced moved by about one point per sixty metres walked, which read as a readout that never changed. The same scene is nine chunks square here: eighty-one bytes, no compression, no dirty rectangle, and every chunk entered is a visible step.
Chunk size is a scene's own business. It comes from the map definition, falling back
to FogOfWarDefaults.ChunkSize, so a cramped dungeon can be divided more finely than
open country without anything else changing. It is baked into the saved file and a change
discards the old one — a chunk index means a different piece of ground at a different size.
Constructors
FogOfWarMap(Rect, float)
Builds an entirely unexplored map over a world rectangle.
public FogOfWarMap(Rect worldRect, float chunkSize)
Parameters
worldRectRectThe world rectangle to cover, on the XZ plane.
chunkSizefloatLength of one chunk's side in world metres.
Fields
Explored
Chunk state meaning the character has entered this chunk.
public const byte Explored = 1
Field Value
Unexplored
Chunk state meaning the character has never been here.
public const byte Unexplored = 0
Field Value
Properties
ChunkCount
Total number of chunks in the scene.
public int ChunkCount { get; }
Property Value
ChunkSize
The length of one chunk's side, in world metres.
public float ChunkSize { get; }
Property Value
Chunks
The chunk grid, row-major from the rectangle's minimum corner.
public byte[] Chunks { get; }
Property Value
- byte[]
Remarks
Exposed because the store writes it verbatim — one byte per chunk, which for any real scene is smaller than the header describing it.
ChunksX
Number of chunks along the X axis.
public int ChunksX { get; }
Property Value
ChunksZ
Number of chunks along the Z axis.
public int ChunksZ { get; }
Property Value
ExploredChunkCount
How many chunks have been explored.
public int ExploredChunkCount { get; }
Property Value
Remarks
Kept as the grid is written rather than counted on demand. That is what lets ExploredFraction() be a division instead of a walk, and it is why this class no longer needs a cached fraction and a dirty flag to guard it.
GridRect
The world rectangle the chunk grid actually spans.
public Rect GridRect { get; }
Property Value
- Rect
Remarks
Not WorldRect, and the difference is visible. The grid rounds up to whole chunks, so it always covers the scene's rectangle and usually overhangs it — by up to one chunk short of a full one on each axis. Anything mapping the chunk grid onto screen space has to use this rectangle: treating the texture as covering WorldRect stretches it by the overhang, which at four-metre cells was a rounding error nobody could see and at chunk sizes is a third of a chunk of drift by the far edge — the fog visibly out of step with the ground it describes.
IsDirty
True when the map has changed since it was last written to disk.
public bool IsDirty { get; }
Property Value
WorldRect
The world rectangle, on the XZ plane, this map covers.
public Rect WorldRect { get; }
Property Value
- Rect
Methods
ClearDirty()
Clears the dirty flag after the map has been written to disk.
public void ClearDirty()
ExploredFraction()
The fraction of the scene that has been explored.
public float ExploredFraction()
Returns
- float
Zero to one: explored chunks over total chunks.
FromChunks(Rect, float, byte[])
Rebuilds a map from chunks loaded off disk.
public static FogOfWarMap FromChunks(Rect worldRect, float chunkSize, byte[] chunks)
Parameters
worldRectRectThe world rectangle the chunks cover.
chunkSizefloatLength of one chunk's side in world metres.
chunksbyte[]The chunk data. Must be exactly the grid's size.
Returns
- FogOfWarMap
The loaded map, or null when the data does not match the grid.
Remarks
Returns null rather than resizing. A chunk array of the wrong length means the scene's bounds or chunk size changed since the file was written, and stretching old data across new bounds would put the player's explored ground in the wrong place — worse than starting again, because it looks plausible.
Every byte is normalised to one of the two states rather than trusted. The file is signed, so this is not defending against tampering; it is making sure a future writer that stores something else in this array cannot leave a chunk that is neither explored nor unexplored, which nothing downstream has a meaning for.
GetTexture()
The fog as a texture, alpha carrying the fog, rebuilt only when a chunk has changed.
public Texture2D GetTexture()
Returns
- Texture2D
The fog texture. Never null once the map exists.
Remarks
RGBA rather than a single-channel format because UI Toolkit multiplies the sampled texel by the vertex colour: leaving the colour channels at white lets the fog be tinted to whatever suits the theme, where a single-channel texture would force it to be black.
Point sampling, and that matters. A chunk either has been visited or has not, and the edge between the two is a real boundary the player can walk across — so it is drawn as one. Interpolating would smear each chunk into its neighbours and produce a soft blob that no longer lines up with the ground it describes.
The whole grid is uploaded on any change. It is a few hundred texels for a scene, so the dirty-rectangle bookkeeping the per-cell version needed buys nothing here.
IsDiscovered(Vector3)
Whether a world position has been explored.
public bool IsDiscovered(Vector3 worldPosition)
Parameters
worldPositionVector3The position to test.
Returns
- bool
True when the chunk holding the position has been entered.
Remarks
A position outside the grid reads as explored. Off-map is not a place the player can discover, and reporting it as fogged would hide every marker that sits just outside a scene's derived bounds — which, since those bounds are a boundary volume plus padding, includes things a level designer legitimately put at the edge.
ReleaseTexture()
Destroys the texture.
public void ReleaseTexture()
Reveal(Vector3)
Explores the chunk containing a world position.
public bool Reveal(Vector3 worldPosition)
Parameters
worldPositionVector3Where the character is, in world space.
Returns
- bool
True when this entered a chunk that had not been explored before.
Remarks
Returning false is the ordinary case and says nothing is wrong: a character standing
still, or crossing ground it has already walked, re-enters the same explored chunk
several times a second. A position outside the grid also returns false — see
ClientMapSystem, which reports that case rather than letting it pass unnoticed.
RevealAll()
Explores the entire map.
public void RevealAll()
Remarks
For scenes whose definition turns fog off, and for the map baker's preview. Cheaper and clearer than special-casing "no fog" at every draw site.
RevealArea(Rect)
Explores every chunk that a world-space rectangle touches.
public int RevealArea(Rect worldArea)
Parameters
worldAreaRectThe rectangle, on the XZ plane, in world metres.
Returns
- int
How many chunks this explored that were not explored already.
Remarks
Touching, not covering. A rectangle that clips the corner of a chunk explores that whole chunk, because a chunk is the smallest thing this map can describe — asking for half of one is not a question it can answer, and rounding the other way would let an area smaller than a chunk reveal nothing at all.
RevealAround(Vector3, float)
Explores every chunk within a radius of a world position.
public int RevealAround(Vector3 worldCenter, float radius)
Parameters
worldCenterVector3Centre of the area, in world space.
radiusfloatRadius in world metres. Zero or less explores nothing.
Returns
- int
How many chunks this explored that were not explored already.
Remarks
The shape a consumable naturally wants — "reveals the land within five hundred metres". A chunk counts when the circle reaches any part of it, so the result is a disc rounded outwards to the chunk grid rather than a square.
RevealChunk(int, int)
Explores one chunk by its grid coordinates.
public bool RevealChunk(int chunkX, int chunkZ)
Parameters
Returns
- bool
True when that chunk had not been explored before.
Remarks
The grain everything else here is built on, and the one to reach for when something other than a character's feet decides a chunk is known — a map fragment that names the chunks it fills in, a quest that opens up the valley it sends you to, a trigger volume at a vista. Out-of-range coordinates are ignored rather than throwing: a scene's grid changes size when its bounds are re-derived, and content that named a chunk beyond the edge should be inert, not fatal.
TryGetChunk(Vector3, out int, out int)
The chunk containing a world position.
public bool TryGetChunk(Vector3 worldPosition, out int x, out int z)
Parameters
worldPositionVector3The position to convert.
xintThe chunk's X index.
zintThe chunk's Z index.
Returns
- bool
True when the position is inside the grid.