È passato un po’ dal mio ultimo devlog, ma finalmente ne ho scritto uno nuovo. È pieno di cose interessanti quindi l’attesa si farà ripagare. Let us ncJump into it!
Dying state
Sicuramente uno degli stati fondamentali. Ogni entità vivente dovrebbe morire a un certo punto, e questo stato rappresenta quel lasso di tempo in cui un’animazione dovrebbe essere mostrata per dire al giocatore che un nemico è sconfitto o che il gioco è finito. Il metodo di update di questo stato è semplicissimo:
void DyingState::update(Entity& entity)
{
if (entity.animation.has_finished()) {
entity.set_enabled(false);
// just reuse this entity for something else
}
}
Enabled flag
Questo ovviamente spiega il metodo Entity::set_enabled()
appena introdotto nella sezione precedente. Invocandolo, possiamo effettivamente disabilitare un’entità e tutte le sue componenti senza distruggere nulla, o rilasciare memoria, cosicché possa essere riciclata e riusata successivamente quando, per esempio, dobbiamo spawnare un’altra entità.
Array of states
Questa è stata un’importante ottimizzazione. Prima, ogni transizione di stato innescava la distruzione del vecchio stato e la creazione del nuovo stato, cosa non proprio necessaria e non scala col crescere della scena. Ora, lo state component tiene traccia di un array di tutti gli state objects possibili, insieme a un puntatore che ci indica quale sia lo stato corrente.
Initial position
Un altro requisito della scena consiste nel definire dove dovrebbe apparire il personaggio. La posizione iniziale è una coppia di coordinate che ci dice esattamente questo. Quando la scene inizia, mettiamo il personaggio lì.
Definitions and factories
Adoro questo pattern e penso che lo userò d’ora in poi per tutti i miei progetti. In pratica, abbiamo a che fare con oggetti grossi e pesanti non proprio facili da serializzare. Si pensi a dei wrapper per componenti third-party come Box2D bodies o nCine sprites. Ho provato a trovare un modo di serializzare questi oggetti e penso di aver trovato una soluzione niente male che in realtà è molto usata, quindi probabilmente ho solo reinventato la ruota qui, ma va bene.
Non serializzo questi oggetti. Serializzo le loro definizioni. Una definizione è una collezione di tutti i parametri necessari alla costruzione dell’oggetto. Prendiamo come esempio un graphics component di ncJump.
// Very simplified
auto def = GraphicsDef(GraphicsType::TILE);
def.path = String("image.png");
def.layer = 2;
auto gfx = GraphicsComponent(def);
Come si puo vedere, il costruttore del graphics component prende un graphics definition come parametro. Un graphics definition, come tutte le altre definizioni, e’ un Plain Old Data (POD) structure, che permette l’uso di nlohmann macros per definire automaticamente tutte quelle funzioni necessarie alla serializzazione. Fatto. Fantastico.
// That's it
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GraphicsDef, type, path, layer);
CI and automatic deploy
This will blown your mind, poiché il progetto ora compila automaticamente, mediante GitHub actions, per Linux, Window, MacOS, Android, e che ci crediate o no per il web. Infatti, cliccate qui e preparatevi a dire wow. Tutto il merito di questa roba qua va all’eccezionale autore dell’nCine: @encelo!