Colisiongambas2

9
Inicio Programación › Colisiones y reflexiones Colisiones y reflexiones Publicado en 22 junio, 2013 por jguardon 3 Comentarios ↓ Hola. En la serie de artículos sobre el evento Draw() del DrawingArea vimos la forma de dibujar objetos y animarlos dentro de la superficie de dibujo, pero se nos plantean dos nuevas cuestiones relacionadas con el movimiento. Por una parte, si un objeto se mueve en línea recta lo que puede ocurrir es que dicho objeto desaparezca para siempre al sobrepasar los límites de nuestro “lienzo” y por otro lado quizás lo que queremos es que ese objeto rebote contra algún otro cuerpo o los bordes del lienzo o reaparezca de nuevo en pantalla. Comencemos por el último caso. Conseguir que un objeto no se escape es sencillo usando aritmética modular. El efecto que conseguiremos es que el objeto vuelva a aparecer en el lienzo justo por la parte opuesta por donde desapareció con la misma velocidad y trayectoria, una técnica muy usada en los antiguos juegos de arcade, también llamada Screen Wrapping ó Wraparound. Lo único que debemos hacer es actualizar la posición del objeto de manera que cuando sea mayor o menor de los límites X e Y recalcular la posición para que aparezca por el lado opuesto siendo ésta el resto o residuo de la división entre la posición y la anchura o altura del lienzo. De forma que podemos escribir pseudocódigo como este para obtener ese efecto: posiciónX = posicionX modulo ANCHO_LIENZO posiciónY = posicionY modulo ALTO_LIENZO Para probar su efecto, podemos usar el código que vimos en la entrada anterior donde poníamos en movimiento un círculo rojo. El código quedaría como sigue: Ir Ir Entradas recientes Aceleración y Fricción Pong, el juego Pong y los números mágicos La clase Paint en Gambas3 Colisiones y reflexiones Comentarios recientes aztk en Pong, el juego Licenciatura a Distancia Obtenga su licenciatura en linea Estudios universitarios a distancia Inicio Inicio Bases Bases plugin cookies Uso de cookies Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/ 1 de 9 11/10/14 19:57

Transcript of Colisiongambas2

Page 1: Colisiongambas2

Inicio › Programación › Colisiones y reflexiones

Colisiones y reflexionesPublicado en 22 junio, 2013 por jguardon — 3 Comentarios ↓

Hola. En la serie de artículos sobre el evento Draw() del

DrawingArea vimos la forma de dibujar objetos y animarlos dentro de la

superficie de dibujo, pero se nos plantean dos nuevas cuestiones

relacionadas con el movimiento. Por una parte, si un objeto se mueve en

línea recta lo que puede ocurrir es que dicho objeto desaparezca para

siempre al sobrepasar los límites de nuestro “lienzo” y por otro lado quizás

lo que queremos es que ese objeto rebote contra algún otro cuerpo o los

bordes del lienzo o reaparezca de nuevo en pantalla. Comencemos por el

último caso.

Conseguir que un objeto no se escape es sencillo usando aritmética

modular. El efecto que conseguiremos es que el objeto vuelva a aparecer

en el lienzo justo por la parte opuesta por donde desapareció con la misma

velocidad y trayectoria, una técnica muy usada en los antiguos juegos de

arcade, también llamada Screen Wrapping ó Wraparound. Lo único que

debemos hacer es actualizar la posición del objeto de manera que cuando

sea mayor o menor de los límites X e Y recalcular la posición para que

aparezca por el lado opuesto siendo ésta el resto o residuo de la división

entre la posición y la anchura o altura del lienzo. De forma que podemos

escribir pseudocódigo como este para obtener ese efecto:

posiciónX = posicionX modulo ANCHO_LIENZO

posiciónY = posicionY modulo ALTO_LIENZO

Para probar su efecto, podemos usar el código que vimos en la entrada

anterior donde poníamos en movimiento un círculo rojo. El código

quedaría como sigue:

IrIr

Entradasrecientes

Aceleración y Fricción

Pong, el juego

Pong y los números

mágicos

La clase Paint en

Gambas3

Colisiones y

reflexiones

Comentariosrecientes

aztk en Pong, el juego

Licenciatura a DistanciaObtenga su licenciatura en linea Estudios universitarios a distancia

InicioInicio BasesBasesplugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

1 de 9 11/10/14 19:57

Page 2: Colisiongambas2

567891011121314151617181920212223242526272829303132333435363738

Public Sub _new() ' inicializamos las variables globales con valores ' enteros dentro de un array que representa los puntos x, y ' que representan un punto en las coordenadas del DA

