Repository navigation
CustomPlatformer Entita e Gameplay
Le classi fisiche descrivono come un oggetto si muove e incontra gli altri. Le entità di questa pagina decidono che cosa significa quel contatto per il gioco: cambiare animazione, attivare una piattaforma, colpire un nemico o mostrare il game over.
PlatformerGameScene::updateControls() legge lo stato della tastiera una volta per frame. Le frecce impostano la direzione con Mario::move(), spazio passa un booleano a jump() e Z a run(). Mario deriva da DynamicObject: il suo update(dt) esegue prima la fisica e le collisioni, poi aggiorna gli stati _walking, _running, _jumping, _attacking e lo sprite corrispondente. Il flip orizzontale usa direzione e ultima velocità non nulla per conservare l'orientamento quando il personaggio si ferma.
run(true) aumenta limite di velocità e accelerazione orizzontale quando Mario è a terra. jump(true) applica un impulso verticale soltanto se non è in aria; mentre il tasto rimane premuto usa una gravità ridotta, e al rilascio ripristina una gravità maggiore. La differenza produce un salto la cui traiettoria dipende dalla durata della pressione. grounded() e midair() arrivano dalla catena MovableObject/CollidableObject, descritta nella pagina Movimento e collisioni.
Il tasto A chiama Mario::attack(). Nel percorso normale crea una Sword, che è un DynamicObject temporaneo collegato a Mario. A ogni update la spada segue il personaggio, orienta lo sprite, ricostruisce il collider e ne varia la larghezza in funzione dell'avanzamento di AnimatedSprite. collidableWith() la limita ai nemici; collision() invoca Enemy::smash().
L'attacco non avvia un thread: Mario registra uno scheduler "attack_off" per la durata dell'animazione della spada. Alla scadenza disattiva lo stato d'attacco, chiede alla scena di eliminare la spada e azzera il puntatore. Una nuova pressione durante l'attacco riavvia l'azione e sostituisce la spada precedente. È un esempio concreto di scheduler degli oggetti.
Enemy è la base dei nemici dinamici. La sua collision() reagisce a Mario: se è possibile schiacciare il nemico e Mario è invincibile, chiama smash(); altrimenti chiama Mario::hurt(). smash() disabilita le collisioni, modifica moto e flip, riproduce un suono e programma la rimozione dopo due secondi. Nel costruttore di questo esempio Mario parte con _invincible = true: è una scelta dimostrativa da tenere presente quando si osservano gli scontri.
HammerBrother mostra un comportamento costruito con più scheduler: genera martelli periodicamente, tenta salti a intervalli e, dopo un ritardo, passa dall'oscillazione attorno al punto iniziale all'inseguimento. Hammer nasce vicino al lanciatore, poi uno scheduler attiva gravità, impulso, direzione e rotazione; collidableWith() lo limita a Mario. Sprite di camminata, lancio e salto dipendono dallo stato corrente, senza cambiare la classe base del motore.
Lift deriva da KinematicObject e oscilla entro un intervallo verticale o orizzontale. La classe base registra i DynamicObject in contatto e, durante l'update, ne corregge lo spostamento: chi si trova sopra viene trasportato insieme alla piattaforma, chi la urta da sotto o di lato riceve la correzione sull'asse pertinente. Il contatto controlla anche il passaggio temporaneo dal CCD al percorso AABB per l'oggetto trasportato.
Nel JSON gli ascensori appartengono alla categoria Lift, con proprietà vertical, range e layer. Il costruttore sceglie autonomamente lo sprite platform.
Trigger è un CollidableObject compenetrabile: riceve l'oggetto da osservare e un task std::function<void()>. Non conosce gli ascensori. LevelLoader::loadJson() raccoglie i Lift creati dal JSON e, al termine, mantiene il trigger di esempio hardcoded:
if (!lifts.empty())
new Trigger(world, RectF(1, -12, 0.5f, 13),
world->player()->to<CollidableObject*>(), [lifts]()
{
for (auto lift : lifts)
lift->toggleFreezed();
});La cattura per valore conserva la lista dei puntatori anche dopo il ritorno dal loader. Nel JSON non servono una regione Trigger, ID, azioni o riferimenti agli ascensori. Trigger::collision() filtra l'oggetto osservato ma non il parametro begin: esegue quindi il task anche alla notifica di fine contatto, secondo il comportamento originale. In Box2DPlatformer e ActionRPG il trigger generico riceve lo stesso tipo di task, ma lo esegue solo all'ingresso.
Mario::die() disabilita le sue collisioni, ferma la musica, riproduce il suono e chiama PlatformerGame::freeze(true), che congela gli oggetti della scena di gioco tranne il player. Due azioni programmate gestiscono l'animazione di morte e poi chiedono gameover(). Questa funzione inserisce nello stack una UIScene con l'immagine di game over e riproduce la musica. È un esempio di collaborazione tra oggetto, servizio audio e gestione globale delle scene.
L'HUD e i menu sono descritti in Scena e risorse. Per seguire una modifica nel debugger, parti da PlatformerGameScene::updateControls() per l'input, passa a Mario::update() per lo stato e termina in CollidableObject::collision() e nei metodi collision() delle entità per gli effetti sul gameplay.
- 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