Duda detector de colisiones (XNA principiantes)

tracker086

Buenas gente. Hace un tiempo que me he ido poniendo poco a poco con C# y XNA. El caso es que ya me decidí a empezar mi primer juego, el pong! El caso es que tengo un problemilla con el detector de colisiones. Tengo hecho lo que posiblemente sea la version mas sencilla y cutre q se pueda hacer, y no se como mejorarla.

Os pongo en situación: tengo el pong, con su respectivas paletas, llamemosla player1 y player2 con su rectangulo para hacer un intersect con el rectangulo de la bola. El caso sq si la bola choca de frente con el player1, no hay problema, rebota y punto, pero si la bola choca digamos q por la parte de arriba de la pala o en diagonal, hace un efecto raro. Os pongo el codigo de la colision el cual pertenece a la clase bola:

private void Colision()
        {
            if (posicion.Y < 0 || posicion.Y + tamano > pantalla.Height)
                velocidad.Y = -velocidad.Y;
            if (rectangulo.Intersects(player1.rectangulo) || rectangulo.Intersects(player2.rectangulo))
            {
                velocidad.X = -velocidad.X;
            }
        }

Ese codigo para entrar defrente no pasa nada, pero si entra por arriba, empieza a cambiar la direccion de la velocidad.X como en un bucle.

El caso sq he intentao arreglarlo intentando hacer q si entrara por arriba, rebotara para arriba, pero no hecho mas que joderlo mas jaja. Tambien he pensado q lo mejor sería q solo detectara una colision con la pala por segundo o cada 2. Pero realmente no se como poder hacerlo. He intentado trastear con la clase GameTime pero no he sacado nada en claro.

Muchas gracias por adelantado!! Un saludo!

ItNaS

no tengo ni idea de programar en ese lenguaje, pero prueba que si la bola se ha pasado, a parte de cambiar el signo de la velocidad, moverla "manualmente" hasta el punto en que no se pasaria y no volviera a entrar en el if :S

tracker086

Lo primero gracias por la respuesta :). He probado a intentar hacer q una vez pase en el eje X donde esta la cara de la pala q haria cambiar la velocidad, q si la interseccion da positivo pues que cambie la velocidad en Y. Es decir, el efecto de q le de con el canto y vaya para arriba otra vez.

Ahora esta arreglado es eefecto, pero ahora me pasa, q como la pala se mueve mas rapido q la bola, si le pillo bien, ahora me hace el efecto contrario, me empieza a cambiar el signo de la velocidad en Y. No es tan grande, pero me gustaria pulirlo al tope.

Se me ha ocurrido hacer, q solo lo haga cada X segundos la comprobacion de si sta colisionando. El caso sq no se como poder hacerlo. Alguien se le ocurre?

Gracias de nuevo!!

tracker086

Vale solucionado, despues de un ratillo de darle un rato al coco, he puesto una variable q cada vez q se comprueba la colision sume 1, y en la condicion para mirar la colision he puesto q si es menor de 10 no haga la colision.

Un saludo!

Meleagant

Ese problema creo que se debe a la velocidad de movimiento de los elementos (pelota y palas) puede ser mayor de 1 pixel por update. Entonces se pueden dar casos en los que el movimiento en una situación en la que no hay colisión lleve a una situación siguiente en la que hay superposición de elementos.

Te he hecho un cutrepaint para que lo veas, porque a mi me pasó también cuando hice un pong en XNA:

En el punto 1 la pelota no está en colisión. Sin embargo subes la paleta a una velocidad tal que en el siguiente update (punto 2) ambos elementos están superpuestos.

Por tanto, antes del update siguiente, se modifica la velocidad en el eje X de la pelota para que vaya hacia la izquierda, pero la velocidad de la pelota no es suficiente para salir de la colisión en el update siguiente (punto 3), por lo que vuelve a cambiarse, y así indefinidamente.

PD: Creo que mi solución a este problema fue hacer que la pelota sólo pudiese rebotar hacia la izquierda si chocaba con la paleta derecha y viceversa.

4 respuestas
BLZKZ

#5 puedes tambien guardar el estado anterior y que si chocó en esta no choca.