posicion = [20, 20] velocidad = [1, 1]

End

Public Sub DrawingArea1_Draw()

Draw.FillColor = Color.Red Draw.FillStyle = Fill.Solid Draw.Circle(posicion[0], posicion[1], 20) ' sumamos el vector velocidad a la posición en cada momento ' primero el componente X (que es el primer elemento del array) posicion[0] += velocidad[0] ' y luego el componente Y que es el segundo elemento del array posicion[1] += velocidad[1] ' actualizamos la posición si el circulo de sale del área de dibujo posicion[0] = posicion[0] Mod DrawingArea1.Width posicion[1] = posicion[1] Mod DrawingArea1.Height

End

Public Sub Timer1_Timer() ' el timer tiene su propiedad Delay = 16 ' lo que equivale a 60 fps

DrawingArea1.Refresh

End

Bien, si corremos este código veremos cómo el círculo siempre está

presente, una técnica muy útil para dibujar una nave o meteoritos que

vuelan por todas partes…

A continuación veremos cómo hacer que un objeto rebote cuando alcanza

el borde de nuestro lienzo (cuando digo “lienzo” me refiero lógicamente al

DrawingArea, que es donde pintamos a modo de lienzo. No lo he dicho

antes por su obviedad, pero me pareció oportuno hacerlo ahora antes de

continuar complicando la cosa). Para ello no hay más remedio que

recordar algunos fundamentos matemáticos bastante sencillos, como el

Teorema de Pitágoras que vamos a usar para calcular la distancia entre

dos puntos en un plano.

Distancia entre dos puntos

Consideremos dos puntos p y q cuya posición representamos

descompuesta en coordenadas X e Y como (p.X, p.Y) y (q.X, q.Y).

Representamos dichos puntos en un plano y calculamos la distancia D:

juego

aztk en Pong y los

números mágicos

jguardon en La clase

Paint en Gambas3

Archivos

septiembre 2013

junio 2013

Categorías

General

Programación

Etiquetas

aceleración animacióncoordenadasdibujo drawdrawing areaejes cartesianosevento fricción magic numbers

movimiento paint

pong posiciónvelocidad

Enlaces

Comunidad gambas-es

plugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

2 de 9 11/10/14 19:57

Page 3: Colisiongambas2

triángulo rectángulo

que forman los

vectores (p.X, q.X) y

(p.Y, q.Y). Aplicando el

teorema de Pitágoras:

Una vez obtenida la distancia entre dos puntos, podemos saber si están lo

suficientemente cerca como para considerar que han colisionado. Por

ejemplo, en el caso del círculo es sencillo deducir que el punto central, el

origen del círculo es uno de los puntos y el otro puede ser cualquier punto

del borde del drawing area. Para saber si el círculo (o la “pelota”) ha tocado

el borde de la pantalla sólo tendremos que sumar o restar el radio del

círculo en el eje de coordenadas que corresponda.

Si la pelota se acerca al borde derecho tendremos que sumar el radio al

punto que representa el centro del círculo para obtener el punto más

cercano al borde y si consideramos el borde izquierdo, haremos lo

contrario, restar el radio. Lo mismo es aplicable para los bordes superior e

inferior. Si la colisión fuese entre dos círculos bastaría con calcular la

distancia entre ambos centros y restar la suma de los radios.

Pero vamos a verlo con otra imagen para comprenderlo mejor:

Aplicando lo

explicado hasta

ahora podemos

calcular si el círculo

colisiona con la

pared izquierda si

p.X <= r y con la

pared derecha si

p.X >= (ancho -1) –

r. Lo mismo se

aplica para los casos con colisiones en las paredes superior e inferior,

sustituyendo X por Y. En el caso de querer comprobar si ambos círculos

colisionan entre sí, comprobaremos que la distancia entre ellos es menor

que la suma de los radios de cada uno. Para comprobar colisiones entre

objetos complejos hay varios algoritmos, pero resulta mucho más sencillo

usar una circunferencia de un radio suficiente para rodear un objeto

irregular.

Si recordamos que el origen de las coordenadas en un ordenador es la

esquina superior izquierda, incrementando X hacia la derecha e Y hacia

plugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

3 de 9 11/10/14 19:57

Page 4: Colisiongambas2

1234567891011121314151617181920212223242526

' usamos una constante para el radio del circuloPrivate Const RADIO_1 As IntegerPrivate Const RADIO_2 As Integer

