Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: DeMoNcRaZy en 24 Junio 2015, 22:12 pm



Título: [SFML] Rebote de colisión
Publicado por: DeMoNcRaZy en 24 Junio 2015, 22:12 pm
Buenas,

Tengo un problema al intentar colisionar dos figuras en sfml, la cosa es que colisiona pero en vez de chocar y estarse quieto sin poder avanzar se coloca en la otra parte de la otra figura y así consecutivamente.

Aquí un vídeo previo del error:
El vídeo es capturado por mi, dura 30 seg. Para mostrar el error.
i8TFZbfo7EE


Código
  1. sf::FloatRect rect = rec.getGlobalBounds();
  2.        sf::FloatRect rect2 = rec2.getGlobalBounds();
  3.  
  4.        if(rect.intersects(rect2)){
  5.  
  6.            if(rec.getPosition().x<400){
  7.                std::cout << rec.getPosition().x << std::endl;
  8.                rec.setPosition(350, rec.getPosition().y);
  9.            }
  10.            if(rec.getPosition().x>400){
  11.                rec.setPosition(450, rec.getPosition().y);
  12.            }
  13.            if(rec.getPosition().y<400){
  14.                rec.setPosition(rec.getPosition().x, 350);
  15.            }
  16.            if(rec.getPosition().y>400){
  17.                rec.setPosition(rec.getPosition().x, 450);
  18.            }
  19.  
  20.        }

rec -> es el rectángulo que manejo con el keyboard.
rec2 -> es el rectángulo que está situado fijamente en el centro de la ventana.


Si supiesen a que debe deberse dicho error agradecería alguna respuesta.
Gracias.


Saludos!


Título: Re: [SFML] Rebote de colisión
Publicado por: ivancea96 en 25 Junio 2015, 10:57 am
A que se debe es algo obvio, pusiste los setPosition().

Para empezar, y dado que tienes la rect2, en vez de poner 400, 350 y 450, deberías poner los campos de la variable rect2.

No es que haya un error, simplemente el concepto es incorrecto.


Título: Re: [SFML] Rebote de colisión
Publicado por: DeMoNcRaZy en 25 Junio 2015, 13:09 pm
Intenté hacer lo que me indicaste pero no daba resultado al igual también intenté esto:

Código
  1. if(rec.getPosition().x<400){
  2.                std::cout << rec.getPosition().x << std::endl;
  3.                rec.setPosition(rec.getPosition().x, rec.getPosition().y);
  4.            }

Que no pueda avanzar más si colisiona sin ser posicionado, pero al igual sigue con el mismo error.

Gracias por la respuesta.

Saludos!


Título: Re: [SFML] Rebote de colisión
Publicado por: ivancea96 en 25 Junio 2015, 15:13 pm
Código
  1. rec.setPosition(rec.getPosition().x, rec.getPosition().y);

Mira atentamente esa línea.

A parte:

Ese ejemplo que pones en el vídeo no explica nada si no indicas cuales son las coordenadas de los objetos. ¿Qué es 400?

Lo que indiqué era cambiar esos valores por valores de las variables. Poner un 400 para quien ve ese código es como poner un 7568. No dice nada. Cambia cada valor por el campo de la variable que ha de ser. No va a corregir el problema ni mucho menos, pero haré cl programa más legible y más fácil de corregir (y más fácil de rehacer en caso de que quieras cambiar la posición del cuadrado ese)


Título: Re: [SFML] Rebote de colisión
Publicado por: DeMoNcRaZy en 25 Junio 2015, 15:35 pm
Así está repartido:

Código
  1. sf::RenderWindow window(sf::VideoMode(800, 600), "Colision");
  2.  
  3.    //Rectangualo
  4.    sf::RectangleShape rec(sf::Vector2f(50, 50));
  5.    rec.setFillColor(sf::Color::Cyan);
  6.  
  7.    //Otro rectangulo
  8.    sf::RectangleShape rec2(sf::Vector2f(50, 50));
  9.    rec2.setFillColor(sf::Color::Magenta);
  10.    rec2.setPosition(400, 300);

