Repository navigation
Tile Based Level Map Reference
LevelMapData is the parsed, render-independent representation of a level. A MapLoader reads a map file and returns a LevelMapData holding the level's dimensions, its legend of tile definitions, its ordered list of layers, and its spawn data. It has no rendering dependencies, so it can be created and asserted on in unit tests without a graphics context. The renderer and collision systems both read from it. All classes live in the package com.csse3200.game.areas.terrain.map.
flowchart LR
F["maps/*.json"] -->|"JsonMapLoader.load()"| D["LevelMapData<br/>(pure data)"]
D -->|"TerrainFactory.createTerrainFromMap()"| R["TerrainComponent<br/>(renders layers)"]
D -->|"LevelGameArea.spawnCollisions()"| C["Tile colliders<br/>(SOLID / PLATFORM / HAZARD)"]
D -->|"getSpawns()"| S["Player / enemies / loot"]
LevelMapData is the hub: the loader produces it, and the renderer, collision, and spawning systems
all read from it.
The data model is made of small, focused classes.
| Class | Purpose |
|---|---|
LevelMapData |
The whole level: name, tile size, dimensions, legend, layers, spawns. |
MapLayerData |
One named layer holding a grid of TileDefinitions (null = empty). |
TileDefinition |
A legend entry: a TileType paired with a texture path. |
MapSpawns |
The player start plus lists of enemy and loot SpawnPoints. |
SpawnPoint |
A single spawn: an optional type name and tile coordinates. |
Key LevelMapData accessors
| Method | Returns |
|---|---|
getWidth() / getHeight()
|
Level size in tiles. |
getTileSize() |
World units per tile. |
getLayer(name) |
A layer by name, or null. |
getCollisionLayer() |
The collision layer, else the terrain layer. |
getTileType(x, y) |
The collision-relevant tile type at a cell, or null. |
getSpawns() |
The MapSpawns for this level. |
getTexturePaths() |
Distinct tile textures to load. |
Maps are loaded through the MapLoader interface, so alternative formats (TMX, overworld maps) can
be added without changing callers. JsonMapLoader is the default JSON implementation. Its parsing
step is exposed separately from file access so it can be unit-tested without libGDX:
public interface MapLoader {
LevelMapData load(String path); // throws MapLoadException
}// Load from an asset file:
MapLoader loader = new JsonMapLoader();
LevelMapData map = loader.load("maps/demo.json");
// Or parse a string directly (used in tests, no graphics context needed):
LevelMapData map = new JsonMapLoader().parse(jsonText);A missing, unreadable, malformed, or structurally invalid map throws MapLoadException.
A space, or any character not in the legend, is an empty cell.
Layers are drawn back-to-front in the order they appear in the file. The collision system reads
the collision layer if present, otherwise terrain.
flowchart LR
A["background<br/>(drawn first)"] --> B["terrain<br/>(walls / platforms / hazards)"] --> C["foreground<br/>(drawn last, over the player)"]
In sprint two the entity layer was added. Older maps that use the coordinate based spawns system are still supported and are dominant, but this allows for the location of the entities to be explicitly chosen rather than calculating coordinates.
Each tile is one of five TileTypes, each mapped to a CollisionType.
Passable ground the player walks and stands on. Collision type SOLID.
A solid tile that blocks movement. Collision type SOLID.
A support surface the player can stand on. Collision type PLATFORM.
Passable, but triggers damage on contact via the player's CombatStatsComponent. Collision type
HAZARD.
Visual-only scenery with no collision. Collision type NONE.
Tile differences
| Tile | Solid | Damages | Drawn |
|---|---|---|---|
FLOOR |
yes | no | yes |
WALL |
yes | no | yes |
PLATFORM |
yes | no | yes |
HAZARD |
no | yes | yes |
DECORATIVE |
no | no | yes |
Layer rows are written top-to-bottom for readability; the loader flips them so the game uses a
bottom-left origin — the top row in the file becomes the highest y, the bottom row becomes y = 0.
Spawn coordinates are already in this world convention (x right, y up).
flowchart LR
subgraph File["File rows (top → bottom)"]
direction TB
r0["row 0 (top)"]
r1["row 1"]
r2["row 2 (bottom)"]
end
subgraph World["World tiles (y up)"]
direction TB
y2["y = 2"]
y1["y = 1"]
y0["y = 0"]
end
r0 --> y2
r1 --> y1
r2 --> y0
There are two ways to bring a map into the game.
1. Play a map as a game area — LevelGameArea loads the map, renders it, builds tile colliders,
and spawns the player and enemies from the map's spawn data:
TerrainFactory terrainFactory = new TerrainFactory(renderer.getCamera());
LevelGameArea area = new LevelGameArea(terrainFactory, "maps/demo.json");
area.create();2. Build just the terrain from already-parsed map data (e.g. for a custom area):
LevelMapData map = new JsonMapLoader().load("maps/demo.json");
TerrainComponent terrain = terrainFactory.createTerrainFromMap(map);Each MapLayerData becomes one tile layer, drawn back-to-front, with tiles typed by their
TileType. The map's tile textures must be loaded into the ResourceService first.
Source:
-
source/core/src/main/com/csse3200/game/areas/terrain/map/(data model and loader), -
areas/terrain/TileType.javaandCollisionType.java(tile definitions), and -
areas/LevelGameArea.java(the game area). Unit tests:src/test/.../areas/terrain/map/.
{ "name": "Demo Level", "tileSize": 0.5, // world units per tile "legend": { // symbol -> tile definition "#": { "type": "WALL", "texture": "images/wall_px.png" }, ".": { "type": "DECORATIVE","texture": "images/bg_decor_px.png" }, "=": { "type": "PLATFORM", "texture": "images/platform_px.png" }, "^": { "type": "HAZARD", "texture": "images/hazard_px.png" } }, "entityLegend": { "P": {"type": "PLAYER"}, "S": {"type": "ENEMY","enemyType": "skeleton"}, "R": {"type": "ENEMY","enemyType": "ranged-skeleton"}, "L": {"type": "LOOT"} }, "layers": { // drawn back-to-front; rows top->bottom "background": ["....", "...."], "terrain": ["####", "# #"], "entities: [" S L P "] }, }