Table of Contents

Class FogOfWarMap

Namespace
FishMMO.Client
Assembly
FishMMO.Client.dll

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

worldRect Rect

The world rectangle to cover, on the XZ plane.

chunkSize float

Length 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

byte

Unexplored

Chunk state meaning the character has never been here.

public const byte Unexplored = 0

Field Value

byte

Properties

ChunkCount

Total number of chunks in the scene.

public int ChunkCount { get; }

Property Value

int

ChunkSize

The length of one chunk's side, in world metres.

public float ChunkSize { get; }

Property Value

float

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

int

ChunksZ

Number of chunks along the Z axis.

public int ChunksZ { get; }

Property Value

int

ExploredChunkCount

How many chunks have been explored.

public int ExploredChunkCount { get; }

Property Value

int

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

bool

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

worldRect Rect

The world rectangle the chunks cover.

chunkSize float

Length of one chunk's side in world metres.

chunks byte[]

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

worldPosition Vector3

The 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

worldPosition Vector3

Where 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

worldArea Rect

The 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

worldCenter Vector3

Centre of the area, in world space.

radius float

Radius 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

chunkX int

The chunk's X index.

chunkZ int

The chunk's Z index.

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

worldPosition Vector3

The position to convert.

x int

The chunk's X index.

z int

The chunk's Z index.

Returns

bool

True when the position is inside the grid.