En el centro de la ventana (800x600).


Código completo:

Código
  1. #include <SFML/Graphics.hpp>
  2. #include <iostream>
  3.  
  4. int main(){
  5.    //Creamos la ventana
  6.    sf::RenderWindow window(sf::VideoMode(800, 600), "Colision");
  7.  
  8.    //Rectangualo
  9.    sf::RectangleShape rec(sf::Vector2f(50, 50));
  10.    rec.setFillColor(sf::Color::Cyan);
  11.  
  12.    //Otro rectangulo
  13.    sf::RectangleShape rec2(sf::Vector2f(50, 50));
  14.    rec2.setFillColor(sf::Color::Magenta);
  15.    rec2.setPosition(400, 300);
  16.  
  17.    window.setFramerateLimit(200);
  18.  
  19.    //Si la ventana está abierta
  20.    while(window.isOpen()){
  21.        //Creamos un evento
  22.        sf::Event ventana;
  23.        //Llamamos a los eventos
  24.        while(window.pollEvent(ventana)){
  25.            //Cerramos la ventana cuando se solicite
  26.            if(ventana.type == sf::Event::Closed){
  27.                window.close();
  28.            }
  29.        }
  30.  
  31.        //Movimiento rectangulo
  32.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){
  33.            rec.move(0, -1);
  34.        }
  35.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)){
  36.            rec.move(0, 1);
  37.        }
  38.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
  39.            rec.move(-1, 0);
  40.        }
  41.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
  42.            rec.move(1, 0);
  43.        }
  44.  
  45.        //Colisiones
  46.        if(rec.getPosition().x<0){
  47.            rec.setPosition(0, rec.getPosition().y);
  48.        }
  49.        if(rec.getPosition().x>750){
  50.            rec.setPosition(750, rec.getPosition().y);
  51.        }
  52.        if(rec.getPosition().y<0){
  53.            rec.setPosition(rec.getPosition().x, 0);
  54.        }
  55.        if(rec.getPosition().y>550){
  56.            rec.setPosition(rec.getPosition().x, 550);
  57.        }
  58.  
  59.        sf::FloatRect rect = rec.getGlobalBounds();
  60.        sf::FloatRect rect2 = rec2.getGlobalBounds();
  61.  
  62.        if(rect.intersects(rect2)){
  63.  
  64.            if(rec.getPosition().x<400){
  65.                std::cout << rec.getPosition().x << std::endl;
  66.                rec.setPosition(350, rec.getPosition().y);
  67.            }
  68.            if(rec.getPosition().x>400){
  69.                rec.setPosition(450, rec.getPosition().y);
  70.            }
  71.            if(rec.getPosition().y<400){
  72.                rec.setPosition(rec.getPosition().x, 350);
  73.            }
  74.            if(rec.getPosition().y>400){
  75.                rec.setPosition(rec.getPosition().x, 450);
  76.            }
  77.  
  78.        }
  79.  
  80.        //Limpiamos ventana
  81.        window.clear();
  82.  
  83.        //Dibujamos el rectangulo
  84.        window.draw(rec);
  85.        window.draw(rec2);
  86.  
  87.        //Actualizamos ventana
  88.        window.display();
  89.  
  90.    }
  91. }

Saludos!


Título: Re: [SFML] Rebote de colisión
Publicado por: ivancea96 en 25 Junio 2015, 15:44 pm
Pues cambia el .x<400 por rec2.left, y así con cada uno. Los valores, en variables, especialmente en este tipo de programa.


Título: Re: [SFML] Rebote de colisión
Publicado por: DeMoNcRaZy en 25 Junio 2015, 16:08 pm
He probado tu forma pero no funcionaba y al igual con tu idea intente hacer algo similar haber si funcionaba al principio parecía que iba a funcionar pero al final no.

Probé con algo así:

