Anuncios Google

11. Colisiones.

Tutoriales Avanzados Homebrewes

En este nuevo tutorial vamos a aprender a crear colisiones sencillas en Lua. Con el método que veremos, implementaremos colisiones de nuestro personaje con varios bloques aunque no sean colisiones perfectas en cuanto a píxeles. Al ser el primer intento de colisión será bastante sencillo para que se entienda bien.

Demostración de colisión

En esta pequeña demostración crearemos un personaje capaz de moverse y también tres bloques en pantalla que tendrán implementados una colisión cada uno. El personaje no podrá atravesar esos bloques.

Para empezar crearemos dos colores, uno para el personaje y otro para nuestros bloques:

--Creamos los Colores.
verde=Color.new(0,255,0)
blanco = Color.new(255,255,255)

Ahora crearemos dos imágenes que usaremos para nuestro personaje y para nuestros bloques. Primero creamos imágenes vacías de 32x32 y las rellenamos del color que acabamos de crear.

--Creamos el personaje.
personaje1 = Image.createEmpty(32,32)
personaje1:clear(blanco)
 
--Creamos el bloque.
bloque1 = Image.createEmpty(32,32)
bloque1:clear(verde)

Ahora necesitamos crear un array para almacenar la información del personaje. En esta demostración sólo guardaremos la posición "x" e "y".

--Array del personaje.
Personaje = { x = 30, y = 100 }

Lo siguiente es definir dos variables que almacenarán la altura y anchura de nuestro personaje. Esos valores serán usados en la función de la colisión. Recuerda que hemos creado nuestras imágenes de 32x32, por lo tanto esos serán los valores que usaremos.

--Definicion de variables.
personajeAltura = 32
personajeAnchura = 32

Ahora crearemos un array para almacenar la información de los tres bloques. Naturalmente guardaremos los valores "x" e "y" y la altura y anchura de cada bloque. Usaremos los comandos width() y height() para ello, que automáticamente hallarán esos valores para nosotros. Los valores son tomados del bloque que hemos creado anteriormente. Vamos allá.

--Array del bloque, con sus tres elementos.
Bloque = {}
Bloque[1] = { x = 100, y = 80, Altura = bloque1:height(), Anchura = bloque1:width() }
Bloque[2] = { x = 300, y = 30, Altura = bloque1:height(), Anchura = bloque1:width() }
Bloque[3] = { x = 200, y = 58, Altura = bloque1:height(), Anchura = bloque1:width() }

Ahora haremos una función para mover nuestro personaje. Será llamada a cada pasada del bucle para comprobar si hay movimiento. No debe ser difícil de entender si has seguido los anteriores tutoriales.

--Funcion para mover el personaje por la pantalla.
function moverPersonaje()
pad = Controls.read()
if pad:left() then
Personaje.x = Personaje.x - 1
end
if pad:right() then
Personaje.x = Personaje.x + 1
end
if pad:up() then
Personaje.y = Personaje.y - 1
end
if pad:down() then
Personaje.y = Personaje.y + 1
end
end

Y ahora viene lo más interesante. Es hora de crear la función que compruebe si hay colisiones. Esta función nos permitirá comprobar si hay colisiones para cualquier objeto de nuestro juego, siempre y cuando la función sea llamada para cada objeto en nuestro bucle principal. Mira la función completa y después la explicaré:

--Funcion que comprueba si se produce alguna colision.
function comprobarColision(object)
if (Personaje.x + personajeAnchura > object.x) and (Personaje.x < object.x + object.Anchura) and (Personaje.y + personajeAltura > object.y) and (Personaje.y < object.y + object.Altura) then
Personaje.x = oldx
Personaje.y = oldy
end
end

Miremos la primera línea: function comprobarColision(object). Con ella creamos nuestra función llamada comprobarColision. Fíjate que dentro de los paréntesis colocamos la palabra object. La palabra es básicamente una variable. Cuando más adelante usemos la función, esta palabra será sustituida por el nombre del objeto del que queremos probar si hay colisión. Si miras dentro de la función te darás cuenta de que la palabra objeto aparece varias veces más. Cuando la función es llamada, todas las palabras object serán reemplazadas por el objeto que le hemos pasado a la función.

