VOLVER

TUTORIAL DE SFML

 

CAPITULO 5

En este capítulo realizamos una nave que dispara misiles contra asteroides.
Aprenderemos a manejar Sprites en movimiento, ademas de detectar colisiones.
La nave se mueve manejando las teclas de cursores del teclado, y disparamos con la tecla de espacio .

.

Creamos varios sprites desde un fichero llamado sprites.png.


Sprite __nave
    sf::Sprite nave { textura};
    nave.setTextureRect( {    0, 0, 64, 64}  );
Sprite __asteN0
    asteN0.setTextureRect( {64, 0, 64, 64}  );
    sf::Sprite asteN1 {textura};
Sprite __misil
    sf::Sprite misil {textura};
    misil.setTextureRect( {576, 32, 6, 32}  );
Estos sprites tienen asociados unas cordenadas y unos estados y velocidades en una serie de matrices de structuras.
    struct ESTRELLAS {    int x;    int y;    int v;    int c; };
    struct ESTRELLAS estrellasm[30];

    struct ASTEROIDES {    int x;    int y;    int v; int vy;   int t;};
    struct ASTEROIDES asteroidesm[5];

    struct MISIL {  int x;    int y;    int v;    int s;};
    struct MISIL misilm0;

La detección de colisión se hace de una forma fácil, aunque no suele ser muy precisa.
Se utiliza la comparación de coordenadas , aunque en las esquinas detectara colisiones erróneas, debido a que compara rectángulos completos, y suele ocurrir que los sprites no suelen tener nada dibujado en las esquinas.
Para remediar algo este error , se suele reducir las dimensiones de los sprites a comparar.
Por ejemplo: para comparar dos sprites de 64x64 , reducimos la comparación a 60x60. .

También hemos creado un contador de tiempo y de asteroides destruidos.

Debo explicar que los sprites no son animados, para animar un sprite de asteroide, deberíamos crear un mínimo de 4 a 10 imágenes por sprite, lo cual no es factible en este pequeño tutorial.

En las viejas versiones de SFML no tiene la orden miTexto.setLetterSpacing(1.f); , en Debian9 no lo soporta, en Debian10 si.
Para los que les pase esto, comentar con //miTexto.setLetterSpacing(1.f);


Espero que este pequeño tutorial os sea útil para vuestros proyectos..

PROGRAMA

Saludos.
Juan Galaz

He insertado el código fuente al final de la página para que sea mas cómodo su visualización.
A partir del siguiente capítulo ya no colocaré el código fuente en la  página web, solo estará en el fichero de PROGRAMA adjunto.

Nave por un cielo de estrellas con asteroides y disparos
#include "SFML/Graphics.hpp"
#include "SFML/Window.hpp"
#include "SFML/System.hpp"

char VARI[100];
std::string CADENAStr;

#define SCAPE    -1
#define KLEFT     1
#define KRIGHT    2
#define KUP        3
#define KDOWN    4
#define KDISPARO 5

#define ASTEVELO 10

int naveX = 100, naveY = 530, inaveX = 10, inaveY = 10;

struct ESTRELLAS {    int x;    int y;    int v;    int c; };
struct ESTRELLAS estrellasm[30];

struct ASTEROIDES {    int x;    int y;    int v; int vy;   int t;};
struct ASTEROIDES asteroidesm[5];

struct MISIL {  int x;    int y;    int v;    int s;};
struct MISIL misilm0;

int tiempo,tiempo1;
int asteN;