Código
  1. sf::FloatRect rect = rec.getGlobalBounds();
  2.        sf::FloatRect rect2 = rec2.getGlobalBounds();
  3.  
  4.        if(rect.intersects(rect2)){
  5.  
  6.            if(rec.getPosition().x<rect2.left){
  7.                rec.setPosition(rec2.getPosition().x-50, rec2.getPosition().y);
  8.            }
  9.            if(rec.getPosition().x>rect2.left){
  10.                rec.setPosition(rec2.getPosition().x+50, rec2.getPosition().y);
  11.            }
  12.            if(rec.getPosition().y<rect2.left){
  13.                rec.setPosition(rec2.getPosition().x, rec2.getPosition().y-50);
  14.            }
  15.            if(rec.getPosition().y>rect2.left){
  16.                rec.setPosition(rec2.getPosition().x, rec2.getPosition().y+50);
  17.            }
  18.  
  19.        }

Puse rect2.left ya que tenia que declararla y no valía rec2.left
Disculpa que te esté dando trabajo, te agradezco la mano que intentas echarme.


He estado buscando información y tal y encontré alguna otra forma también posible de hacerlo:

Código
  1. #include <SFML/Graphics.hpp>
  2. #include <iostream>
  3.  
  4. void comprobarIntersecta();
  5.  
  6. int main(){
  7.    //Creamos la ventana
  8.    sf::RenderWindow window(sf::VideoMode(800, 600), "Colision");
  9.  
  10.    //Rectangualo
  11.    sf::RectangleShape rec(sf::Vector2f(50, 50));
  12.    rec.setFillColor(sf::Color::Cyan);
  13.  
  14.    //Otro rectangulo
  15.    sf::RectangleShape rec2(sf::Vector2f(50, 50));
  16.    rec2.setFillColor(sf::Color::Magenta);
  17.    rec2.setPosition(400, 300);
  18.  
  19.    window.setFramerateLimit(200);
  20.  
  21.    //Si la ventana está abierta
  22.    while(window.isOpen()){
  23.        //Creamos un evento
  24.        sf::Event ventana;
  25.        //Llamamos a los eventos
  26.        while(window.pollEvent(ventana)){
  27.            //Cerramos la ventana cuando se solicite
  28.            if(ventana.type == sf::Event::Closed){
  29.                window.close();
  30.            }
  31.        }
  32.  
  33.        //Colisiones ventana bordes
  34.        if(rec.getPosition().x<0){
  35.            rec.setPosition(0, rec.getPosition().y);
  36.        }
  37.        if(rec.getPosition().x>750){
  38.            rec.setPosition(750, rec.getPosition().y);
  39.        }
  40.        if(rec.getPosition().y<0){
  41.            rec.setPosition(rec.getPosition().x, 0);
  42.        }
  43.        if(rec.getPosition().y>550){
  44.            rec.setPosition(rec.getPosition().x, 550);
  45.        }
  46.  
  47.        sf::FloatRect rect = rec.getGlobalBounds();
  48.        sf::FloatRect rect2 = rec2.getGlobalBounds();
  49.  
  50.        bool chocar [4] = {false,false,false,false}; //Norte, sur, este y oeste
  51.  
  52.        if(rect.intersects(rect2)){
  53.                comprobarIntersecta();
  54.        }
  55.  
  56.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){
  57.            if(!chocar[0])
  58.                rec.move(0, -1);
  59.        }
  60.        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)){
  61.            if(!chocar[1])
  62.                rec.move(0, 1);
  63.        }
  64.        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
  65.            if(!chocar[2])
  66.                rec.move(-1, 0);
  67.        }
  68.        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
  69.            if(!chocar[3])
  70.                rec.move(1, 0);
  71.        }
  72.  
  73.        //Limpiamos ventana
  74.        window.clear();
  75.  
  76.        //Dibujamos el rectangulo
  77.        window.draw(rec);
  78.        window.draw(rec2);
  79.  
  80.        //Actualizamos ventana
  81.        window.display();
  82.  
  83.    }
  84. }
  85.  
  86. void comprobarIntersecta(){
  87.    sf::RectangleShape rec(sf::Vector2f(50, 50));
  88.    rec.setFillColor(sf::Color::Cyan);
  89.    if(rec.getPosition().x<400){
  90.        chocar[0] = true;
  91.    }
  92. }

Pero de igual modo supuestamente hay que modificar el bool por true cuando colisione o tal así.

Gracias.

Saludos!


Título: Re: [SFML] Rebote de colisión
Publicado por: ivancea96 en 25 Junio 2015, 17:12 pm
Repasa el algoritmo. Piensa en qué pasa en cada momento, y en qué es incorrecto.

Código
  1. if(rec.getPosition().x<rect2.left)
  2. if(rec.getPosition().x>rect2.left)

¿Qué ocurre si x==rect.left? En vez de > pon >=, o <= en vez de <, como veas.

Si repasas el algoritmo y sacas valores por pantalla, haciendo las operaciones acabarás viendo por qué ocurre lo que ocurre. Sinó, puedes hacerlo en papel con valores pequeños (1-5) para encontrar el error, o mejor aun, para encontrar la forma correcta.

Mirarlo en internet no te va a ayudar a entenderlo, y menos copiando código sin más.


Título: Re: [SFML] Rebote de colisión
Publicado por: DeMoNcRaZy en 25 Junio 2015, 23:28 pm
Probé como me dijiste y por una parte responde bien entre sí...

De nuevo un corto vídeo del funcionamiento.
Solo funciona si voy de izquierda a derecha. <-->

DHS5weUasYU

Código
  1. #include <SFML/Graphics.hpp>
  2. #include <iostream>
  3.  
  4. int main(){
  5.    //Creamos la ventana
  6.    sf::RenderWindow window(sf::VideoMode(800, 600), "Colision");
  7.  
  8.    //Rectangualo
  9.    sf::RectangleShape rec(sf::Vector2f(50, 50));
  10.    rec.setFillColor(sf::Color::Cyan);
  11.  
  12.    //Otro rectangulo
  13.    sf::RectangleShape rec2(sf::Vector2f(50, 50));
  14.    rec2.setFillColor(sf::Color::Magenta);
  15.    rec2.setPosition(400, 300);
  16.  
  17.    window.setFramerateLimit(200);
  18.  
  19.    //Si la ventana está abierta
  20.    while(window.isOpen()){
  21.        //Creamos un evento
  22.        sf::Event ventana;
  23.        //Llamamos a los eventos
  24.        while(window.pollEvent(ventana)){
  25.            //Cerramos la ventana cuando se solicite
  26.            if(ventana.type == sf::Event::Closed){
  27.                window.close();
  28.            }
  29.        }
  30.  
  31.        //Movimiento rectangulo
  32.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){
  33.            rec.move(0, -1);
  34.        }
  35.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)){
  36.            rec.move(0, 1);
  37.        }
  38.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
  39.            rec.move(-1, 0);
  40.        }
  41.        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
  42.            rec.move(1, 0);
  43.        }
  44.  
  45.        //Colisiones
  46.        if(rec.getPosition().x<0){
  47.            rec.setPosition(0, rec.getPosition().y);
  48.        }
  49.        if(rec.getPosition().x>750){
  50.            rec.setPosition(750, rec.getPosition().y);
  51.        }
  52.        if(rec.getPosition().y<0){
  53.            rec.setPosition(rec.getPosition().x, 0);
  54.        }
  55.        if(rec.getPosition().y>550){
  56.            rec.setPosition(rec.getPosition().x, 550);
  57.        }
  58.  
  59.        sf::FloatRect rect = rec.getGlobalBounds();
  60.        sf::FloatRect rect2 = rec2.getGlobalBounds();
  61.  
  62.        if(rect.intersects(rect2)){
  63.  
  64.            if(rec.getPosition().x==rect2.left){
  65.                rec.setPosition(rec.getPosition().x-50, rec.getPosition().y);
  66.            }
  67.            if(rec.getPosition().x==rect2.left){
  68.                rec.setPosition(rec.getPosition().x+50, rec.getPosition().y);
  69.            }
  70.            if(rec.getPosition().y==rect2.left){
  71.                rec.setPosition(rec.getPosition().x, rec.getPosition().y-50);
  72.            }
  73.            if(rec.getPosition().y==rect2.left){
  74.                rec.setPosition(rec.getPosition().x, rec.getPosition().y+50);
  75.            }
  76.  
  77.        }
  78.  
  79.        //Limpiamos ventana
  80.        window.clear();
  81.  
  82.        //Dibujamos el rectangulo
  83.        window.draw(rec);
  84.        window.draw(rec2);
  85.  
  86.        //Actualizamos ventana
  87.        window.display();
  88.  
  89.    }
  90. }