En la segunda línea tenemos una sentencia if que comprueba varias condiciones. Fíjate que he puesto cada sección entre paréntesis, lo cual hace que sea mucho más legible.

La primera comprobación pregunta si (Personaje.x + personajeAnchura > object.x). El valor "x" de las imágenes es la esquina superior izquierda de la imagen. La colisión consiste en ver si dos imágenes se superponen la una a la otra y si eso ocurre lo detenemos. Entonces ¿por qué sumamos la anchura del personaje al valor "x" del personaje? Simple, porque si sólo comprobamos el valor "x" del personaje no se detectaría la colisión a menos que el píxel superior izquierdo de la imagen se superpusiera. Si nos estamos moviendo a la derecha y hay un bloque en el camino, entonces necesitamos que la colisión ocurra en la parte derecha de la imagen del personaje. El valor "x" del personaje sumado a la anchura del personaje es la esquina superior derecha de la imagen.

Básicamente lo que vamos a hacer es crear una área cuadrada al rededor del bloque que requiera la colisión. Si entras en ese área ocurrirá la colisión. He dibujado varias imágenes para que veas como trabajan estas líneas de código. El cuadrado azul es el bloque y el rojo es el personaje. La línea negra muestra donde ocurre la colisión. Hasta ahora sólo hemos comprobado si el lado derecho de nuestro personaje supera el valor "x" de nuestro otro objeto. Mira:

/biblioarchivosdrupal/fm_publicos/active/0/Imagen1.jpg

Como puedes ver, ahora nuestro personaje está dentro de la zona de colisión, pero tenemos que hacer más comprobaciones.

Lo siguiente que tenemos es: (Personaje.x < object.x + object.Anchura). Este fragmento de código es para cuando el personaje se esté moviendo hacia la izquierda y golpea la parte derecha del bloque. Es básicamente lo contrario de la anterior línea. El valor "x" del bloque sumado al ancho será su lado derecho. Veamos la zona de colisión con las dos primeras comprobaciones:

/biblioarchivosdrupal/fm_publicos/active/0/Imagen2.jpg

Todavía estamos dentro de la zona de colisión con estas primeras dos comprobaciones.

La siguiente comprobación es: (Personaje.y + personajeAltura > object.y). Este fragmento de código es para cuando el personaje se esté moviendo hacia abajo. La posición del personaje sumado a la altura del personaje nos da la parte inferior de la imagen del personaje. Si este valor es superior que la posición "y" del bloque entonces estamos colisionando. Veamos la imagen con las tres comprobaciones:

/biblioarchivosdrupal/fm_publicos/active/0/Imagen3.jpg

Ahora, como ves, el área que está por encima del bloque no forma parte del área de colisión, luego la parte inferior de la imagen del personaje tiene que ser superior a la parte alta del bloque para que ocurra la colisión.

Nuestra última comprobación: (Personaje.y < object.y + object.Altura). Esta parte es para cuando nos movemos hacia arriba. Si el valor "y" del personaje es menor que la parte inferior del bloque (valor "y" sumado a la altura) entonces estaremos colisionando. Usamos "menor que" esta vez porque al moverte hacia arriba por la pantalla disminuye el valor "y". Veamos la última imagen:

/biblioarchivosdrupal/fm_publicos/active/0/Imagen4.jpg

Ahora la zona inferior del bloque ya no es un área de colisión, el único área considerado de colisión está dentro del bloque. Con las cuatro comprobaciones envolvemos el área del bloque.

Ahora tendremos que hacer algo una vez estemos dentro de este área, igualaremos los valores "x" e "y" del personaje a oldx y oldy. No hemos definido aún esas variables, lo haremos al inicio del bucle principal. Esas variables tendrán el valor de "x" e "y" al comienzo de cada pasada del bucle. Si nuestra función determina que estamos dentro del área de colisión, volveremos a la posición anterior, la cual está almacenada en oldx y oldy, lo que nos hace creer que nunca podemos entrar dentro del bloque.