int main()
{
    int tecla;
    int x, y,z,zz;

    sf::RenderWindow ventana(sf::VideoMode(800, 600), "CINTURON DE ASTEROIDES", sf::Style::Close);
    ventana.setFramerateLimit(30);

    sf::Font font;
    if (!font.loadFromFile("sansation.ttf")){
        printf("¡FUENTES NO ENCONTRADAS!");
        return false;
    }

    sf::Texture textura;
    if (!textura.loadFromFile("sprites.png")) {
        printf("nave.png no encontrado\n");
        return false;
    }
    sf::Sprite nave { textura};
    nave.setTextureRect( {    0, 0, 64, 64}  );
    sf::Sprite asteN0 {textura};
    asteN0.setTextureRect( {64, 0, 64, 64}  );
    sf::Sprite asteN1 {textura};
    asteN1.setTextureRect( {128, 0, 64, 64}  );
    sf::Sprite asteN2 {textura};
    asteN2.setTextureRect( {192, 0, 64, 64}  );
    sf::Sprite asteN3 {textura};
    asteN3.setTextureRect( {256, 0, 64, 64}  );
   
    sf::Sprite misil {textura};
    misil.setTextureRect( {576, 32, 6, 32}  );
   
    sf::Sprite explosion {textura};
    explosion.setTextureRect( {512, 0, 64, 64}  );
    sf::Sprite explosion1 {textura};
    explosion1.setTextureRect( {448, 0, 64, 64}  );

    sf::Texture cielo;
    if (!cielo.loadFromFile("cielo.jpg")) {
        printf("cielo.jpg no encontrado\n");
        return false;
    }
   
    sf::Sprite cieloS { cielo};
   
//Inicializando Texto
    sf::Text miTexto;
    miTexto.setFont(font);
    miTexto.setFillColor(sf::Color::Red);
    miTexto.setOutlineThickness(0.f);
    miTexto.setLetterSpacing(1.f);
    miTexto.setCharacterSize(20);
    miTexto.setPosition(1, 1);

//Inicializando  colores y estrellas
    sf::Vertex estrella[1];
    sf::Color colorS[5];
    colorS[0] = { 0xFF, 0, 0, 255 };
    colorS[1] = { 0, 0, 255, 255 };
    colorS[2] = { 0xf8, 0x96, 0x9d, 255 };
    colorS[3] = { 0xf9, 0xf9, 0x08, 255 };
    colorS[4] = sf::Color::White;

    sf::Event evento;

Inicio:
    naveX = 470; naveY = 530;
    for (x = 1; x < 30; x++) {
        estrellasm[x].y = 0;
        estrellasm[x].x = rand() % 800;
        estrellasm[x].v = rand() % 5;
        estrellasm[x].c = 2+rand() % 5;
    }

    for (x = 0; x < 5; x++) {
        asteroidesm[x].y = 0;
        asteroidesm[x].x = rand() % 800;
        asteroidesm[x].v = 3 + rand() % ASTEVELO;
        asteroidesm[x].t = rand() % 4;
    }
   
    misilm0.v = 10;
    misilm0.s = 0;
   
    tiempo=tiempo1=0;
    asteN=0;;

    while (ventana.isOpen()) {
        tiempo++;
        if(tiempo>29)
            {
                tiempo=0; tiempo1++;
                CADENAStr = "TIME " + std::to_string(tiempo1) +"   ASTE." + std::to_string(asteN);
                miTexto.setString(CADENAStr);
                miTexto.setPosition(2, 2);               
            }
        while (ventana.pollEvent(evento)) {
            if (evento.type == sf::Event::Closed) {
            printf("Cerrando ventana SFML\n");
            ventana.close();
            }

            tecla = 0;
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
            tecla = KLEFT;
            else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            tecla = KRIGHT;
            else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
            tecla = KUP;
            else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            tecla = KDOWN;
            else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
            tecla = SCAPE;
            else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space))
            tecla = KDISPARO;
        }

        switch (tecla) {
        case KLEFT:
            naveX -= inaveX;
            if (naveX < 0)    naveX = 0;
            break;
        case KRIGHT:
            naveX += inaveX;
            if (naveX > 736)   naveX = 736;
            break;
        case KDOWN:
            naveY += inaveY;
            if (naveY > 536)   naveY = 536;
            break;
        case KUP:
            naveY -= inaveY;
            if (naveY < 0)     naveY = 0;
            break;
        case KDISPARO:
            misilm0.s=1;
            misilm0.x=naveX+29;
            misilm0.y=naveY;
            break;
        case SCAPE:
            ventana.close();
            break;
        }