Parte que modifiqué:

Código
  1. if(rect.intersects(rect2)){
  2.  
  3.            if(rec.getPosition().x==rect2.left){
  4.                rec.setPosition(rec.getPosition().x-50, rec.getPosition().y);
  5.            }
  6.            if(rec.getPosition().x==rect2.left){
  7.                rec.setPosition(rec.getPosition().x+50, rec.getPosition().y);
  8.            }
  9.            if(rec.getPosition().y==rect2.left){
  10.                rec.setPosition(rec.getPosition().x, rec.getPosition().y-50);
  11.            }
  12.            if(rec.getPosition().y==rect2.left){
  13.                rec.setPosition(rec.getPosition().x, rec.getPosition().y+50);
  14.            }
  15.  
  16.        }

Gracias.

Saludos!


Título: Re: [SFML] Rebote de colisión
Publicado por: DeMoNcRaZy en 26 Junio 2015, 01:21 am
#ACTUALIZACIÓN


Acabo de probar otra forma que da resultado por una parte, pero por la otra no.
Si voy de izquierda a derecha colisiona y no puede avanzar "bien", si voy de derecha a izquierda colisiona y no puede avanzar "bien", eso si quitamos la colisión de "y". Y si añado la colisión de "y" se desplaza el rectángulo a los lados como se ve al principio del siguiente vídeo:

KOVwKT6rf54

Código al que hago referencia en el vídeo:

Código
  1. sf::FloatRect rect = rec.getGlobalBounds();
  2.        sf::FloatRect rect2 = rec2.getGlobalBounds();
  3.  
  4.        if(rect.intersects(rect2)){
  5.  
  6.            if(rec.getPosition().x<=rect2.left){
  7.                rec.setPosition(rec.getPosition().x-1, rec.getPosition().y);
  8.            }
  9.            if(rec.getPosition().x>=rect2.left){
  10.                rec.setPosition(rec.getPosition().x+1, rec.getPosition().y);
  11.            }
  12.            if(rec.getPosition().y<=rect2.left){
  13.                rec.setPosition(rec.getPosition().x, rec.getPosition().y-1);
  14.            }
  15.            if(rec.getPosition().y>=rect2.left){
  16.                rec.setPosition(rec.getPosition().x, rec.getPosition().y+1);
  17.            }
  18.  
  19.        }

Por una parte parece que funciona:

Código
  1. if(rec.getPosition().x<=rect2.left){
  2.                rec.setPosition(rec.getPosition().x-1, rec.getPosition().y);
  3.            }
  4.            if(rec.getPosition().x>=rect2.left){
  5.                rec.setPosition(rec.getPosition().x+1, rec.getPosition().y);
  6.            }

Ya que colisionan y no pueden avanzar bien, eso si están solo estas funciones.

Si añado las demás funciones que hace referencia a "y" ya al colisionar se desplaza solo.

Código
  1. f(rec.getPosition().x<=rect2.left){
  2.                rec.setPosition(rec.getPosition().x-1, rec.getPosition().y);
  3.            }
  4.            if(rec.getPosition().x>=rect2.left){
  5.                rec.setPosition(rec.getPosition().x+1, rec.getPosition().y);
  6.            }
  7.            if(rec.getPosition().y<=rect2.left){
  8.                rec.setPosition(rec.getPosition().x, rec.getPosition().y-1);
  9.            }
  10.            if(rec.getPosition().y>=rect2.left){
  11.                rec.setPosition(rec.getPosition().x, rec.getPosition().y+1);
  12.            }