Esto es lo que hace la función. He intentado explicarlo con mucho detalle, así que espero que te haya ayudado a comprenderlo.

Lo siguiente es crear nuestro bucle principal:

--Bucle principal.
while true do
 
--Variables que almacenan la posicion del personaje al comienzo de cada bucle.
oldx = Personaje.x
oldy = Personaje.y
screen:clear()
 
 
--Comprobamos si hay movimiento.
moverPersonaje()
 
--Comprobamos si hay colisión para cada bloque.
comprobarColision(Bloque[1])
comprobarColision(Bloque[2])
comprobarColision(Bloque[3])
 
--Muestra el personaje en pantalla.
screen:blit(Personaje.x,Personaje.y,personaje1)
 
--Muestra los tres bloques en pantalla.
for a = 1,3 do
screen:blit(Bloque[a].x,Bloque[a].y,bloque1)
end
 
screen.waitVblankStart()
screen.flip()
end 

4.62069
Tu voto: Ninguno Votos totales: 4.6 (29 votos)

Anuncios Google

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de chocodino

bueno xD

yo cree mis propias colisiones, sin ayuda de este tutorial, y la verdad es casi igual, solo que este esta un paso mas avanzado xD, felicidades al creador del tutorial

Imagen de NightKnight

POR FIN YA SE PROGRAMAR EN

POR FIN YA SE PROGRAMAR EN LUA

EDITADO: El uso abusivo de Mayusculas no está permitido. Por favor, revise las normas de la comunidad. Gracias.hhuanag

muy buen tutto muy

muy buen tutto

muy util,gracias

Imagen de yoan03

gracias

muy bueno el tutto aunque un poco dificil de entender gracias elender

tarde lo se

es un poco tarde para esta pregunta pero digo que para la programacion no ahi hora ni tiempo.

mi pregunta es como le puedo añadir una imagenes en ves de los cuadros esos.

Saludos.

Imagen de Tanos

Movimiento

Necesito ayuda

Quiero saber como hago que una imagen se mueva por sí sola.

por favor contesten.

Imagen de PhAnThOm

Timers

Timers

Imagen de jjdrako

lo pregunto aqui

bueno te lo pregunto aqui, se que no tiene que ver con lo que explicas, pero ya que es el ultimo, queria preguntarte dos cosillas

