Vai al contenuto

Creare un videogioco: 4 componiamo la scena

In questo quarto articolo della serie “Creare un videogioco con Irrlicht” vedremo come caricare e posizionare tutti gli elementi che comporranno la scena di gioco, nello specifico useremo le funzioni per posizionare le mesh usando coordinate x,y,z  relative agli assi di riferimento. Particolare attenzione è rivolta alla creazione di un vincolo padre – figlio, usate per legare la posizione della torretta alla base.

Gli elementi che compongono tutta la scena sono:

  • Il campo di battaglia, che chiameremo “campo”
  • La base del carro armato blu, che chiameremo “baseBlu”
  • La torretta del carro armato blu, che chiameremo “torrettaBlu”
  • La base del carro armato rosso, che chiameremo “baseRosso”
  • La torretta del carro armato rosso, che chiameremo “torrettaRosso”
  • Potremmo caricare degli ostacoli nel campo di battaglia ma, almeno per ora, non lo facciamo.

Impostiamo un vincolo padre figlio su base e torretta dei carri affinchè muovendo la base, la torretta essendo solidale si muova anch’essa.

Di seguito il codice completo che andremo ad analizzare.

#include "irrlicht.h"
using namespace irr;

#pragma comment(lib, "irrlicht.lib")

int main()
 {
    // inizializzo il device impostando risoluzione 800x600 usando DirectX9
    IrrlichtDevice *device = createDevice(video::EDT_DIRECT3D9,
        core::dimension2d<u32>(800,600), 16, false, false, false);
	// Creo driver
    video::IVideoDriver* driver = device->getVideoDriver();
	// Creo il gestore di scena
    scene::ISceneManager* scenemgr = device->getSceneManager();
	// Tramite il device imposto il titolo della finestra
    device->setWindowCaption(L"Irrlicht tutorial by softgame.it");

	/* --------------------------------
		Per caricare tutta la scena dobbiamo caricare i seguenti elementi: 
		1) Campo di battaglia
		2) Base tank blu
		3) Torretta tank blu
		4) Base tank rosso
		5) Torretta tank rosso
		6) Eventuali barriere (opzionale)
		-------------------------------- */

	// 1) Carico in memoria la mesh del campo di battaglia
	scene::IAnimatedMesh* meshCampo = scenemgr->getMesh("field.x");
	// Creo il nodo che conterrà il campo di battaglia (campo)
	scene::ISceneNode* campo = scenemgr->addOctreeSceneNode(meshCampo->getMesh(0),0,-1,512);
	// Disabilito il lighting
	campo->setMaterialFlag(video::EMF_LIGHTING, false);
	// 2) Aggiungo alla scena la base del carro armato Blu
	scene::ISceneNode* baseBlu = scenemgr->addMeshSceneNode(scenemgr->getMesh("take.x"));
    // Se caricata correttamente la mesh procedo al caricamento della texture e
	// disabilito il lighting *1, setto la posizione di partenza a -100 -220 -100
    if (baseBlu)
    {
        baseBlu->setMaterialTexture(0, driver->getTexture("milwalll.bmp"));
        baseBlu->setMaterialFlag(video::EMF_LIGHTING, false);
		baseBlu->setPosition(core::vector3df(-100, -220, -100));
    }	
	// 3) Aggiungo alla scena la torretta del carro blu, e collego i due nodi rendendo la torretta nodo figlio della base
	scene::ISceneNode* torrettaBlu = scenemgr->addMeshSceneNode(scenemgr->getMesh("HeadBlue.x"));
	if (torrettaBlu)
    {
		torrettaBlu->setMaterialFlag(video::EMF_LIGHTING, false); //lighting disabilitato
		baseBlu->addChild(torrettaBlu); // Rendo il nodo baseBlu Padre e torrettaBlu nodo Figlio
		torrettaBlu->setPosition(core::vector3df(0, 60, 0)); //posiziono correttamente la torretta sulla base
	}	
	// 4) Aggiungo alla scena la base del carro armato Rosso
	scene::ISceneNode* baseRosso = scenemgr->addMeshSceneNode(scenemgr->getMesh("take.x"));
	if (baseRosso)
    {
        baseRosso->setMaterialTexture(0, driver->getTexture("milwalll.bmp"));
        baseRosso->setMaterialFlag(video::EMF_LIGHTING, false);
		baseRosso->setPosition(core::vector3df(300, -220, -600));
    }
	// 5) Aggiungo alla scena la torretta del carro armato Rosso
	scene::ISceneNode* torrettaRosso = scenemgr->addMeshSceneNode(scenemgr->getMesh("HeadRed.x"));
	if (torrettaRosso)
    {
		torrettaRosso->setMaterialFlag(video::EMF_LIGHTING, false); //lighting disabilitato
		baseRosso->addChild(torrettaRosso); // Rendo il nodo baseRosso Padre e nodeTurret nodo Figlio
		torrettaRosso->setPosition(core::vector3df(0, 60, 0)); //posiziono correttamente la torretta sulla base
	}
    // Aggiungo una camera di tipo FPS (si controlla tramite mouse e frecce)
	// Gli indico il target da inquadrare con le coord.
    scenemgr->addCameraSceneNodeFPS();
	scenemgr->getActiveCamera()->setPosition(core::vector3df(20.0f, 400.0f, -1000.0f));
	scenemgr->getActiveCamera()->setTarget(core::vector3df(20.0f, -500.0f, 0.0f));
    // Ciclo principale dove viene renderizzata la scena
    while(device->run() && driver)
    {
        driver->beginScene(true, true, video::SColor(255,0,0,255));
        scenemgr->drawAll();
        driver->endScene();
    }
    // Cancello il device prima di uscire
    device->drop();
    return 0;	
 }

