Facciamo quattro salti

Nei precedenti articoli, abbiamo avuto modo di affrontare diverse tematiche, tra cui quelle relative a giochi basati sui tiles.

In questo articolo riprenderemo il discorso dei tiles, gia' affrontato con un semplice gioco di labirinto, implementando la possibilita' di avere un giocatore che si muova liberamente in un livello molto poco simmetrico e restrittivo, saltando a piacimento.

Qui l'unica differenza rispetto al classico movimento a quattro direzioni gia' visto, e' che il movimento verticale viene controllato e condizionato dal programma in automatico.
Se prima ci si poteva muovere liberamente verso l'alto e verso il basso, ora questo movimento e' ristretto e limitato ad un campo d'azione prestabilito.

Quando il nostro personaggio salta, e' come se venisse esercitata una forza su di se, che lui gradualmente consuma eseguendo il movimento verso l'alto.
Come quando lanciamo una pallina contro il muro, non e' la pallina a possedere la forza, siamo noi che gliela diamo, lei si limita solo a consumarla gradualmente.
In maniera analoga in un gioco, potremmo assegnare al giocatore una forza, rappresentata magari da un valore numerico che ne decreti la distanza percorribile verso l'alto, e che gradualmente diminuisca durante il movimento.

Il meccanismo che presento in questo articolo, e' ripreso a grosse linee dall'ottima serie di tutorial
jnrdev, la mia e' una reimplementazione in linguaggio C con qualche lieve modifica.

Prima di tutto cominciamo definendo le costanti e le variabili che useremo.
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 320

#define MAP_W 10
#define MAP_H 10

#define TILESIZE 32
#define VELJUMP 4.5

int playfield[10][10] =
{
1,1,1,1,1,1,1,1,1,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,1,1,1,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,1,0,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,1,1,0,0,1,
1,0,0,0,1,1,1,0,1,1,
1,1,1,1,1,1,1,1,1,1,
};

struct _Player
{
float x, y, dirX, dirY, lockjump;
} Player;
Potete aggiungere e sostituire a vecchi esempi presi dai precedenti articoli, le variabili scritte qui sopra, oltre che aggiungere le funzioni di disegno necessarie.

A questo giro, nella struttura Player, ho optato per valori di posizione espressi in float, per poter rendere piu' lenti e soffici i movimenti all'occhio, anche se i movimenti orizzontali rimarranno sempre di 1 pixel alla volta.
Con la costante VELJUMP, dichiariamo l'altezza massima a cui il giocatore potra' saltare, che in seguito convertiremo in valore intero per gestire al meglio le collisioni.

Ora gestiamo la parte relativa al movimento con la nostra solita funzione getInput().

    if (keystate[SDLK_LEFT])
     {
         Player.dirX = -1;
     }

    if (keystate[SDLK_RIGHT])
     {
          Player.dirX = 1;
     }

    if (!keystate[SDLK_LEFT] && !keystate[SDLK_RIGHT])
     {
         Player.dirX=0;
     }

  if (keystate[SDLK_UP] && !Player.lockjump)
   {
    //if the player isn't jumping already
   Player.dirY = -VELJUMP; //Salta!
   Player.lockjump = 1; //Il giocatore non puo' piu' saltare
   }

  if (!keystate[SDLK_UP])
   {
    //Facciamo cadere il giocatore
   Player.dirY+=0.1;
   }

}
A differenza di quanto fatto nel precedente articolo, in cui la nuova posizione del giocatore veniva aggiornata direttamente in questa funzione, qui ho optato per una aggiornamento delle posizioni X e Y del giocatore da farsi un secondo tempo.

Notate subito che non vengono incrementate le posizioni X e Y del giocatore direttamente, ma solo impostati dei valori nelle variabili dirX e dirY che terranno conto della direzione scelta dal giocatore in base alla pressione dell'apposito tasto.
La mancata pressione del tasto freccia sinistra o destra, impostera' la variabile dirX a 0, mandando il giocatore in uno stato di stallo, annullando cosi' il suo movimento.

Successivamente troviamo un controllo sul tasto freccia su, se il tasto in questione e' premuto e la variabile lockjump e' posta a zero, allora verra' impostato un valore di dirY negativo che gli consentira' di saltare.
La variabile lockjump, tiene conto dello status del giocatore nel momento del salto, il suo valore posto a 0 significa nessun salto, in caso contrario avremo 1.

L'ultimo controllo sul tasto freccia su, serve nel caso vogliate avere un salto dinamico, al rilascio del tasto in questione, si crea la condizione per cui il giocatore debba muoversi verso il basso, ovvero cadere.
Potete commentare questo blocco condizionale nel caso vogliate un salto statico ad altezza fissa nel vostro gioco.

Le cose appena presentate, potrebbero tornarvi piu' chiare dando uno sguardo alla funzione che si occupa di realizzare il movimento del giocatore nel campo di gioco.
void Move()
{
 // Controlliamo di essere all'interno del campo di gioco
 if(Player.x >=0 || Player.x + TILESIZE <= MAP_W ||
    Player.y >=0 || Player.y + TILESIZE <= MAP_H)
 {
  if(!tileCollision(Player.x+Player.dirX, Player.y, TILESIZE/2, TILESIZE/2))
  {
   Player.x+=Player.dirX; // Incrementa/Drecrementa le coordinate
  }
  //Movimento verticale
  if(Player.dirY < 0)
  { //Verso l'alto (Salto)    
   if(tileCollision(Player.x, Player.y-1, TILESIZE/2, TILESIZE/2))
   {
    Player.dirY= 0;
   }
   else
   {
    Player.y+= (int)Player.dirY;
    Player.dirY+=0.1;
   }
  }
  else if(Player.dirY >= 0)
  {  //Controlliamo che sia su una piattaforma
   if(tileCollision(Player.x, Player.y+Player.dirY, TILESIZE/2, TILESIZE/2))
   {
    Player.dirY=1;
    //Testiamo continuamente se il giocatore tocca una piattaforma,
    //1 per farlo subito, 0 per farlo al frame successivo 

    //Il giocatore puo' saltare solo quando il tasto apposito
    //e' rilasciato e si trova su una piattaforma 
   if(!keystate[SDLK_UP])
     Player.lockjump = 0;
   }
   else
   { //In aria (caduta)
    Player.y+=(int)Player.dirY;
    Player.dirY+=0.1;

    if(Player.dirY >= TILESIZE/2) //Limitiamo la velocita' di caduta
     Player.dirY = TILESIZE;

    Player.lockjump = 1; //Disabilitiamo il salto durante una caduta
   }
  }
 }
}

Il codice parla da se, nel primo blocco di istruzioni, troviamo il movimento orizzontale del giocatore, la sua posizione X verra' aggiornata rispetto al valore di dirX sempre se questa non lo porti a sovrastare un ostacolo.

Successivamente troviamo la gestione dei movimenti verticali, qui per prima cosa controlliamo se il giocatore e' in procinto di saltare, aggiornando la sua posizione Y ad un valore intero approssimativo preso da dirY, successivamente decrementiamo dirY con un valore veramente basso, che donera' al movimento la giusta velocita'.

Nel caso il valore di dirY sia maggiore o uguale a 0, questo significhera' che il giocatore e' in fase di caduta, per questo motivo da subito controlliamo una sua eventuale collisione con una qualche ostacolo del livello.
Incrementiamo da subito il suo valore dirY a 1 per avere un continuo movimento controllo alla sua posizione successiva verso il basso, ed anche per avere certezza che continui ad essere su una piattaforma.

Impostare questo valore a 1, permette di effettuare il controllo prima che l'immagine su schermo venga aggiornata, impostandolo a 0 invece potremmo vedere il giocatore fermarsi un attimo prima di toccare un ostacolo ed adattarsi alla superficie un attimo dopo.
In tutti e due i modi funziona correttamente, e' solamente una cosa che ci fa notare l'autore dell'articolo originale.

Una volta assicurati che il giocatore e' su una piattaforma ed il tasto per il salto non e' premuto, impostiamo la variabile lockjump a 0, cosi' da permettere un nuovo salto.
L'effettivo movimento verso il basso, viene effettuato nel blocco condizionale seguente, in quanto a rigor di logica, vi sono validi motivi per aggiornare le posizione Y del giocatore.

Incrementiamo la sua posizione Y al valore corrente di dirY che verra' incrementato lievemente ad ogni ciclo, in modo da dare anche qui una caduta lieve e alla giusta velocita' come nel salto.
Avremmo l'accortezza di limitare il valore di dirY alla grandezza del nostro personaggio, per evitare che vengano mal interpretate le collisioni con gli ostacoli del livello, e di ritrovarci un personaggio che trapassa gli ostacoli o che ci si incagli dentro.

Per entrambe le direzioni verticali, il valore della posizione Y del giocatore, viene convertito sul momento ad un valore intero da float, questo perche' le collisioni ora come ora vengono gestite con valori interi, consideriamo un pixel intero pari ad 1 e non ad un valore minore.

Questo e' quanto vi serve per poter implementare un semplice meccanismo di salto nel vostro gioco, anche se ovviamente questo esempio non e' perfetto e sicuramente si potrebbe migliorare qua e la con qualche modifica.
Sbizzarritevi con modifiche di vario genere e combinate fra loro diversi parametri per vederne gli effetti sul gioco, e buon divertimento!

Nessun commento :

Posta un commento

Related Posts Plugin for WordPress, Blogger...