Come creare un gioco in C++ esempio 1

Guida pratica su come creare un semplice gioco in C++ esempio 1

In questa guida vedremo come creare un gioco in C++ senza usare librerie grafiche , il gioco sarà simile a snake ma non avremo un serpente che cresce ogni punto che mangia, in questa versione avremo un cursore “ + ”  che dovrà mangiare i punti “ o ” e fare attenzione ai muri “ # ” che compariranno mangiando.

Faremo muovere il nostro cursore con i tasti W, A, S, D e renderemo possibile di scegliere la difficoltà del gioco.

Le librerie che utilizzeremo nel progetto esempio sono :

#include <iostream> 
#include <conio.h>
#include <Windows.h>
#include <time.h>

Ricordiamoci di usare :

using namespace std;

Le variabili che ci serve dichiarare e inizializzare prima di iniziare a creare il nostro gioco sono queste, in questo caso sono dichiarate come globali quindi prima del main :

#define difficile 30
#define medio 75
#define facile 150

bool gameOver;
const int larghezza = 40;
const int altezza = 20;
int x, y, puntoX, puntoY, score;
int wallX[100], wallY[100], nWall = 0;
enum Direzione { STOP = 0, LEFT, RIGHT, UP, DOWN };
Direzione dir;
int difficulty;

Vedremo man mano a cosa ci serve ogni singola variabile anche se è già intuibile dai nomi, ora iniziamo a scrivere le funzioni che ci servono :

–> Inizializzare una nuova partita

void Setup() {

srand(time(NULL));
gameOver = false;
dir = STOP;
x = larghezza/2;
y = altezza / 2;
puntoX = rand() % larghezza;
puntoY = rand() % altezza;
score = 0;

}

Questa funzione serve ad inizializzare le variabili ad uno stato di inizio partita , ” gameOver ” quando è true segnala che abbiamo perso, quindi di base la settiamo false, ” dir ” indica la direzione che sta percorrendo il nostro cursore, inizialmente sarà stop , quindi quando avvieremo una nuova partita il gioco sembrerà in pausa .

Le variabili x, y rappresentano le coordinate del nostro cursore, ad inizio partita vediamo che nell’esempio lo posizioniamo al centro del nostro spazio di gioco delimitato da altezza e larghezza, i livelli di gioco facile, medio, difficile ci serviranno per impostare la velocità del cursore.

–> Utilizzare i comandi

void Input() {

if (_kbhit()) {

switch (_getch()) {

case ‘a’:
case ‘A’:
dir = LEFT;
break;

case ‘d’:
case ‘D’:
dir = RIGHT;
break;

case ‘w’:
case ‘W’:
dir = UP;
break;

case ‘s’:
case ‘S’:
dir = DOWN;
break;

case ‘O’:
case ‘o’:
gameOver = true;
break;

case ‘p’:
case ‘P’:
dir = STOP;
break;

}
}
}

Questa funzione serve per poter far muovere il cursore nella direzione corrispondente al comando premuto, la funzione _kbhit() serve a rilevare se è stato premuto un tasto, qundi se ci restituisce true passiamo allo switch che utilizza nella condizione _getch() che ritorna quale tasto è stato schiacciato, quindi ricapitolando kbith ci permette di arrivare allo switch solo quando premiamo un tasto e tramite quest’ultimo il codice imposta quale direzione far prendere al puntatore assegnandola alla variabile dir.

I tasti p/P e o/O sono rispettivamente per mettere in pausa ed uscire dalla partita.

–> Implementiamo la logica del gioco

void Logic() {

/* 1 */

switch (dir) {

case LEFT:
x–;
break;
case RIGHT:
x++;
break;
case UP:
y–;
break;
case DOWN:
y++;
break;
default:
break;

}

in questa prima parte della funzione implementiamo una parte del meccanismo che permetterà al cursore di spostarsi, immaginiamo lo spazio di gioco come una matrice, facendo dei +1 o -1 ciclici sulle coordinate del cursore a seconda della variabile dir lo faremo spostare all’interno della matrice quando la stamperemo a video.

/* 2 */

if (x >= larghezza || x <= 0 || y >= altezza || y <= 0 )
gameOver = true;

for (int i = 0;i < nWall;i++) {
if (x == wallX[i] && y == wallY[i])
gameOver = true;
}

Nella seconda parte della funzione stabiliamo che le condizioni per perdere la partita sono due , se il puntatore si trova sui margini o se si trova su un muro, i muri da come si può vedere nel codice sono rappresentati da due vettori wallX e wallY che contengono rispettivamente le coordinate del nodo x e y del muro i.

/* 3 */

if (x == puntoX && y == puntoY) {

score += 1;
nWall += 1;
puntoX = rand() % (larghezza);
puntoY = rand() % (altezza);

wallX[nWall-1] = rand() % (altezza);
wallY[nWall-1] = rand() % (larghezza);

if ((wallX[nWall-1] == puntoX && wallY[nWall-1] == puntoY) || (wallX[nWall-2] == wallX[nWall – 1] && wallY[nWall-2 ] == wallY[nWall – 1])) {

wallX[nWall] += 20;
wallY[nWall] += 20;

}
}
}