' función que devuelve distancia entre dos puntos' (usamos la clase nativa Point para los puntos)Private Function dist(punto1 As Point, punto2 As Point) As Float Return Sqr((punto1.X - punto2.X) ^ 2 + (punto1.Y - punto2.End

' comprobamos colisiones con los bordes del lienzoIf p.X <= RADIO_1 Then 'borde izquierdo ' rebotarElse If p.X >= (DrawingArea1.Width - 1) - RADIO_1 Then ' borde derecho ' rebotarElse If p.Y <= RADIO_1 Then ' borde superior 'rebotarElse If p.Y >= (DrawingArea1.Height - 1) - RADIO_1 Then ' borde inferior 'rebotarEndif

' si queremos comprobar la colisión entre dos objetos p y q' considerando que ambos son circularesIf dist(p, q) - (RADIO_1 + RADIO_2) <= 0 Then ' rebotarEndif

Vectores y movimiento

En anteriores entradas habíamos aprendido que la velocidad es también

un vector y es éste el que sumamos a la posición del objeto que movemos.

Recordemos las fórmulas, usando la clase Point en este caso:

p.X = p.X + a * vel.Xp.X = p.Y + a * vel.Y

Hemos introducido una nueva variable “a” que será un multiplicador de la

velocidad, que puede ser constante o no. Podemos aumentar la velocidad

multiplicando por valores float superiores a 1 o disminuir la velocidad

multiplicando por valores inferiores a 1. Si el multiplicador es 0, entonces la

velocidad será 0 y el objeto se detendrá. ¿Pero qué pasa si usamos

valores negativos? Pues que el vector de velocidad será inverso y el

movimiento también. Esta deducción nos lleva a…

Reflexiones

Si consideramos el siguiente gráfico donde se expresa un punto P en

movimiento llega al borde derecho con una velocidad V podemos ver que

se produce una reflexión en el eje X que mantiene la misma magnitud tanto

en el eje X como en el Y, pero que es contraria en el eje X. Es decir,

cambia el sentido porque también cambia el signo del componente X del

plugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

4 de 9 11/10/14 19:57

Page 5: Colisiongambas2

De este modo tan sencillo podemos calcular las reflexiones en las paredes

de nuestro drawing area, teniendo en cuenta este mismo esquema para

reflexiones en la pared superior e inferior, donde el componente del vector

V que cambiará de signo será el Y.

Así podemos afirmar:

vel.X *= -1 ' para las paredes lateralesvel.Y *= -1 ' para las paredes sup. e inferior

Vamos a ver todo lo expuesto hasta ahora modificando el ejemplo inicial

del círculo en movimiento para que éste rebote en todas las paredes y en

otro círculo adicional.

Ejemplo práctico en Gambas3

He aquí un ejemplo de todo lo explicado. Tengo que reconocer que el

desempeño del DrawingArea es bastante malo, ya que se queda parado

en ocasiones sin saber a qué se debe exactamente. Otro de los

inconvenientes es que la classe Draw sólo acepta valores enteros, por lo

que las animaciones no son suaves y no hay variaciones significativas en

las trayectorias de los elementos. Veremos más adelante cómo se

comportan las animaciones usando la clase Paint, que admite valores más

exactos de tipo Float y además dibuja con suavizado antialiasing.

Una última reflexión personal: el control Drawing Area no creo que esté

diseñado para hacer juegos o animaciones muy costosas, pero servirá

para explicar lo básico y desde luego para crear otros controles, como por

ejemplo algún tipo de display, vúmetros o algún tipo de instrumento de

plugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

5 de 9 11/10/14 19:57

Page 6: Colisiongambas2

aprender a usar SDL…

Bueno, finalmente aquí os dejo el código:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455

' Gambas class file

Private posicion1 As PointPrivate velocidad1 As PointPrivate posicion2 As PointPrivate velocidad2 As PointPrivate Const RADIO_1 As Integer = 25Private Const RADIO_2 As Integer = 15Private Const COEFICIENTE As Float = 2

Public Sub _new() ' inicializamos las variables globales con valores aleatorios

Randomize posicion1 = Point(Rnd(RADIO_1, DrawingArea1.Width - RADIO_1 Rnd(RADIO_1, DrawingArea1.Height - RADIO_1))

posicion2 = Point(Rnd(RADIO_1, DrawingArea1.Width - RADIO_2 Rnd(RADIO_1, DrawingArea1.Height - RADIO_2))

velocidad1 = Point(Rnd(-3, 3), Rnd(-3, 3)) velocidad2 = Point(Rnd(-3, 3), Rnd(-3, 3))

End

Public Sub DrawingArea1_Draw()

Draw.FillColor = Color.Red Draw.FillStyle = Fill.Solid Draw.Circle(posicion1.X, posicion1.Y, RADIO_1) Draw.FillColor = Color.Orange Draw.Circle(posicion2.X, posicion2.Y, RADIO_2) ' actualizamos posicion del objeto 1 posicion1.X += velocidad1.X * COEFICIENTE posicion1.Y += velocidad1.Y * COEFICIENTE ' actualizamos posicion del objeto 2 posicion2.X += velocidad2.X * COEFICIENTE posicion2.Y += velocidad2.Y * COEFICIENTE

' comprobamos colisiones con los bordes del lienzo velocidad1 = colisionParedes(DrawingArea1, posicion1, RADIO_1, velocidad1 velocidad2 = colisionParedes(DrawingArea1, posicion2, RADIO_2, velocidad2

' comprobamos colision entre los dos objetos velocidad1 = colisionEntre2objetos(posicion1, RADIO_1, velocidad1, posici velocidad2 = colisionEntre2objetos(posicion2, RADIO_2, velocidad2, posici

End

Public Sub Timer1_Timer() ' el timer tiene su propiedad Delay = 16 ' lo que equivale a 60 fps

DrawingArea1.Refresh

plugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

6 de 9 11/10/14 19:57

Page 7: Colisiongambas2

Título: colisiones.tar (132 clicks)

Leyenda:

Filename: colisiones.tar.gz

Size: 6 kB

6061626364656667686970717273747576777879808182838485868788899091

' (usamos la clase nativa Point para los puntos)

Return Sqr((punto1.X - punto2.X) ^ 2 + (punto1.Y - punto2.

End

Private Function colisionParedes(canvas As DrawingArea, obj ' comprobar colisión con bordes del lienzo

If obj.X <= r Then 'borde izquierdo Return Point(vel.X * -1, vel.Y) Else If obj.X >= (canvas.Width - 1) - r Then ' borde derecho Return Point(vel.X * -1, vel.Y) Else If obj.Y <= r Then ' borde superior Return Point(vel.X, vel.Y * -1) Else If obj.Y >= (canvas.Height - 1) - r Then ' borde inferior Return Point(vel.X, vel.Y * -1) Else Return vel Endif

End

Private Function colisionEntre2objetos(obj1 As Point, r1 As

If dist(obj1, obj2) - (r1 + r2) <= 0 Then Return Point(vel.x * -1, vel.Y * -1) Else Return vel Endif

End

Puedes descargar el proyecto de gambas3 completo:

Análisis final

Después de analizar un poco el código, he visto que realmente la clase

Point no sirve, porque solo maneja valores enteros. De manera que he

vuelto a usar arrays de tipo Float[] para almacenar las coordenadas y la

velocidad como números Float. También he cambiado a dibujar con la

clase Paint, en vez de Draw por las mismas razones, y parece que el

ejemplo gana un poco en suavidad, aunque no desaparecen algunos

“glitches” y paradas bruscas por motivos que aun desconozco.

plugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

7 de 9 11/10/14 19:57

Page 8: Colisiongambas2

Título: colisiones2.tar (142 clicks)

Leyenda: colisiones2

Filename: colisiones2.tar.gz

Size: 8 kB

‹ DrawingArea y su evento Draw() – III La clase Paint en Gambas3 ›

Etiquetado con: animación, coordenadas, dibujo, draw, drawing area, ejes

cartesianos, movimiento, posición, velocidad

Publicado en: Programación

3 comentarios sobre “Colisiones y reflexiones”

jsbsan dice:

23 junio, 2013 a las 00:45

Para el tema de detectar colisiones, yo he usado la clase rect (

http://gambasdoc.org/help/comp/gb.qt4/rect?es&v3 ).

Tiene varios métodos muy útiles, por ejemplo el de Intersection.

Lo use en el programa “guerra de estrellas”, para saber cuando dos

naves colisionaban…

Saludos

Responder

jguardon dice:

23 junio, 2013 a las 15:40

Gracias Julio, lo echaré un vistazo

Responder

Shell dice:

24 junio, 2013 a las 12:38

Muy buen articulo y muy buenos ejemplo. Gracias por crearlos.

plugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

8 de 9 11/10/14 19:57

Page 9: Colisiongambas2

© 2014 Blog gambas-es.org ↑ Responsive Theme Funciona con WordPress

cargando

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos

necesarios están marcados *

Nombre *

Correo electrónico *

Sitio web

Comentario

Publicar comentarioPublicar comentario

plugin cookies

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la

aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

Colisiones y reflexiones | Blog gambas-es.org http://blog.gambas-es.org/colisiones-y-reflexiones/

9 de 9 11/10/14 19:57