//__Detectar colisiones misil______ 
    if(misilm0.s==1)
        for (x = 0; x < 5; x++)
        {
            if (asteroidesm[x].y < misilm0.y+32 && asteroidesm[x].y +64 > misilm0.y)           
                if (asteroidesm[x].x < misilm0.x+8 && asteroidesm[x].x +64 > misilm0.x)
                {
                    explosion.setPosition( {(float) asteroidesm[x].x, (float) asteroidesm[x].y});
                    ventana.draw(explosion);
                    ventana.display();
                    asteroidesm[x].y=1000;
                    misilm0.s=0;
                    asteN++;       
                }           
        }   

//__Detectar colisiones nave______                  
        for (x = 0; x < 5; x++)
        {
            if (asteroidesm[x].y+4 < naveY+60 && asteroidesm[x].y +60 > naveY+10)           
                if (asteroidesm[x].x+4 < naveX+60 && asteroidesm[x].x +60 > naveX+4)
                {
                    explosion.setPosition( {(float) naveX, (float)naveY});
                    explosion1.setPosition( {(float) naveX, (float)naveY});
                    for (z = 0; z < 10; z++)
                        {
                        ventana.draw(explosion);
                        ventana.display();
                        sf::sleep(sf::milliseconds(100));
                        ventana.draw(explosion1);
                        ventana.display();
                        sf::sleep(sf::milliseconds(100));
                        }
                    goto Inicio;   
                }           
        }   

        ventana.clear(sf::Color::Black);
        ventana.draw(cieloS);           
//__MISIL___________________
                misilm0.y -= misilm0.v;
                if (misilm0.y < -60) misilm0.s = 0;
                if (misilm0.s==1)
                {
                     misil.setPosition( {(float) misilm0.x, (float) misilm0.y});
                     ventana.draw(misil);
                }

//__Movimiento asteroides_____                  
        for (x = 0; x < 5; x++) {
            asteroidesm[x].y += asteroidesm[x].v;
            asteroidesm[x].x += asteroidesm[x].vy;
            if (asteroidesm[x].y > 600) {
                asteroidesm[x].y = -64;
                asteroidesm[x].x = rand() % 770;
                asteroidesm[x].t = rand() % 4;
                asteroidesm[x].v = 3 + rand() % ASTEVELO;
                asteroidesm[x].vy = -5 + rand() % 9;
            }
            switch(asteroidesm[x].t)
            {
                case 0:
                    asteN0.setPosition( {(float) asteroidesm[x].x, (float) asteroidesm[x].y});
                    ventana.draw(asteN0);
                break;
                case 1:
                    asteN1.setPosition( {(float) asteroidesm[x].x, (float) asteroidesm[x].y});
                    ventana.draw(asteN1);               
                break;
                case 2:
                    asteN2.setPosition( {(float) asteroidesm[x].x, (float) asteroidesm[x].y});
                    ventana.draw(asteN2);               
                break;
                case 3:
                    asteN3.setPosition( {(float) asteroidesm[x].x, (float) asteroidesm[x].y});
                    ventana.draw(asteN3);               
                break;
            }           
        }       
               
    //__Movimiento estrellas_____                  
        for (x = 1; x < 30; x++) {
            estrellasm[x].y = estrellasm[x].y + estrellasm[x].v;
            if (estrellasm[x].y > 630) {
                estrellasm[x].y = 0;
                estrellasm[x].x = rand() % 770;
                estrellasm[x].v = rand() % 10;
            }
            if (estrellasm[x].v < 1)     estrellasm[x].v = rand() % 5;
            estrella[0] = { sf::Vector2f(estrellasm[x].x, estrellasm[x].y), colorS[estrellasm[x].c] };
            ventana.draw(&estrella[0], 1, sf::Points);
        }

        nave.setPosition( {     (float) naveX, (float) naveY});
        ventana.draw(nave);
        ventana.draw(miTexto);
        ventana.display();
    }
}