Qui nella terza parte stiamo stabilendo cosa accade quando il nostro cursore si trova su un punto ” (x == puntoX && y == puntoY) ” , come si vede aumentiamo lo score e il numero di muri di uno e generiamo un nuovo punto da mangiare cambiando le variabili puntoX e puntoY, successivamente generiamo le coordinate per i nuovi muri da far comparire.

L’ultima condizione non è perfetta e puo essere perfezionata, serve per far capire che rendendo casuale la generazione delle coordinate degli ostacoli/punti potrebbe capitare che le coordinate di un muro siano anche quelle del punto o viceversa , le coordinate di due muri generati potrebbero anche coincidere o ancora potrebbe comparire un muro sul cursore, game over.

Ho scelto di non riapplicare il modulo per l’altezza e larghezza sulle coordinate degli elementi che si sovrappongono per evitare condizioni svantaggiose all’aumentare dello score, permettendo a questi di poter assumere coordinate al di fuori dello spazio di gioco cosi da non essere visualizzati e dare meno disturbo.

–> Mostriamo sullo schermo

void Draw() {

system(“cls”);
for (int i = 0;i< larghezza ;i++)
cout << “#”;
cout << endl;

for (int i = 0;i< altezza;i++) {
for (int j = 0;j< larghezza;j++) {

if (j == 0)
cout << “#”;

else if (i == y && j == x)
cout << “+”;
else if (i == puntoY && j == puntoX)
cout << “o”;

else {

bool stampa = false;
for (int k = 0;k < nWall;k++) {

if (wallX[k] == j && wallY[k] == i) {
cout << “#”;
stampa = true;
}
}

if (!stampa)
cout << ” “;
}
if (j == larghezza-1)
cout << “#”;
}
cout << endl;
}

for (int i = 0;i < larghezza + 2;i++)
cout << “#”;

cout << endl;
cout << “score :” << score << endl;
cout << “wX :” << wallX[nWall] << ” wY :” << wallY[nWall] << endl;

}

Questa è la funzione che da praticamente vita al nostro gioco, semplicemente scorre la matrice dello spazio di gioco e controlla ogni singola coordinata se è uno spazio vuoto o un muro / punto o il cursore quindi ne stampa il carattere associato, ad ogni chiamata in oltre pulisce il terminale prima di iniziare a stampare.

–> Mettiamo assieme

int main()

{

/* 1 */

system(“color 3B”);

cout << “\t EVITA I # \n \n”;
cout <<“comandi : \” W, A, S, D \” \n \n” << “premi un tasto per continuare()”;

_getch();

system(“cls”);

cout << “scegli un livello di difficolta :” << endl << “\n 1=Difficile \n 2=medio \n 3=facile \n ” << endl << ” scelta : “;
cin >> difficulty;

while (x < 1 && x>3) {

cout << “scegli un livello di difficolta :” << endl << “\n 1=Difficile \n 2=medio \n 3=facile \n ” << endl << ” scelta : “;
cin >> difficulty;

}

switch (difficulty) {

case 1:
difficulty = difficile;
break;
case 2:
difficulty= medio;
break;
case 3:
difficulty= facile;
break;

}

Setup();

In questa prima parte del main implementiamo un menù in cui scegliere la difficoltà

/* 2 */

while (!gameOver) {

Input();
Logic();
Draw();

Sleep(difficulty);

}

system(“color F”);
cout << “\n OOOOOPS FAI ATTENZIONE \n \n”;

system(“PAUSE”);

}

Nella seconda parte facciamo partire il gioco mettendo in un while true le funzioni Input , Logic , Draw la velocita di stampa e quindi la velocità di gioco è impostata con la difficultà

–> Un ultimo commento

Il gioco risultante è alquanto semplice è soffre di difetti per rendere più comprensibile la lettura del codice in fase di apprendimento, laciamo al lettore il compito di migliorarlo se vuole sperimentare quanto appreso.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *