Repository navigation
Core Livelli JSON
I livelli usano il formato del level editor: categories contiene i nomi delle categorie e objects le regioni da convertire in oggetti di gioco. LevelData legge e valida il documento, converte le geometrie e applica le impostazioni della scena. Nei quattro prototipi LevelLoader::load() legge il documento e crea la scena; loadJson() riceve la scena e il LevelData già letto, poi crea gli oggetti specifici del gioco.
I livelli inclusi sono:
CustomPlatformer/levels/overworld.jsonCustomPlatformerSlopes/levels/1-1.jsonBox2DPlatformer/levels/level0.jsonActionRPG/levels/overworld.json
Tutti e quattro i prototipi caricano levels/<name>.json dalla cartella dell'eseguibile e usano lo stesso formato: scene, categories e objects. Nuovi file possono essere caricati passando il loro nome a LevelLoader::load(). Cambiano le categorie e le proprietà specifiche del gioco, non la struttura del documento.
LevelData è un modulo del core condiviso da loader ed editor. Conserva il documento come nlohmann::ordered_json, controlla il formato comune e converte le geometrie. Ogni LevelLoader resta nel proprio progetto e decide quali classi creare e come interpretare le proprietà di gioco.
file JSON -> LevelData -> LevelLoader::loadJson() -> oggetti della scena
^
|
editor (lettura e salvataggio)
| Componente | Responsabilità |
|---|---|
LevelData |
Lettura, validazione del formato, conversione di rettangoli e polilinee, accesso ai dati della scena. |
LevelLoader::load(name) |
Scelta del file, creazione di LevelData e della scena, chiamata a loadJson(), restituzione della Scene*. |
LevelLoader::loadJson(world, level) |
Applicazione della configurazione, creazione delle entità secondo categoria e proprietà, collegamenti di gameplay. |
| Costruttori delle entità | Sprite e comportamento propri della classe: per esempio Lift richiede platform e Gear richiede gear alla factory. |
EditorScene e EditableObject
|
Modifica di geometrie, categorie e nomi opzionali; conservazione delle altre proprietà durante il salvataggio. |
Il core non istanzia Mario, Link o un body Box2D. Non serve aggiungere un metodo di caricamento JSON a ogni classe di gioco: l'associazione tra categoria e costruttore rimane leggibile nel loader.
| Metodo | Uso |
|---|---|
LevelData(path) |
Legge e valida il documento; segnala gli errori con il percorso del file. |
json(), objects()
|
Accedono al documento completo e alle regioni. |
category(object) |
Risolve l'indice category nel corrispondente nome in categories. |
sceneRect(), pixelUnitSize(), timeStep()
|
Forniscono i parametri per costruire la scena. Richiedono il blocco scene. |
configure(world) |
Registra il percorso JSON nella scena e applica view e backgroundColor, se presenti. Non ricostruisce la scena né crea oggetti. |
rect(json) |
Converte il contenuto di un campo rect in RectF. |
rotRect(object) |
Legge rect oppure rotRect da un oggetto e restituisce RotatedRectF. |
multiline(json) |
Converte il contenuto del campo multiline in punti. |
save(path, json) |
Valida il documento e lo scrive su disco. |
I quattro prototipi usano questo schema in load() (ActionRPG istanzia RPGGameScene al posto di PlatformerGameScene):
Scene* LevelLoader::load(const std::string& name)
{
std::string path = std::string(SDL_GetBasePath()) + "levels/" + name + ".json";
LevelData level(path);
PlatformerGameScene* world = new PlatformerGameScene(
level.sceneRect(), level.pixelUnitSize(), level.timeStep());
try
{
loadJson(world, level);
}
catch (...)
{
delete world;
throw;
}
return world;
}Anche ActionRPG descrive nel JSON la scena, le immagini di sfondo e gli spawn di NPC, Soldier e Link, oltre a collider e portali. loadJson() crea prima Link, così i portali possono osservarlo indipendentemente dall'ordine delle regioni. Se il caricamento fallisce dopo la creazione della scena, i loader eliminano la scena parziale e propagano l'errore.
{
"core_version": "3.7.3",
"categories": ["Static", "Mario"],
"scene": {
"rect": {"x": 0, "y": -20, "width": 224, "height": 50, "yUp": false},
"pixelUnitSize": {"x": 16, "y": 16},
"dt": 0.01,
"backgroundColor": [92, 148, 252, 255],
"view": {"x": 0, "y": -12, "width": 16, "height": 15, "yUp": false}
},
"objects": [
{
"category": 0,
"rect": {"x": 0, "y": 1, "width": 68, "height": 2, "yUp": false},
"sprite": "terrain",
"layer": 0
},
{
"category": 1,
"rect": {"x": 2.5625, "y": 0, "width": 1, "height": 1, "yUp": false}
}
]
}scene è obbligatorio in tutti e quattro i prototipi e contiene rect, pixelUnitSize e dt. backgroundColor (RGBA) e view sono opzionali. dt è il passo fisico in secondi: 0.01 corrisponde a 100 passi al secondo di tempo simulato, indipendentemente dal refresh del monitor. core_version è un metadato scritto dall'editor, non un selettore di formato.
Ogni oggetto ha category (indice in categories) e una sola geometria:
-
rect:x,y,width,height,yUp; posizione e dimensioni del rettangolo. -
rotRect:cx,cy,width,height,angle,yUp; centro, dimensioni e angolo in gradi.LevelData::rotRect()converte l'angolo in radianti per il gioco. -
multiline: array di punti{"x": ..., "y": ...}, almeno due. I segmenti di lunghezza nulla vengono saltati dai loader.
name è opzionale: normalmente basta la categoria. Si usa quando serve identificare una regione specifica, come i due portali "A" di ActionRPG, collegati dal loader tramite il nome. Gli attributi come sprite, layer e range sono proprietà autonome e non richiedono un nome. L'editor mostra l'etichetta solo se presente e omette name dal salvataggio quando è vuoto.
| Gioco / categoria | Geometria | Proprietà aggiuntive |
|---|---|---|
CustomPlatformer / Static
|
Rettangolo allineato agli assi |
sprite opzionale, layer (default 0) |
CustomPlatformer / HammerBrother
|
Rettangolo del personaggio |
layer; la posizione viene convertita nello spawn atteso dal costruttore |
CustomPlatformer / Lift
|
Rettangolo allineato agli assi |
vertical (default true), range (default 3), layer; il costruttore richiede lo sprite platform
|
CustomPlatformer / Mario
|
Rettangolo del personaggio |
layer; deve essere presente esattamente un Mario |
CustomPlatformerSlopes / Slope
|
Rettangolo allineato agli assi, yUp: false
|
risesRight (default true), color RGBA, layer (default 1); triangolo statico senza sprite |
CustomPlatformerSlopes / Platform
|
multiline con segmenti orizzontali |
layer; piattaforma attraversabile dai lati e dal basso |
CustomPlatformerSlopes / Block, Brick, Wood
|
Rettangolo dello spawn |
content: stringa vuota, coin, powerup, life o starman; layer
|
CustomPlatformerSlopes / Static, Pipe, Lift
|
Rettangolo allineato agli assi, yUp: false
|
sprite, color, layer; per Lift anche vertical e range positivo |
CustomPlatformerSlopes / Mario, Goomba, Coin
|
Rettangolo del personaggio o oggetto | Posizione di spawn e layer; esattamente un Mario |
Box2DPlatformer / Renderable
|
rect o rotRect
|
sprite opzionale, layer; decorazione senza fisica |
Box2DPlatformer / Terrain
|
multiline |
layer; un collider per segmento, la grafica del terreno è in backgroundImages
|
Box2DPlatformer / Static
|
rect o rotRect
|
sprite opzionale, layer
|
Box2DPlatformer / Gear
|
rect o rotRect
|
layer; il costruttore richiede lo sprite gear
|
Box2DPlatformer / Box
|
rect o rotRect
|
layer; mantiene lo sprite e l'impulso iniziale del costruttore |
Box2DPlatformer / Slime
|
rect o rotRect
|
layer; il centro indica lo spawn |
Box2DPlatformer / Player
|
rect o rotRect
|
layer; il centro indica lo spawn, deve essere presente esattamente un player |
ActionRPG / Static, Bush
|
rect o rotRect
|
Collider statico, senza sprite |
ActionRPG / Static, Cliff
|
multiline |
Un collider statico per segmento valido |
ActionRPG / Portal
|
rect o rotRect
|
name non vuoto; collega la coppia con lo stesso nome |
ActionRPG / Clipper
|
rect o rotRect
|
Regione di clipping del rendering |
ActionRPG / NPC
|
rect o rotRect
|
La posizione del rettangolo indica lo spawn |
ActionRPG / Soldier
|
rect o rotRect
|
Posizione di spawn e patrolRect, con gli stessi campi di rect
|
ActionRPG / Link
|
rect o rotRect
|
La posizione del rettangolo indica lo spawn; deve essere presente esattamente un Link |
I personaggi mantengono dimensioni e collider definiti dalle rispettive classi; il rettangolo nell'editor ne determina la posizione. CustomPlatformer supporta rettangoli allineati agli assi: rotazioni e polilinee richiederebbero un supporto fisico aggiuntivo e vengono segnalate come errori.
In CustomPlatformerSlopes le rampe sono descritte dal rettangolo che le delimita e da risesRight; il loader costruisce la classe Slope. I segmenti di Platform devono essere orizzontali. La scena e i rettangoli usano yUp: false; non sono supportate rampe mobili o ruotate. L’editor conserva risesRight, color e content, che si impostano nel JSON. Vedi Scena e risorse di CustomPlatformerSlopes.
In ActionRPG, Static e Bush creano collider StaticObject, Clipper crea una maschera di rendering e Portal crea un portale da collegare tramite name. Le polilinee vengono convertite in segmenti statici. Le categorie NPC, Soldier e Link definiscono gli spawn dei personaggi; Soldier legge anche patrolRect. Dimensioni fisiche, sprite, dialoghi e comportamento restano nelle rispettive classi. Vedi Scena e risorse di ActionRPG.
Il trigger di CustomPlatformer è un esempio hardcoded nel loader: dopo aver creato Mario e gli ascensori, viene istanziato in RectF(1, -12, 0.5f, 13) passando il giocatore e una lambda che alterna lo stato freezed degli ascensori. Trigger resta generico e riceve un task std::function<void()>, come negli altri prototipi. In CustomPlatformer il task viene eseguito all'ingresso e all'uscita dell'oggetto osservato; in Box2DPlatformer e ActionRPG soltanto all'ingresso, secondo il comportamento originale. Il trigger di esempio non è una regione del JSON e non richiede ID, azioni o riferimenti da risolvere.
Le classi specifiche scelgono lo sprite nel proprio costruttore. Il campo sprite rimane per gli oggetti generici, come StaticObject e RenderableObject, e per le immagini di sfondo.
In CustomPlatformerSlopes, Box2DPlatformer e ActionRPG le immagini fisse del mondo sono nell’array principale backgroundImages: rispettivamente lo sfondo di 1-1, il terreno del platformer e le mappe di Link. Per esempio:
{
"rect": {"x": 0, "y": 0, "width": 96, "height": 13, "yUp": true},
"sprite": "fg_ground",
"layer": -1
}Queste immagini non hanno categoria né nome; accettano rect o rotRect nei limiti geometrici del prototipo, uno sprite e un layer (default -1 in Box2DPlatformer e ActionRPG, 0 in CustomPlatformerSlopes). Restano visibili nel gioco e nell'editor, senza generare rettangoli modificabili che coprano i collider. In Box2DPlatformer il profilo fisico del terreno rimane una multiline di categoria Terrain in objects.
In Box2DPlatformer gli sfondi e gli effetti con parallasse sono nell'array principale overlays, nell'ordine di disegno:
{
"placement": "background",
"sprite": "bg_houses",
"parallax": {"x": 0.2, "y": 1},
"seamless": true
}placement può essere background o foreground. parallax ha default (0,0) e seamless ha default false; gli sfondi ripetuti richiedono uno sprite compatibile (FilledSprite).
Esc disattiva lo strumento o la selezione corrente e annulla le operazioni di trascinamento; durante la rinomina annulla il testo inserito. Non chiude l'applicazione. Ctrl+S salva; Q, in modalità normale, salva ed esce dall'editor.
Il tasto E apre il JSON effettivamente caricato dal livello. L'editor modifica geometrie, categorie ed etichette e conserva le altre proprietà degli oggetti e del documento, comprese impostazioni della scena e sfondi. Quando crea un nuovo documento, l'editor include scene ricavandolo dalla scena di gioco. Le proprietà specifiche del gioco si impostano direttamente nel JSON; non è stato aggiunto un pannello delle proprietà.
CMake copia i JSON accanto all'eseguibile a ogni build, anche senza modifiche C++. L'editor salva quella copia di esecuzione: riportare le modifiche nel file sorgente del progetto prima della build successiva, che ricopia i livelli distribuiti. Per applicare un livello modificato occorre ricaricarlo o riavviare il gioco.
File mancanti, documenti malformati, categorie fuori intervallo e geometrie non valide producono un errore esplicito. Non è previsto un formato alternativo senza scene.
LevelData controlla che le categorie siano un array non vuoto di stringhe, gli indici siano validi e ogni oggetto contenga una sola geometria. Verifica coordinate finite, rettangoli validi, polilinee con almeno due punti e il blocco obbligatorio scene (dt positivo, unità positive intere, colore RGBA valido).
Le regole specifiche restano nei loader: categoria supportata, sprite esistente, presenza di un solo giocatore in ciascun prototipo, intervallo positivo di un Lift, nome dei portali. backgroundImages è interpretato dai loader di CustomPlatformerSlopes, Box2DPlatformer e ActionRPG; overlays dal loader di Box2DPlatformer. LevelData conserva questi dati senza creare automaticamente oggetti grafici.
-
LevelData.heLevelData.cpp core/levels.mdCustomPlatformer/levels/overworld.jsonCustomPlatformerSlopes/levels/1-1.jsonBox2DPlatformer/levels/level0.jsonActionRPG/levels/overworld.json
Per lavorare sulle geometrie, prosegui con Editor dei livelli. Per seguire la creazione delle entità, leggi le pagine scena e risorse di CustomPlatformer, CustomPlatformerSlopes, Box2DPlatformer e ActionRPG.
- Panoramica
- Diagramma delle classi
- Architettura e game loop
- Sequenza di un frame
- Input ed eventi
- Scene e oggetti
- Rendering e coordinate
- Sprite e testo
- UI e overlay
- Quadtree
- Servizi e asset
- Estendere il core
- Editor dei livelli
- LevelData e livelli JSON
- Shader