Paso a paso desarrollo de un videojuego con C++ y SFML Parte III

Mi primer videojuego C++ y SFML

Hasta los momentos hemos construido gran parte de nuestro videojuego. 
Básicamente lo hemos realizado en varios pasos (Parte I + Parte II) y que vamos a resumir a continuación:


Parte I
1) Creamos una ventana de dibujo.
2) Dibujamos una figura en pantalla (Circulo)
3) Movemos el circulo en la pantalla cambiando sus coordenadas con circulo.setPosition(400, 300)
4) Mover el circulo repetidamente usando el bucle while de la ventana creada y utilizando variables para las coordenadas del circulo circulo.setPosition(x, y) incrementando los valores de x e y.
4.1) Establecer limites al movimiento del circulo con la condición if.
5)  Mover el circulo con una variable de incremento que cambia de sentido con el signo (+ o -) y utilizar la condición if para establecer los limites superior, inferior,  izquierdo y derecho.
6) Dibujar un rectángulo en la parte inferior.
7) Mover el rectángulo con el teclado (Usando las teclas A y D)
8) Eliminar el limite inferior y establecer el rectángulo como el limite manejando la colisión entre el circulo y el rectángulo.

Parte II
9) Dibujar los cuadrado en la parte superior manualmente.
10) Dibujar cuadrado en la parte superior aplicando matemáticas y utilizando arrays.

Para simplificar he eliminado los limites laterales (derecho e izquierdo) de la barra inferior y hemos dejado el color por defecto de las figuras,
es importante concentrarnos mas en la mecánica del juego y mas adelante ocuparnos del diseño y la apariencia aspectos también muy importantes.

Código fuente Parte I y Parte II 
 
#include <SFML/Graphics.hpp>
int main()
{

int x_min = 0 ,x_max = 800;
int y_min = 0 ,y_max = 600;

sf::RenderWindow ventana(sf::VideoMode(x_max,y_max),"IntelligenciaVirtual");

//Circulo-Pelota
int xc = 0, yc = 0 , rc=10;
int incre_xc = 1, incre_yc = 1; //incremento de variables
sf::CircleShape circulo(rc); // (rc= radiodel circulo)

// Rectángulo-Barra
int lr = 100, ar = 20;
int incre_xr = 10, incre_yr = 10;
int xr = (x_max-lr)/2 , yr = y_max - ar;
sf::RectangleShape rectangulo(sf::Vector2f(lr, ar));

// cuadrados-ladrillos
int const n = 20;// Cantidad de cuadrados
int ls = (4 * x_max) / (5 * n);// ecuación calcula el ancho de cuadrado para una separación = ls/4
int xs = 0 , ys = 0; //posición inicial de los cuadrados
sf::RectangleShape cuadrados[n]={};// array de cuadrados

for (int i = 0; i < n; ++i)//creamos n cuadrados y lo guardamos en un array
{
cuadrados[i] = sf::RectangleShape (sf::Vector2f(ls, ls)); //creamos n cuadrados y lo guardamos en un array
}

while (ventana.isOpen())
{
sf::Event event;
while (ventana.pollEvent(event))
{
if(event.type == sf::Event::Closed)
ventana.close();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
xr += incre_xr;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
xr -= incre_xr;
}

ventana.clear(); //Limpiamos La Ventana

//Aquí cambiamos la posición de los objetos

if (xc > x_max-rc*2 || xc < x_min)
incre_xc = -incre_xc;
if (yc > y_max-rc*2)
xc = yc = 0;
if (yc < y_min)
incre_yc = -incre_yc;

xc += incre_xc; // equivale a xc = xc + incre_xc
yc += incre_yc;

circulo.setPosition(xc,yc);
rectangulo.setPosition(xr,yr);

//Cajas de colisión
sf::FloatRect circuloBox = circulo.getGlobalBounds();
sf::FloatRect rectanguloBox = rectangulo.getGlobalBounds();

//detectando la colisión del circulo con el rectángulo
if (circuloBox.intersects(rectanguloBox)){
incre_yc = -incre_yc;
}

//Aquí dibujamos
for(int i = 0; i < n; ++i){
xs = i * (ls + ls/4 );
cuadrados[i].setPosition(xs,ys);
ventana.draw(cuadrados[i]);
}

ventana.draw(circulo);
ventana.draw(rectangulo);
ventana.display(); //Mostramos el Contenido de la Ventana
}

}


 
 