Irrlicht è un framework che opera in tre dimensioni (3D), usa come riferimento il seguente sistema di assi dello spazio cartesiano:
assi2
Dove X rappresenta l’ascissa, Z l’ordinata e Y l’asse delle quote. Quindi X-Z come piano 2D ed Y come altezza. Alcuni corsi di studio utilizzano gli assi in modo diverso, per questo motivo dobbiamo fare attenzione e riferirci sempre allo schema mostrato e non alle nostre remiscenze di geometria.

Iniziamo ad analizzare il codice.
Dalla linea 1 alla 4 nulla di nuovo rispetto il precedente articolo, inclusione delle intestazioni di irrlicht, utilizzo del namespace irr e direttiva per il linker.
Alla linea 6 inizia il main del programma.
Dalla linea 9 alla 16 anche qui nulla di nuovo, inizializzo il device, poi il driver ed infine il gestore di scena (scenemgr).

Arriviamo finalmente alla nuova parte di codice:

// 1) Carico in memoria la mesh del campo di battaglia
scene::IAnimatedMesh* meshCampo = scenemgr->getMesh("field.x");
// Creo il nodo che conterrà il campo di battaglia (campo)
scene::ISceneNode* campo = scenemgr->addOctreeSceneNode(meshCampo->getMesh(0),0,-1,512);
// Disabilito il lighting
campo->setMaterialFlag(video::EMF_LIGHTING, false);

carichiamo la mesh del campo di battaglia usando un puntatore di ripo IAnimatedMesh che chiamiamo meshCampo.
Successivamente creiamo un nodo per la gesione del campo di battaglia e lo chiamiamo campo.
Come nell’esempio scorso disabilitiamo l’illiminazione del campo con l’istruzione alla riga 33.

// 2) Aggiungo alla scena la base del carro armato Blu
scene::ISceneNode* baseBlu = scenemgr->addMeshSceneNode(scenemgr->getMesh("take.x"));
// Se caricata correttamente la mesh procedo al caricamento della texture e
// disabilito il lighting *1, setto la posizione di partenza a -100 -220 -100
if (baseBlu)
{
    baseBlu->setMaterialTexture(0, driver->getTexture("milwalll.bmp"));
    baseBlu->setMaterialFlag(video::EMF_LIGHTING, false);
    baseBlu->setPosition(core::vector3df(-100, -220, -100));
}    
// 3) Aggiungo alla scena la torretta del carro blu, e collego i due nodi rendendo la torretta nodo figlio della base
scene::ISceneNode* torrettaBlu = scenemgr->addMeshSceneNode(scenemgr->getMesh("HeadBlue.x"));
if (torrettaBlu)
{
    torrettaBlu->setMaterialFlag(video::EMF_LIGHTING, false); //lighting disabilitato
    baseBlu->addChild(torrettaBlu); // Rendo il nodo baseBlu Padre e torrettaBlu nodo Figlio
    torrettaBlu->setPosition(core::vector3df(0, 60, 0)); //posiziono correttamente la torretta sulla base
}

Alla riga 36 creiamo un nodo chiamato baseBlu e gli assegnamo la mesh della base del carro blu.
Successivamente controlliamo che questo è stato creato e quindi che tutto è andato per il meglio, se così è allora procediamo a caricare la texture, disabilitiamo il lighting e lo posizioniamo alle coordinate -100, -220, -100. La texture non è altro che un immagine che viene applicata sulla superficie della mesh per aggiungerli dettagli e colori. La mesh e la texture sono legate tra di loro, tutto questo viene creato in un programma di grafica 3D, noi ci limitiamo solo a caricarli. Irrlicht gestisce diversi formati di mesh e diversi formati per le texture, per l’elenco completo di tutti i formati supportati vi invito a visitare il sio ufficiale.
Caricata la base ora è la volta della torretta.
Alla riga 45 creiamo il nodo torrettaBlu e carichiamo la corrispondente mesh (HeadBlue.x). Se il caricamento va a buon fine disabilitiamo il lighting e subito dopo la rendiamo nodo figlio della base. Ora i due nodi sono legati tra di loro. Successivamente posizioniamo la torretta, il posizionamento ora non è relativo agli assi d’origine dello spazio cartesiamo bensì all’oggetto padre, quindi la torretta viene ad essere 60 unitù più in altro della base. Usano l’associazione padre figlio in futuro per il movimento non dovremo pensare a muovere entramvi gli oggetti, ma muoveremo solo il nodo padre ovvero la base, la torretta seguirà automaticamente tutti i movimenti.
Dalla riga 56 alla 67 ripetiamo le stesse operazioni questa volta però per caricare e posizionare il carro armato rosso sempre con un vincolo padre figlio tra il nodo della base e quello della torretta.

scenemgr->addCameraSceneNodeFPS();
scenemgr->getActiveCamera()->setPosition(core::vector3df(20.0f, 400.0f, -1000.0f));
scenemgr->getActiveCamera()->setTarget(core::vector3df(20.0f, -500.0f, 0.0f));

Dalla riga 70 alla 72 creiamo una telecamra di tipo First Person Shooter e la settiamo in modo tale da inquadrare il campo di battaglia per intero. Essendo una telecamera FPS possiamo cambiare la visuale tramite mouse e tasti direzionali.
Il restante codice non è altro che il loop principale dove viene renderizzata la scena finquando non viene chiusa la finestra.

Come di consuetudine potete scaricare il codice di questo articolo cliccando qui.

Conclusioni
In questo articolo abbiamo visto come caricare le mesh, assegnagli correttamente le texture, creare nodi con vincoli padre figlio, modificare la posizione di un elemento della scena, creare una camera di tipo FPS e posizionarla. Da segnalare che non abbiamo usato luci, la luce e le ombre che si vedono sul campo di battaglia sono state create con il software di modellazione 3D che ha generato la mesh e soprattuto la texture. Questa infatti include luci e ombre precalcolate.
Nel prossimo articolo vedremo come gestire i movimenti del carro blu in relazione all’input impartito dal giocatore.

3 commenti su “Creare un videogioco: 4 componiamo la scena”

  1. Ciao, belle guide ma ho notato che tra l’una è l’altra passa parecchio tempo…non continui più?

  2. Ciao, belle guide ma ho notato che tra l’una è l’altra passa parecchio tempo,
    sarebbe bello avere la quinta parte,comunque grazie

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.