Saludos!


Título: Re: [SFML] Rebote de colisión
Publicado por: ivancea96 en 26 Junio 2015, 01:58 am
Código
  1. if(rec.getPosition().y>=rect2.left)
Eso será rect2.top, no .left.

De cualquier forma, es emétodo lo que hace es corregir progresivamente la superposición de los rectángulos. Una colisión es una colisión.

Una opción, es, en el código de movimiento, tras moverse, comprobar con intersecs(). En caso verdadero, deshaces el movimiento. Es una forma muy simple, pero efectiva en casos también simples.


Título: Re: [SFML] Rebote de colisión
Publicado por: DeMoNcRaZy en 26 Junio 2015, 02:09 am
De igual modo al colocar (.top):

Código
  1. if(rec.getPosition().x<=rect2.left){
  2.                rec.setPosition(rec.getPosition().x-1, rec.getPosition().y);
  3.            }
  4.            if(rec.getPosition().x>=rect2.left){
  5.                rec.setPosition(rec.getPosition().x+1, rec.getPosition().y);
  6.            }
  7.            if(rec.getPosition().y<=rect2.top){
  8.                rec.setPosition(rec.getPosition().x, rec.getPosition().y-1);
  9.            }
  10.            if(rec.getPosition().y>=rect2.top){
  11.                rec.setPosition(rec.getPosition().x, rec.getPosition().y+1);
  12.            }

Me da el mismo error que muestra al comenzar el vídeo, intento chocar con la figura pero al chocar se desplaza sola cuando se debería estar quieta sin poder avanzar más hasta que yo cambie de movimiento.

Código
  1. if(rec.getPosition().y<=rect2.top){
  2.                rec.setPosition(rec.getPosition().x, rec.getPosition().y-1);
  3.            }
  4.            if(rec.getPosition().y>=rect2.top){
  5.                rec.setPosition(rec.getPosition().x, rec.getPosition().y+1);
  6.            }


Referente a:

Código:
Una opción, es, en el código de movimiento, tras moverse, comprobar con intersecs(). En caso verdadero, deshaces el movimiento. Es una forma muy simple, pero efectiva en casos también simples.

¿Te refieres a crear una función?


Saludos!


Título: Re: [SFML] Rebote de colisión
Publicado por: ivancea96 en 26 Junio 2015, 12:39 pm
Si yo no digo que eso lo corrigiera, pero era técnicamente incorrecto. Si funcionó era porque X e Y son iguales.
Lo que digo es que, tras hacer el .move() al captar las teclas, en caso de que esté en una zona incorrecta, que haga un .move inverso.


Título: Re: [SFML] Rebote de colisión
Publicado por: Maurice_Lupin en 3 Julio 2015, 17:40 pm
Hola, podrias utilizar otro rectangulo que se mueve, detectas si colisiona con rec2
Código
  1.      //Movimiento otro rectangulo
  2.       if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){
  3.           otroRec.move(0, -1);
  4.       }
  5.       // ...  detectando teclas para otroRec
  6.  
  7.      if( !otroRec.intersects(rect2) ) { // Si no hay interseccion, actualizas el move
  8.          rec.move(otroRec.getPosition().x, otroRec.getPosition().y);
  9.      }
  10.      // otroRec no lo tienes que pintar
  11.  

Si te interesa averiguar sobre programación de juegos, vi por ahi un manual para juegos en C++ con Allegro, usa clases, excelentes conceptos me dio una idea sobre los videojuegos. Incluso hay un capitulo sobre colisiones.

El link : CURSO DE PROGRAMACIÓN DE VIDEJUEGOS CON C++ Y ALLEGRO (http://www.cin.ufpe.br/~yrms/curso_programacion.pdf)

Saludos.