El resultado del código fuente desarrollado en la parte I y II debe darte como resultado algo parecido a la siguiente imagen. En en código establecimos que para mover la barra inferior que hace rebotar a la pelota se debe utiliza las teclas A (izquierda) y D (derecha).



Parte III Destruir los cuadrados cuando es tocado por el circulo. Como vemos la pelota representada por un circulo solo rebota en lo limites de la ventana y no con cada cuadrado y en la parte inferior, sólo cuando colisiona con la barra controlada por el jugador. Hasta ahora no hemos manejado la colisión con cada ladrillo que son representado con un cuadrado.
Para lograr que nuestra pelota destruya cada ladrillo debemos hacer algo similar a lo realizado con la barra y el circulo; debemos crear una caja de bordes para cada cuadrado, es decir para cada uno de los cuadrados almacenados en cuadrado[i] donde i representa el índice de cada cuadrado que va desde 0 hasta n.

Ahora además del array de cuadrado que creamos con la siguiente sentencia:

sf::RectangleShape cuadrados[n];// array de cuadrados

debemos crear un array de cajas de bordes para guardar el área de  cada cuadrado

sf::FloatRect cuadradosBox[n];// array de caja de colisiones

para aprovecha el bloque del dibujo de cada cuadrado en la parte del código que dice //aquí dibujamos vamos a guardar el borde de cada cuadrado[i] en cuadradoBox[i]

//Aquí dibujamos
        for(int i = 0; i < n; ++i){
            xs = i * (ls + ls/4 );
            cuadrados[i].setPosition(xs,ys);
            cuadradosBox[i] = cuadrados[i].getGlobalBounds();
            ventana.draw(cuadrados[i]);
        }

y finalmente similar a la detección de la colisión del circulo con el rectángulo usamos una condición if para detectar la colisión del circulo con cada cuadrado recorriendo todo el array con la sentencia de control for.

//colisión del círculo con cada cuadrado
        for (int i = 0; i < n; ++i){
            if (circuloBox.intersects(cuadradosBox[i])){
                incre_yc = -incre_yc;// incremento de la variable "y" del circulo
            }
        }

agregando los fragmentos de código en colo azul al código completo desarrollado en la parte I y II obtendremos un resultado igual al que muestra la siguiente imagen.




se puede observar a diferencia de la imagen anterior, el circulo al tocar un cuadrado rebota,  y ese es el resultado que queremos por lo momentos. El siguiente paso es agregar las sentencias necesarias para destruir los cuadrados que toque el circulo. Daremos todo el detalle necesarios y el código fuente completo.

En este enlace encontraras el código fuente para obtener el resultado de la imagen anterior.

Ya teniendo el control de la colisión del circulo con cada cuadrado el siguiente paso es destruir el cuadrado. Para ello vamos a darle valor a cada cuadrado el valor uno "1" cuando esta activo y cero "0" cuando esta destruido. Es decir cada cuadrado va a tener dos estados "0" (vivo) o "1" (muerto). Para almacenar el estado de cada cuadrado utilizamos un arreglo (array) de estado de cuadrados y lo declaramos de la siguiente manera:

// cuadrados  
   int estadocuadrado[n]={};

y dentro de bucle for que usamos para crear y almacenar cada cuadrado al mismo tiempo se inicializa su valor  en 1.

for(int i = 0; i < n; ++i){
  cuadrados[i]    = sf::RectangleShape (sf::Vector2f(ls, ls));
  estadocuadrado[i] = 1;
}

 Esto quiere decir que todos los cuadrados inician todos prendidos. Cuando el circulo toca por primera vez a un cuadrado es decir hay una intersección entre sus coordenadas y su estado es 1 e inmediatamente debe pasar a estado 0.