1º hay funciones geometricas para crear por ejemplo un circulo?? o hay que hacerse una imagen de un circulo hecha en paint?? :(

2º si alguien sabe como en los juegos hechos con lua se les hace un eboot para poder jugar como si se hubiese hecho con el sdk


Diooooooos! que complicado

Diooooooos! que complicado no hubiera sido mejor poner
if pad:left() and personaje.x < bloque1.x then
Personaje.x = Personaje.x - 1
end
Asi no se mueve el personaje y solo hay que jugar un poco con el codigo.
Esto es demasiado tengo que combinar (para un ataque a distancia) un timer de animacion otro timer o for para la bola y otro para el choque y la animacion de la explosion, no hay otra forma?

yo creo

que asi lo que arias seria que el personaje se moviera en direccion contraria

Imagen de ELeNDeR

Pues si te parece complicado

Pues si te parece complicado leyendolo en español imagínate traducirlo del inglés Resumiendo un poco, lo que este código hace es definir el contorno de un bloque como área de colisión y así el jugador cuando llega a ese contorno, no puede atravesarlo. La función que contiene el IF es la que delimita el contorno del bloque. Esto es muy útil si quieres hacer un juego de plataformas, porque delimitas la plataforma y el personaje puede caminar sobre ella, pero no atravesarla. Espero que ahora quede más claro, saludos.

La verdad es que aun no lo

La verdad es que aun no lo he leido xD, pero lo he visto muy largo y con imagenes y me ha asustado, pensaba que era mas corto. Por cierto buen curro en la traduccion, de lo mejor (que digo, es lo mejor)
¿por cierto como pones la cara con lengua? voy a probar... :P

Imagen de Unicorn

Asi es

Elender esta en lo cierto. Es una de las ventajas que tenemos los miembros del Staff, o los VIP. Para saber como llegar a ser usuario VIP, visita este hilo.

Un saludo. 


Para recibir ayuda más rápidamente, recomendamos que pongas títulos descriptivos y no utilices abreviaturas (estilo MSN) en tus post de los foros. Recuerda revisar el Manual del perfecto forero y las Normas de la Comunidad.

jejeje... no se que decir

jejeje... no se que decir ¬¬

Imagen de ELeNDeR

Gracias N2eiDer, debo decir

Gracias N2eiDer, debo decir que es gratificante saber que mis traducciones ayudan a que tu juego sea cada vez más espectacular.

En cuanto a lo de las caritas, creo que al ser yo editor tengo esa opción, que un usuario normal no tiene. Subrayo creo porque no estoy completamente seguro de eso. Saludos.

Pues la verdad es que me he

Pues la verdad es que me he estancado, hay cosas del salto que no consigo arreglar. Y visto que te interesa bastante tal vez (mierda yo no se subrayar) podria interesarme tu ayuda, ya que hace mas de mes y medio que no veo a mordi, aunque sin su "empujoncito" como el dice me hubiera costado bastante hacer el resto

Imagen de ELeNDeR

Mandame un privado con tu

Mandame un privado con tu msn y te agrego, podemos intercambiar ideas.

eh!! mi comentario no sale entero

eh!! mi comentario no sale entero

Imagen de halcon994

no se si me estoy

no se si me estoy equivocando mucho, pero yo escribo este codigo inventado por mi del juego de la serpiente tipica de los nokia y otros moviles:

blanco = Color.new(255,255,255)
verde = Color.new(0,255,0)
azul = Color.new(0,0,255)
anchoPantalla = 480 - Serpiente:width()
altoPantalla = 272 - Serpiente:heigh()
--temporizador
Timer.new()
contador = Timer.new()
contador:start()
--serpiente
serpiente1 = Image.createEmpty(5,5)
serpiente1 = clear(verde)
--comida
comida1 = Image.createEmpty(5,5)
comida1 = clear(azul)
--array serpiente
Serpiente = { x = 100,y = 100 }
--definicion variables
SerpienteAltura = 5
SerpienteAnchura = 5
--array comida
Comida { x = 200,y = 58,Altura = 5,Anchura = 5 }
--funcion mover serpiente
function moverSerpiente
pad = Controls.read()
if pad:left() then
Serpiente.x = Serpiente.x - 1
end
if pad:right () then
Serpiente.x = Serpiente.x + 1
end
if pad:up() then
Serpiente.y = Serpiente.y - 1
end
if pad:down() then
Serpiente.y = Serpiente.y + 1
end
end
--funcion comida
function moverComida
screen:clear()
tiempoActual = contador:time()
if tiempoActual > 5000 and Comida.x > 50 then
Comida.x = Comida.x - 50
else
Comida.x = Comida.x + 200
end
if tiempoActual > 10000 and Comida.y > 50 then
contador:reset(0)
contador:start()
Comida.y = Comida.y - 50
else
contador:reset(0)
contador:start()
Comida.y = Comida.y + 200
end
end
while true do
screen:clear()
pad = Controls.read()
tiempoActual = contador:time()
moverSerpiente
moverComida
if Serpiente.x == Comida.x and Serpiente.y == Comida.y then
contador:reset(0)
contador:start
Serpiente.y = Serpiente.y + 5
end
screen.waitVblankStart()
screen.flip()
end

 

y cuando abro el archivo para ver si funciona, se cierra solo y no sale nada.

saludos y gracias


La esperanza es el sueño del hombre despierto

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.