Yo lo que hice fue controlar con un bool el choque, si choca el bool se pone a true y rebota entonces en el estado siguiente no puede volver a chocar (con la paleta) asi que en la siguiente comprobacion simplemente cambio el valor del booleano y actualizo la posición de la bola. Así siempre saldrá de la paleta (porque es matematicamente imposible que se quede "dentro" xD)

1 respuesta
Meleagant

#6

Esa es una opción si la relación entre el ancho de la paleta y la velocidad de la bola no permite que haya 3 updates seguidos en colisión, pero si modificas los valores de velocidad es posible que se de ese caso.

EDIT:

Vale, entiendo, una vez que el booleano de colisión está a true sólo vuelve a false al salir de la colisión.

No lo entendí bien, la verdad es que así está perfecto.

EDIT 2:

#8

Puede darse el caso de que el recorrido de salida sea más largo, por ejemplo si el eje de la bola ha rebasado el eje de la paleta, y subes la paleta de golpe.

1 respuesta
BLZKZ

#7 entonces haces mal la detección de colisiones xD porque actualizas posicion antes de comprobarla -.- por eso te pasaba lo que comentas em #5

Si en la figura 2 ya detectas la colisión automaticamente cambias la velocidad en eje X en esa misma actualización, no te esperas un update, lo mueves y luego la modificas -.-"

Si el problema es que está ya la bola dentro de la pala, es facil porque nunca en ningún caso hará mas colisiones para "salir" de la pala que para entrar.

Edit: #7 no puede darse el caso dado a no ser que des una velocidad variable al ejeX, puesto que las palas solo se mueven en el eje Y lo que no afecta en nada a dicho recorrico. El rebote se calcula en función del estado actual de la pala y la bola en el momento de la colisión no en los estados posteriores.

2 respuestas
Meleagant

#8

Hace años que hice esto y ahora mismo no lo tengo muy fresco, pero no entiendo bien lo que dices.

Pongamos que la pelota está en la situación del dibujo 1 y subes la paleta rápidamente. Al detectar la colisión, según tú, ¿qué reacción tendrían los dos elementos?

Entiendo que la pelota cambia el signo de la velocidad en el eje x y la barra simplemente se mueve a la posición correspondiente.

(Se me está empezando a calentar la cabeza de pensar en esto xD)

Seguramente tengas razón porque ahora mismo no lo recuerdo claramente y seguro que me estoy olvidando de algo.

1 respuesta
BLZKZ

#9 en la misma actualizacion que se detecta la colision cambiaria haria que velocidadBolaEjeX = - velocidadBolaEjeX;

Ten en cuenta que la dirección de la bola siempre es la misma a no ser que haya una colisión, por lo que cambia automaticamente al colisionar, independientemente del movimiento de la pala.

Digamos que la pala está en x:10 Y:30 y la bola choca en ese momento, a mi me da igual que la pala vaya a estar en x:10 Y:40 en la proxima actualización, porque ya he calculado el rebote de la bola y su velocidad y ya no va a volver a chocar xD

Esto es facil si actualizas antes la bola que la pala. Yo procederia asi:

  1. compruebo colision
  2. actualizo estado/velocidad bola
  3. actualizo estado pala

Asi es imposible que se de lo que tu comentas xD

Si encuentro codigo de alguna de mis pruebas con colisiones y XNA lo pongo, es que ahora mismo solo tengo el de un plataformas y no es igual del todo a un pong xD

1 respuesta
Meleagant

#10

Pues toda la razón tienes. Una cosa tan tonta y yo hacía primero todos los updates y después los cálculos.

Cualquier día vuelvo a retomarlo, tengo aquí el Essential XNA que me compré para esto y abandoné justo en el tema de las colisiones xD

1 respuesta
BLZKZ

#11 a mi lo que me trae un poco loco es el tema de calculo de fisicas algo complejas, eso de tirar de senos y cosenos y constantes que pongo al tuntun es gracioso xD

tracker086

Muchas gracias a los 2. Ya lo he arreglado, cambiando el orden de los updates y algun arreglillo mas, como poner un framecounter para q el intersect solo lo haga una vez cada 15 framecounters xd.

Lo que me pasa a hora, sq segun vas durando en el juego la pelota se acelera. Y cuando va bastante rapido, el detector es bastante triste...como ponia #5 en el dibujo.

Digamos q tngo puesto q si choca con la parte delantera rebota pero si ya "ha pasado" la barrera de la parte delantera de la pala, entonces lo q hace es cambiar el signo a la velocidad.Y es decir, como si le dieramos de canto. A velocidades pequeñas va de lujo. Pero al acelerarse, pasa eso, q debe aparecer despues de un update ya metida la bola en la pala...entonces en vez de rebotar, simplemente cambia la velocidad en Y como si le hubiera dado con el canto.

Entonces no se como hacerlo, para poder hacer q el movimiento de la vola pudiera star "mejor definido" para q no aparezca dentro de la pala...seguire pensando a ver si se me ocurre alguna idea xd

BLZKZ

15 framecounters = 15 frames? Yo no lo he usado, pero si es así de ahí viene el problema que tienes ahora, porque haces un skip de 15 frames entre detección y deteccíon por lo que actualiza 15 veces el la posicion de la bola y de la pala, pero solo haces una comprobación :/

Una pregunta... acelera hasta infinito o le tienes puesto un limite?

Si el problema sigue la única solución que se me ocurre es que hagas un calculo de trayectoria (y así sepas cuando debería chocar) y que actualices "a mano" la posición, y dirección de la bola.

tracker086

Pero sq el skip de frames lo hace una vez ha exo ya la colision. Es decir tngo un int q en cada update suma 1. Y en el if para detectar la colision hay otra condicion de q los frames tienen q ser mas de 15. Si entra dentro del if de la colision, entonces la variable frames la pongo a 0 otra vez. Asi aseguro q no me repite la colision. Pero siempre cuando va de una pala a otra, hay tiempo mas q de sobra para q entre en la colision no? Si prefieres te pongo el codigo

1 respuesta
BLZKZ

#15 vale pero eso de 15 es una constante que te has sacado de la manga xD para despues de una colisión xD Habia entendido que siempre hacias el skip de 15. Aun asi pastea el codigo de actualización de posicion de pelota y pala y cuando llamas a la deteccion de colisiones

1 respuesta
tracker086

#16 Jaja, si el 15 me lo he sacado de la manga xD. Como veras soy novato novato en esto pero me mola mazo, asiq poco a poco xD.

A ver voy a copiarte las cosas:

en la clase Ball, es donde controlo todo el tema de las colisiones. Creo q deberia hacerlo quizas en otra clase por el tema de modularidad y encapsulacion pero weno, primero decidi intentar hacer algo q funcionara y ya luego irlo perfeccionando hasta intentarlo dejar lo mas correcto posible. A la clase Ball le paso los 2 objetos de las 2 palas para poder usar su rectangulo y tal.

El metodo update de la clase Ball:

public void Update()
        {
            Colision();
            posicion.Y += velocidad.Y;
            posicion.X += velocidad.X;
            if (Keyboard.GetState().IsKeyDown(Keys.Space) && !jugando)
                Iniciar();
        }

Y ahora te copio el de la colision:

private void Colision()
        {   
//Compruebo que no se me salga de la pantalla por arriba y abajo y rebote if (posicion.Y < 0 || posicion.Y + tamano > pantalla.Height) velocidad.Y = -velocidad.Y; //Colision con las palas framecounter++; //Comprueba q no haya pasado la posicion de las palas(para q solo rebote si da con la cara de la pala if (posicion.X > 30 && posicion.X < pantalla.Width - 50) { //Comprueba q da cn la cara de la pala. Framecounter evita q esto suceda sucesivamente en un corto periodo de tiempo if ((rectangulo.Intersects(player1.rectangulo) || rectangulo.Intersects(player2.rectangulo)) && framecounter > 10) { velocidad.X = -velocidad.X; acelerar(); framecounter = 0; } } else { //Si ya ha pasado la distancia de las palas, puede ser q aun de con un canto, aqui se comprueba if ((rectangulo.Intersects(player1.rectangulo) || rectangulo.Intersects(player2.rectangulo)) && framecounter > 10) { velocidad.Y = -velocidad.Y; framecounter = 0; } } //Si "mete gol" al jugador 1 if (posicion.X < - tamano) { player2.Puntuar(); reset(); } //si mete gol al jugador 2 if (posicion.X > pantalla.Width) { player1.Puntuar(); reset(); } }

Ahi esta jeje. Cualquier detalle q veas para mejorar, ya sea en diseño o directamente en codigo, encantado de escuxarlo. No conozco a nadie asi con experiencia y yo no quiero tener un pong, sino intentar comprender mejor como funciona y hacerlo lo mas eficiente posible

r2d2rigo

A ver, el problema es que te estas liando un poco. Tienes que calcular la interseccion de los rectangulos, si, pero al mismo tiempo tambien tienes que calcular que cantidad de pixeles/unidades intersectan esos rectangulos (cuando se de la situacion del dibujo 2 de #5) para asi obtener un vector, que al invertirlo de signo te indique el vector de rebote de tu pelota.

1 1 respuesta
elkaoD

#18 básicamente. Dicho de otra forma: aleja la pelota a la distancia mínima de la pala en la que no hay intersección. Dado que supongo que la pala está fija en el eje X, es su borde (más el radio de la pelota, suponiendo que tiene origen en el centro.)

1 respuesta
BLZKZ

#19 la bola la trata como un cuadrado asi que es aun más facil

1 respuesta
elkaoD

#20, es indistinto, el radio de la pelota es la mitad del lado del cuadrado.

1 respuesta
BLZKZ

#21 claro, pero de un rectangulo en xna puedes sacar sus coordenadas automaticamente con accedentes (si no recuerdo mal)

1 respuesta
elkaoD

#22 no tengo ni idea de XNA así que... por cierto, accedentes?

1 respuesta
BLZKZ

#23 en ingles getters xD no se si te sonara, son metodos que acceden a los atributos privados de las clases y asi favorecer el encapsulamiento y demas mierdas de la oo

#25 es que los llamo accedentes/mutadores getters/setters indistintamente xD según me salga

1 respuesta
elkaoD

#24 sí, claro... pero es la primera vez que lo oigo en castellano xD Tiene sentido.

1 respuesta
r2d2rigo

Accesores/propiedades, hostias.

NeB1

getters xD lo otro me suena muy xungo xD

1 mes después
Meleagant

Resucito este hilo porque a raíz de esta discusión me dio por profundizar más con XNA.

Para dominar mejor el tema de las colisiones me he hecho un pequeño proyecto que consiste en una ventana en la que se generan aleatoriamente 20 bolas de distintos tamaños, colores y vectores de velocidad.

Las bolas rebotan con los bordes de la pantalla y entre sí, aplicándose leyes físicas para determinar los vectores velocidad de cada bola resultantes de los choques entre las mismas.

A nivel alto el procedimiento que utilizo es el siguiente:

1 - Coger una bola "b" (en orden según una lista de elementos de la clase Bola)
2 - Desplazarla (b.Posicion += b.Velocidad)
3 - Comprobar si hay colisión con las paredes y reubicarla en caso de haber excedido el límite.
4 - Comprobar colisiones de la bola "b" con otras bolas y, de haberlas, recalcular los vectores velocidad de las dos bolas implicadas en la colisión.

Repetir esto para todas las bolas de la lista.

El caso es que funciona correctamente, pero hay algunos momentos en los que dos bolas quedan "unidas" girando concéntricamente en lugar de rebotar. Tengo bastante claro que la causa del problema está en que se produce una colisión, se cambian los vectores de velocidad pero en el estado siguiente las bolas no se separan lo suficiente como para salir del estado de colisión, volviendo a cambiarse dichos vectores de forma constante (algo parecido al problema de #1 con la pala).

Sigo investigándolo, pero a lo mejor alguno veis claramente el problema y podéis echarme un cable.

1 respuesta
r2d2rigo

#28, el problema que tienes es que en el mismo momento de detectar la colision y obtener el vector de rebote, tienes que desplazar las pelotas la distancia justa para que ya no se consideren en colision. si no te pueden pasar bugs como ese.

1 1 respuesta
BLZKZ

#29 calla noob

PD: lo que dice es cierto

1 respuesta