Map Format - UQcsse3200/2026-studio-1 GitHub Wiki
Map Format
How a level map file is built, what every symbol means, and how entities on a map become things in the game.
All maps live in source/core/assets/maps/. template.json in that folder is a small working map with one of everything. Copy it to start a new one.
File structure
{
"name": "Level 1 - Out of the Underworld",
"tileSize": 0.5,
"backgroundTexture": "images/level1/level1-map.png",
"legend": { "#": { "type": "WALL", "texture": "images/level1/cavern-rock-512px.png" } },
"entityLegend": { "P": { "type": "PLAYER" } },
"lighting": {"ambient": {"color": ..., "intensity": ...}, "player": {"radius": ..., "color": ...}}
"layers": {
"background": ["..."],
"terrain": ["..."],
"foreground": ["..."],
"entities": ["..."]
},
"backdrops": {"sublvl1": [...]}
"subLevels": [ ... ],
"transitions": [ ... ]
}
| Key | Required | What it is |
|---|---|---|
name |
yes | Shown on the level title card and in logs. |
tileSize |
no | World size of one tile. Every map uses 0.5. |
backgroundTexture |
no | One image stretched over the whole map, behind the tile layers. Draw it at 16 pixels per tile, so a 56 by 64 tile map needs an 896 by 1024 image. If the file does not exist yet, the map still loads and a warning is logged. |
legend |
yes | Tile symbols used in background, terrain and foreground. See Tile legend. |
entityLegend |
no | Symbols used in the entities layer. See Entity legend. |
lighting |
no | Ambient and player lighting for the map. |
layers |
yes | The grids. See Layers. |
backdrops |
no | The backdrop overlays that are animated and move behind the actual map. |
subLevels |
no | Named sections of one map, such as Level 1's dungeon and Nether. Sets the camera bounds and the title shown when crossing between them. |
transitions |
no | Doorways to another map. |
Layers
Each layer is a list of strings: one string per row, one character per tile. A space is an empty tile.
| Layer | Symbols from | What it does |
|---|---|---|
background |
legend |
Art drawn behind everything. No collision. |
terrain |
legend |
Art the player stands on and bumps into. This is the collision layer. |
foreground |
legend |
Art drawn in front of the terrain. No collision. |
entities |
entityLegend |
Where the player, enemies and loot start. Not drawn. |
Two rules catch most mistakes:
- Rows go top to bottom in the file, but
y = 0is the bottom row in the game. The loader flips the rows, so the last string in a layer is the floor. legendandentityLegendare separate. The same letter can mean one thing as a tile and another as an entity. In Level 1,Sis the River Styx inlegendand a skeleton inentityLegend.
Every layer must have the same number of rows. If the entities layer does not match, the map refuses to load.
Tile legend
Each entry gives a tile type and a texture:
"#": { "type": "WALL", "texture": "images/level1/cavern-rock-512px.png" }
| Type | Collision |
|---|---|
FLOOR |
solid |
WALL |
solid |
PLATFORM |
one-way platform, can jump up through it |
HAZARD |
hurts on contact |
LADDER |
climbable, no collision |
DECORATIVE |
none, art only |
Tile properties
Any other key on a legend entry is kept as a property of that tile. The map system does not use it. The feature that needs it reads it back.
"~": { "type": "HAZARD", "texture": "images/level1/river-styx-512px.png", "damage": "15" }
TileDefinition tile = level.tileAt(x, y);
int damage = tile.getInt("damage", 10); // 10 if missing or not a number
boolean secret = tile.flag("hidden"); // false if missing
| Property | Used by | Effect |
|---|---|---|
damage |
Hazard tiles | Health lost on contact. Defaults to 10 when not set. |
Use a property for new tile behaviour instead of adding a new type.
Entity legend
"entityLegend": {
"P": { "type": "PLAYER" },
"S": { "type": "ENEMY", "enemyType": "skeleton" },
"R": { "type": "ENEMY", "enemyType": "ranged-skeleton" },
"L": { "type": "LOOT" }
}
| Type | Extra keys | What spawns |
|---|---|---|
PLAYER |
none | The player. One per map. |
ENEMY |
enemyType |
The enemy registered under that name. See Enemy names. |
LOOT |
none | A spot where loot is placed. The item itself is rolled from the loot table. |
MARKER |
kind (required), id and anything else |
Nothing by itself. Marks a spot for other code to read. See Markers. |
Enemy names
These are the values enemyType accepts. Names ignore case. A name that is not listed here does not crash the level: that enemy is skipped and a warning is logged.
| Enemy | Names | Built by |
|---|---|---|
| Skeleton | skeleton, enemy-skeleton-hoplite |
NPCFactory.createSkeleton. |
| Ranged skeleton | ranged-skeleton, rangedskeleton |
NPCFactory.createRangedSkeleton. |
| Minotaur | minotaur |
NPCFactory.createMinotaur. Crashes when spawned: NPCs.json has no charge block for it. |
| Centaur | centaur, enemy-centaur |
NPCFactory.createCentaur. Crashes when spawned: NPCs.json has no charge block for it. |
| Cyclops | cyclops |
NPCFactory.createCyclops. Crashes when spawned: its ranged cooldown in NPCs.json is shorter than its weapon's wind-up. |
| Ghost | ghost |
NPCFactory.createGhost. Not for use in maps, does not fit the theme. |
| Ghost king | ghostking, ghost_king |
NPCFactory.createGhostKing. Does not fit the theme. Used as Level 3's boss for now, until Minotaur or Cyclops is fixed. |
| Traveler (NPC) | npc:traveler |
NPCFactory.createTravelerNPC. |
Use the first name listed for each. The others are kept so older maps still load.
How a symbol becomes an enemy
The map never picks an entity's sprite. It only says what goes where:
- The
entitieslayer has anSat some tile. entityLegendsaysSis anENEMYnamedskeleton.- When the level loads,
LevelGameAreaasksEntitySpawnRegistryto buildskeleton. - The registry calls the factory registered for that name,
NPCFactory.createSkeleton. - The factory builds the enemy, including its sprite, animations, AI and health.
To change how an enemy looks or behaves, change its factory, not the map.
Adding a new enemy
- Write a factory method that takes the player entity and returns the new enemy, like the ones in
NPCFactory. - Add one line to
DefaultEntitySpawns:EntitySpawnRegistry.registerIfAbsent("harpy", NPCFactory::createHarpy); - In a map, add a symbol for it and place that symbol in the
entitieslayer:"H": { "type": "ENEMY", "enemyType": "harpy" }
No map system code changes.
Markers
A marker puts a named spot on the map for another system to use: an NPC, a pet, a checkpoint, a light. The map system reads it and hands it over. It does not spawn anything.
"N": { "type": "MARKER", "kind": "npc", "id": "traveler" }
for (Marker marker : level.markers("npc")) {
// marker.id(), marker.position(), marker.get("anyKey")
}
kind groups markers and is required. id and any other keys are optional. Positions are in tiles, so multiply by level.tileSize() for world units. No map uses markers yet.
Backdrops
The optional backdrops section defines parallax image layers rendered behind the tiles and characters.
Each key identifies a sub-level ID, or "*" to apply the backdrop to the whole map. Each value is an array of layers, ordered back to front.
"backdrops": {
"*": [
{
"texture": "images/backgrounds/sky.png",
"scroll": 0.1
},
{
"texture": "images/backgrounds/mountains.png",
"scroll": 0.3,
"alpha": 0.8
}
],
"dungeon": [
{
"texture": "images/backgrounds/dungeon.png",
"scroll": 0.0
}
]
}
Layer Properties
| Property | Required | Description |
|---|---|---|
texture |
Yes | Path to the image asset. |
scroll |
No | Horizontal parallax scrolling factor; defaults to 0. |
drift |
No | Object with optional x and y drift values; both default to 0. |
spansMap |
No | Whether the layer spans the map; defaults to false. |
rows |
No | Object with from and to tile-row bounds. |
alpha |
No | Layer opacity from 0 to 1; defaults to 1. |
flicker |
No | Optional animation settings for a strip of frames, useful for distant lightning. |
Flicker Properties
The optional flicker object supports the following properties:
| Property | Default | Description |
|---|---|---|
frames |
1 |
Number of animation frames. |
fps |
12 |
Frames per second. |
minGap |
3 |
Minimum delay between flickers. |
maxGap |
8 |
Maximum delay between flickers. |
height |
0.35 |
Relative height of the flicker strip. |
Notes
- Every layer must specify a valid
texturepath. - Use
"*"for a whole-map backdrop, or a sub-level ID to define a backdrop for that section. - Layers are drawn in array order, from back to front.
- The separate
overlayssection uses the same format but draws layers in front of tiles and characters, for effects such as rain. backgroundTextureis a separate option for a single image stretched across the entire map.
Transitions
The optional transitions section defines doorway or portal regions that move the player to another map when entered.
Each transition is a rectangular trigger area specified in tile coordinates. An optional texture can be rendered for the transition entity.
"transitions": [
{
"id": "exit-to-level2",
"x": 4,
"y": 62,
"width": 1,
"height": 1,
"texture": "images/gate.png",
"destinationMap": "maps/level2.json",
"destinationSpawn": {
"x": 2,
"y": 2
}
}
]
Transition Properties
| Property | Required | Description |
|---|---|---|
id |
No | Unique identifier for the transition. Defaults to transition-N if omitted. |
x |
Yes | Tile x-coordinate of the transition trigger area. |
y |
Yes | Tile y-coordinate of the transition trigger area. |
width |
No | Width of the trigger area in tiles. Defaults to 1. |
height |
No | Height of the trigger area in tiles. Defaults to 1. |
texture |
No | Texture displayed for the transition entity. |
destinationMap |
Yes | Path to the destination map file. |
destinationSpawn |
No | Spawn location within the destination map. If omitted, the destination map's default player spawn is used. |
Destination Spawn Properties
| Property | Required | Description |
|---|---|---|
x |
Yes | Destination tile x-coordinate. |
y |
Yes | Destination tile y-coordinate. |
Notes
destinationMapmust reference a valid map JSON file.- Transition coordinates use map tile coordinates, not world coordinates.
- Trigger regions may span multiple tiles using the
widthandheightproperties. - Transition textures are optional; invisible transition zones are supported.
- Transitions outside the map bounds generate a warning but do not prevent the map from loading.
- If `destinationSpawn
Reading a level from code
Use LevelView rather than reading map data directly. It keeps working when the file format changes.
LevelView level = levelGameArea.getLevel();
level.tileAt(x, y); // the tile and its properties, or null
level.isSolid(x, y);
level.isSupporting(x, y); // floors, walls and one-way platforms
level.isWalkable(x, y);
level.isHazard(x, y);
level.groundTiles(); // open tiles with solid footing
level.markers("npc");
Reading outside the map returns null or false instead of throwing.
Checking a map
ShippedMapsTest loads every file in assets/maps on every push. The build fails if a map has no terrain layer, a spawn or doorway outside the map, a doorway to a map that does not exist, or a texture that is missing. Run it locally from source/:
./gradlew :core:test --tests "*ShippedMapsTest*"
Symbols by level
Generated from the map files. If you change a map's legend, update its table here.
Level 1: Out of the Underworld
File: level1-greek.json
Level Configuration
| Property | Value |
|---|---|
| Name | Level 1 - Out of the Underworld |
| Tile Size | 0.5 |
| Sublevels | dungeon, nether |
Entities
| Symbol | Type | Spawns / ID |
|---|---|---|
P |
PLAYER |
|
S |
ENEMY |
skeleton |
R |
ENEMY |
ranged-skeleton |
M |
ENEMY |
minotaur |
Y |
ENEMY |
cyclops |
C |
ENEMY |
centaur |
D |
ENEMY |
cerberus |
L |
LOOT |
|
* |
MARKER |
npc:shop |
2 |
MARKER |
npc:wizard |
Tiles
| Symbol | Type | Texture | Notes |
|---|---|---|---|
u |
DECORATIVE |
images/level1/bg-tunnel-512px.png |
|
# |
WALL |
images/level1/cavern-rock-512px.png |
|
V |
WALL |
images/level1/cavern-rock-veined-512px.png |
|
^ |
WALL |
images/level1/cavern-ceiling-512px.png |
|
_ |
FLOOR |
images/level1/cavern-floor-ash-512px.png |
|
% |
FLOOR |
images/level1/cavern-floor-sulfur-512px.png |
|
- |
PLATFORM |
images/level1/ledge-basalt-512px.png |
|
= |
PLATFORM |
images/level1/ledge-bone-512px.png |
|
~ |
HAZARD |
images/level1/river-phlegethon-512px.png |
Point light: lava_glow, radius 1.1, flicker 0.2 |
e |
HAZARD |
images/level1/hazard-embers-512px.png |
Point light: lava_glow, radius 0.8, flicker 0.4 |
W |
WALL |
images/level1/wall-limestone-512px.png |
|
w |
WALL |
images/level1/wall-limestone-cracked-512px.png |
|
) |
DECORATIVE |
images/level1/enemy-keres-512px.png |
|
m |
WALL |
images/level1/wall-limestone-mossy-512px.png |
|
F |
FLOOR |
images/level1/floor-limestone-512px.png |
|
M |
FLOOR |
images/level1/floor-marble-512px.png |
|
P |
PLATFORM |
images/level1/platform-marble-512px.png |
|
p |
PLATFORM |
images/level1/platform-wood-512px.png |
|
D |
DECORATIVE |
images/level1/frieze-meander-512px.png |
|
X |
HAZARD |
images/level1/hazard-spikes-bronze-512px.png |
|
L |
HAZARD |
images/level1/hazard-firepit-512px.png |
Point light: fire_glow, radius 1.5, flicker 0.4 |
O |
HAZARD |
images/level1/hazard-pit-512px.png |
Defined but not placed in the terrain layer |
l |
LADDER |
images/level1/ladder-wood-512px.png |
|
r |
LADDER |
images/level1/ladder-bronze-512px.png |
|
i |
DECORATIVE |
images/level1/lift-cable-512px.png |
|
C |
WALL |
images/level1/crust-seam-512px.png |
|
T |
WALL |
images/level1/tunnel-wall-512px.png |
|
t |
FLOOR |
images/level1/tunnel-floor-512px.png |
|
s |
PLATFORM |
images/level1/tunnel-step-512px.png |
|
@ |
DECORATIVE |
images/player/hero-hoplite-512px.png |
|
! |
DECORATIVE |
images/level1/decor-stele-512px.png |
|
J |
DECORATIVE |
images/level1/hero-fallen-512px.png |
|
Y |
DECORATIVE |
images/level1/item-xiphos-512px.png |
|
A |
DECORATIVE |
images/level1/decor-oil-lamp-512px.png |
Point light: oil_lamp, radius 1.5, flicker 0.1 |
Q |
DECORATIVE |
images/level1/decor-amphora-512px.png |
|
U |
DECORATIVE |
images/level1/decor-urn-512px.png |
|
K |
DECORATIVE |
images/level1/hazard-swinging-blade-512px.png |
|
R |
DECORATIVE |
images/level1/decor-column-doric-512px.png |
|
B |
DECORATIVE |
images/level1/decor-bone-pile-512px.png |
|
o |
DECORATIVE |
images/level1/item-obol-512px.png |
|
c |
DECORATIVE |
images/level1/item-chest-512px.png |
|
d |
DECORATIVE |
images/level1/item-phial-red-512px.png |
|
q |
DECORATIVE |
images/level1/decor-gorgoneion-512px.png |
|
+ |
DECORATIVE |
images/level1/decor-tripod-512px.png |
Point light: elysian_fade, radius 1.5, flicker 0.2 |
k |
DECORATIVE |
images/level1/hazard-flame-jet-512px.png |
Point light: fire_glow, radius 1.1, flicker 0.4 |
: |
DECORATIVE |
images/level1/decor-chain-bronze-512px.png |
|
z |
DECORATIVE |
images/level1/item-pouch-512px.png |
|
N |
DECORATIVE |
images/level1/decor-olive-vine-512px.png |
|
a |
DECORATIVE |
images/level1/item-phial-olive-512px.png |
|
$ |
DECORATIVE |
images/level1/item-key-bronze-512px.png |
|
& |
DECORATIVE |
images/level1/item-bolt-512px.png |
|
H |
DECORATIVE |
images/level1/item-hoplon-512px.png |
|
y |
DECORATIVE |
images/level1/item-phial-blue-512px.png |
|
? |
DECORATIVE |
images/level1/decor-ember-vent-512px.png |
Point light: lava_glow, radius 1.1, flicker 0.4 |
h |
DECORATIVE |
images/level1/decor-stalagmite-512px.png |
|
j |
DECORATIVE |
images/level1/decor-stalactite-512px.png |
|
Z |
DECORATIVE |
images/level1/enemy-shade-512px.png |
|
; |
DECORATIVE |
images/level1/decor-column-broken-512px.png |
|
( |
DECORATIVE |
images/level1/enemy-cerberus-512px.png |
|
n |
DECORATIVE |
images/level1/item-laurel-512px.png |
|
g |
DECORATIVE |
images/level1/gate-exit-512px.png |
|
[ |
DECORATIVE |
images/level1/lift-car-512px.png |
|
] |
DECORATIVE |
images/level1/lift-winch-512px.png |
Level 2: Mount Olympus
File: level2.json
Entities
| Symbol | Type | Spawns |
|---|---|---|
P |
PLAYER |
Player |
S |
ENEMY |
skeleton |
R |
ENEMY |
ranged-skeleton |
M |
ENEMY |
minotaur |
Y |
ENEMY |
cyclops |
C |
ENEMY |
centaur |
L |
LOOT |
Loot |
G |
ENEMY |
medusa |
1 |
MARKER |
npc:philosopher |
3 |
MARKER |
npc:satyr |
H |
ENEMY |
harpy |
B |
ENEMY |
ranged-harpy |
D |
ENEMY |
cerberus |
Tiles
| Symbol | Type | Texture | Notes |
|---|---|---|---|
O |
WALL |
none | foothill-soil |
E |
WALL |
none | foothill-grass-edge |
Q |
WALL |
none | quarry-marble-cut |
R |
WALL |
none | quarry-rubble |
g |
WALL |
none | cliff-granite |
S |
WALL |
none | summit-marble |
I |
WALL |
none | summit-gold-inlay |
K |
PLATFORM |
none | cliff-shelf |
c |
ONE_WAY_PLATFORM |
none | cloud-solid |
d |
ONE_WAY_PLATFORM |
none | cloud-drift; defined but not placed |
f |
ONE_WAY_PLATFORM |
none | cloud-fade |
t |
ONE_WAY_PLATFORM |
none | cloud-thermal |
m |
ONE_WAY_PLATFORM |
none | cloud-storm |
P |
DECORATIVE |
none | cliff-piton |
V |
DECORATIVE |
none | summit-pillar-decoration |
X |
HAZARD |
none | rockslide |
Level 3: Zeus's Palace
File: level3.json
Entities
| Symbol | Type | Spawns |
|---|---|---|
P |
PLAYER |
|
Z |
ENEMY |
ghostking |
L |
LOOT |
Tiles
| Symbol | Type | Texture | Notes |
|---|---|---|---|
# |
WALL |
summit-marble-512px.png |
|
G |
WALL |
summit-gold-inlay-512px.png |
|
F |
WALL |
floor-marble-512px.png |
|
b |
DECORATIVE |
bg-sky-summit-512px.png |
|
| |
DECORATIVE |
decor-column-doric-512px.png |
|
q |
DECORATIVE |
decor-gorgoneion-512px.png |
|
t |
DECORATIVE |
decor-tripod-512px.png |
|
o |
DECORATIVE |
decor-oil-lamp-512px.png |
|
v |
DECORATIVE |
decor-shrine-votive-512px.png |
|
^ |
DECORATIVE |
cloud-storm-512px.png |
Footnote
Click for more information on lighting format