Si el circulo vuelve a pasar por donde estaba un cuadrado que está en estado 0 no debe haber colisión  y debe rebota en el borde superior de la ventana. Debemos asegurar que para que pueda haber una colisión debe cumplirse dos condiciones: la primera es que debe haber una intersección y la segunda es que el estado del cuadrado debe ser igual a 1. Traducido en lenguaje C++ es:

//manejo de colisiones del circulo con los cuadrados
for (int i = 0; i < n; ++i){
     if (circuloBox.intersects(cuadradosBox[i]) && estadocuadrado[i] == 1){
                incre_yc = -incre_yc;
                estadocuadrado[i] = 0;
      }
 }

y si un cuadrado esta en estado = 0, debemos asegurarnos de no dibujarlo en pantalla

//Aqui dibujamos
for(int i = 0; i < n; ++i){
       xs = i * (ls + ls/4 );
       cuadrados[i].setPosition(xs,ys);
        if (estadocuadrado[i] == 1){
             ventana.draw(cuadrados[i]);
         }
}

A continuacion el codigo fuente completo:

#include <iostream>
#include <SFML/Graphics.hpp>
int main()
{
int x_min = 0 ,x_max = 800;
int y_min = 0 ,y_max = 600;
sf::RenderWindow ventana(sf::VideoMode(x_max,y_max),"Parte III Intelligencia Virtual");

//Circulo
int xc = 50, yc = 50 , rc=10;
int incre_xc = 1, incre_yc = 1; //incremeto de variables
sf::CircleShape circulo(rc); // (rc= radiodel circulo)

// Rectangulo
int lr = 100, ar = 20;
int incre_xr = 10, incre_yr = 10;
int xr = (x_max-lr)/2 , yr = y_max - ar;
sf::RectangleShape rectangulo(sf::Vector2f(lr, ar));

// cuadrados
int const n = 20;// Cantidad de cuadrados
int ls = (4 * x_max) / (5 * n);// ecuacion calcula el ancho de cuadrado para una separacion = ls/4
int xs = 0 , ys = 0; //posicion inicial de los cuadrados
sf::RectangleShape cuadrados[n];// array de cuadrados
sf::FloatRect cuadradosBox[n];// array de caja de colisiones
for (int i = 0; i < n; ++i){//creamos n cuadrados y lo guardamos en un array
cuadrados[i] = sf::RectangleShape (sf::Vector2f(ls, ls)); //creamos n cuadrados y lo guardamos en un array
}

while (ventana.isOpen())
{
sf::Event event;

while (ventana.pollEvent(event))
{
if(event.type == sf::Event::Closed)
ventana.close();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
xr += incre_xr;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
xr -= incre_xr;
}
ventana.clear(); //Limpiamos La Ventana

//Aqui cambiamos la posicion de los objetos
if (xc > x_max-rc*2 || xc < x_min)
incre_xc = -incre_xc;

if (yc > y_max-rc*2) // cambio en paso 8
xc = yc = 50;

if (yc < y_min)
incre_yc = -incre_yc;

xc += incre_xc; // equivale a xc = xc + incre_xc
yc += incre_yc;

circulo.setPosition(xc,yc);
rectangulo.setPosition(xr,yr);

//Cajas de colision
sf::FloatRect circuloBox = circulo.getGlobalBounds();
sf::FloatRect rectanguloBox = rectangulo.getGlobalBounds();

//detectando la colision del circulocon el rectangulo
if (circuloBox.intersects(rectanguloBox)){
incre_yc = -incre_yc;
}

//colision del circulo con cada cuadrado
for (int i = 0; i < n; ++i){

if (circuloBox.intersects(cuadradosBox[i])){
incre_yc = -incre_yc;
}
}

//Aqui dibujamos
for(int i = 0; i < n; ++i){
xs = i * (ls + ls/4 );
cuadrados[i].setPosition(xs,ys);
cuadradosBox[i] = cuadrados[i].getGlobalBounds();
ventana.draw(cuadrados[i]);
}

ventana.draw(circulo);
ventana.draw(rectangulo);

ventana.display(); //Mostramos el Contenido de la Ventana
}
}


Comentarios

Entradas populares de este blog

Mover formas simples en pantalla con C++ y SFML

Dibujar formas simples con C++ y SFML

Como Mover Formas Simples Con el Teclado con C++ y SFML