Vamos a igualar las fuerzas y hacer el juego interesante, ellos también pueden disparar.
Vimos como disparar con nuestra nave y detectar impactos implementando la detección de colisiones de nuestros lasers en naves enemigas. Ahora repasamos conceptos y armamos el escuadron de aliens.
En primer lugar definimos un nuevo tipo de laser, así distinguiremos nuestros disparos de los demás. Incluimos las siguientes lineas en la función xloadobjs():
Definición
laserred01 = {}
laserred01.image = love.graphics.newImage("recursos/PNG/Lasers/laserRed03.png")
laserred01.W = laserred01.image:getWidth()
laserred01.H = laserred01.image:getHeight()
laserred01.sound=love.audio.newSource("recursos/Bonus/sfx_laser2.ogg", "static")
E inicializamos un array/tabla que contendrá los disparos enemigos, lasers{}
lasers={}
Inicialmente el array está vacio, cada vez que un enemigo dispare creamos un laser y lo insertamos en este array.
La función xalienshoot() incluye la lógica de la creación de disparo:
Disparo
function xalienshoot(a)
-- el alien establece el punto de partida del laser, pero a partir de ese momento
-- el laser es independiente, no se vincula al alien ya que aunque este ultimo sea destruido, el laser seguira existiendo.
if a.ctrshoot < 0 then
a.ctrshoot=200
laser={}
laser.W = laserred01.W
laser.H = laserred01.H
laser.x= a.x + (a.W/2) - (laserred01.W/2)
laser.y= a.y + a.H
laser.R = laserred01.W/2
table.insert(lasers, laser)
-- reproduce sonido laser
laserred01.sound:stop()
laserred01.sound:play()
end
end
Ok! ahora definimos cuando se realiza el disparo. Para ello, en la función xmovealiens() generamos un disparo cuando nuestra nave pasa por delante de una nave enemiga:
if a.x > player1.x-20 and a.x<player1.x+20 then
xalienshoot(a)
end
La función completa queda así:
-- Move Aliens
function xmovealiens(dt)
for _,a in pairs(aliens) do
a.x = a.x + (a.xdir * a.v * dt)
-- si llega a los bordes de la ventana cambia la dirección
if a.x > WINDOWW-a.W then
a.x = WINDOWW-a.W
a.xdir = -1
elseif a.x<0 then
a.x = 0
a.xdir = 1
end
if a.x > player1.x-20 and a.x<player1.x+20 then
xalienshoot(a)
end
if a.ctrshoot>0 then
a.ctrshoot = a.ctrshoot - 1000 * dt
end
xpuntocentral(a)
end
end
a.ctrshoot se utiliza como control para evitar ráfagas rápidas de disparos.
Ok, sigamos, ya tenemos la creación del disparo en el momento justo, ahora vamos a moverlos:
Movimiento
function xmovelasersred(dt)
-- estos lasers van d arriba a abajo
for ldel, laser in ipairs(lasers) do
if laser.y < WINDOWH then
laser.y = laser.y + 500 * dt
xpuntocentralL(laser)
else
-- si llega a la parte inferior de la ventana se elimina
table.remove(lasers, ldel)
end
end
end
El movimiento en este caso es descendente. Recuerda que el eje y comienza en 0 en la parte superior y se incrementa hasta el borde inferior de la ventana, por lo tanto para el desplazamiento hacia abajo sumamos a la posición y actual: laser.y = laser.y + 500 * dt
Fíjate que hay una función xpuntocentralL(). Es una variación de la utilizada para nuestros disparos:
function xpuntocentralL(obj)
-- debe actualizar siempre que hay movimiento
obj.cx = obj.x + obj.W/2 --punto central X para calculo de colisiones
obj.cy = obj.y + obj.H*0.2 --punto central Y para calculo de colisiones
end
De nada sirve una función si no la usamos, vamos a love.update(dt) y justo despues de la llamada a xmovealiens() añadimos la llamada a xmovelasersred()
Ya tenemos lasers enemigos, pero no serán visibles si no los pintamos, vamos a nuestra función de dibujo xdrawpngs() e incluimos el bucle que recorre la tabla lasers() y la dibuja:
Dibujo
for _,l in pairs(lasers) do
love.graphics.draw(laserred01.image, l.x, l.y)
end
La función xdrawpng() completa queda así:
function xdrawpngs()
if not pngs_visible then
return
end
-- Draw player
love.graphics.draw(player1.image, player1.x, player1.y)
love.graphics.print(player1.kills .. " kills", 30, 10)
love.graphics.print("lvl:" .. player1.level, WINDOWW-160, 10)
love.graphics.print("damage:" .. player1.damage, 30, 30)
-- Draw enemies
for _,a in pairs(aliens) do
love.graphics.draw(a.image, a.x, a.y)
end
-- Draw lasers
for _,l in pairs(player1.lasers) do
love.graphics.draw(laserblue01.image, l.x, l.y)
end
for _,l in pairs(lasers) do
love.graphics.draw(laserred01.image, l.x, l.y)
end
end
Genial! si ejecutas ahora debes ver lasers enemigos cada vez que pasemos por delante de sus naves.

Colisiones
Vamos a implementar las colisiones, para tener el código localizado creamos la función xcolisionesred(). La lógica es similar a el caso de nuestros lasers visto en la entrada IX:
function xcolisionesred()
-- comprobamos si alguno de los laserred impacta en player1
-- para ello, tenemos que recorrer los lasersred
p=player1
for ldel, l in ipairs(lasers) do
d=xdistancia(l, p)
if(d-60<(p.R+l.R)) then -- debug cuando estan cerca
if(d<(p.R+l.R)) then -- si la distancia entre objetos es menos que la suma de sus radios... BOOM!
print("impacto!" .. d .. "; RadioA:" .. p.R .. ", RadioL:" ..l.R)
p.damage = p.damage + 1 -- incrementa contador de daño
-- destruye el laser
table.remove(lasers, ldel)
break
end
end
end
end
Incluimos la llamada en love.update(), a continuación de xcolisiones().
La función completa update() debe quedar así:
function love.update(dt)
-- aqui ajustamos el tiempo
dtR=dt*1
if pause_down then
return
end
if left_down then
xmoveplayer(-1,0,dtR)
end
if right_down then
xmoveplayer(1,0,dtR)
end
xmovelasers(dtR)
xmovealiens(dtR)
xmovelasersred(dtR)
xcolisiones()
xcolisionesred()
xcompruebajuego()
end
Con esto hemos creado el ataque enemigo, ya podemos poner a prueba nuestra destreza esquivando lasers.
En la siguiente entrada… Explosiones!!!
Dejo el código completo de la entrada, aunque también puedes descargarlo todo incluidos los recursos del juego aquí:
function xInit()
print("altaruru intro a love2d")
print("ESCAPE TO QUIT")
-- recupera dimensiones de la ventana
WINDOWH = love.graphics.getHeight()
WINDOWW = love.graphics.getWidth()
-- inicia aleatorios:
math.randomseed(os.time()) -- random initialize
pause_down = false
pngs_visible = true
circle_visible = true
end
function xloadobjs()
font = love.graphics.newFont("recursos/Bonus/kenvector_future2.ttf", 30)
love.graphics.setFont(font)
background = {}
background.image = love.graphics.newImage('recursos/Backgrounds/blue.png')
background.W = background.image:getWidth()
background.H = background.image:getHeight()
background.sound=love.audio.newSource("recursos/musica/space1.mp3")
laserblue01 = {}
laserblue01.image = love.graphics.newImage("recursos/PNG/Lasers/laserBlue01.png")
laserblue01.W = laserblue01.image:getWidth()
laserblue01.H = laserblue01.image:getHeight()
laserblue01.sound=love.audio.newSource("recursos/Bonus/sfx_laser1.ogg", "static")
laserred01 = {}
laserred01.image = love.graphics.newImage("recursos/PNG/Lasers/laserRed03.png")
laserred01.W = laserred01.image:getWidth()
laserred01.H = laserred01.image:getHeight()
laserred01.sound=love.audio.newSource("recursos/Bonus/sfx_laser2.ogg", "static")
player1 = {}
player1.image = love.graphics.newImage("recursos/PNG/playerShip1_blue.png")
player1.W = player1.image:getWidth() -- ancho de la nave
player1.H = player1.image:getHeight() -- alto de la nave
player1.x=(WINDOWW-player1.W)/2 -- posicion inicial x
player1.y=WINDOWH-player1.H-10 -- posicion inicial y
xpuntocentral(player1)
player1.R = player1.W/2 -- radio aprox para pruebas
player1.v = 300 --velocidad de la nave
player1.ctrshoot = -1 -- control de disparos
player1.kills = 0
player1.level = 1
player1.damage = 0
-- tabla lasers
player1.lasers={}
-- tabla lasers global, los lasers son indpendientes de los aliens, estos solo establecen el punto de partida
lasers={}
-- tabla de aliens
aliens = {}
-- crea aliens por defecto
xnewalien(0)
xnewalien(1)
xnewalien(2)
end
function xnewalien(fila)
alien1 = {}
alien1.image = love.graphics.newImage("recursos/PNG/Enemies/enemyBlack1.png")
alien1.fila=fila;
alien1.W = alien1.image:getWidth() -- ancho de la nave
alien1.H = alien1.image:getHeight() -- alto de la nave
alien1.x = math.random(1,WINDOWW-alien1.H) -- posicion inicial x
alien1.y = 36 + (fila*alien1.H) -- posicion inicial y
xpuntocentral(alien1)
alien1.R = alien1.W*0.5 -- radio aprox para pruebas
alien1.v = 100 + (5*math.random(10,40)) --velocidad de la nave variable, entre 150 y 300 a intervalos de 5
alien1.xdir = 1 -- afecta al sentido del movimiento en el eje X. 1derecha, -1izquierda
alien1.ctrshoot=-1 -- control de disparos
-- inserta en la tabla
table.insert(aliens, alien1)
end
function xdistancia(obj1, obj2)
-- utiliza el punto central de cada objeto
d=math.sqrt(math.pow(obj2.cx-obj1.cx,2)+math.pow(obj2.cy-obj1.cy,2))
return d
end
function xpuntocentral(obj)
-- debe actualizar siempre que hay movimiento
obj.cx = obj.x + obj.W/2 --punto central X para calculo de colisiones
obj.cy = obj.y + obj.H/2 --punto central Y para calculo de colisiones
end
function xpuntocentralL(obj)
-- debe actualizar siempre que hay movimiento
obj.cx = obj.x + obj.W/2 --punto central X para calculo de colisiones
obj.cy = obj.y + obj.H*0.2 --punto central Y para calculo de colisiones
end
function xcolisiones()
-- comprobamos si alguno de nuestros laser impacta en los aliens
-- para ello, tenemos que recorrer los lasers y los aliens
for ldel, l in ipairs(player1.lasers) do
--love.graphics.draw(laserblue01.image, l.x, l.y)
for adel,a in ipairs(aliens) do
--love.graphics.draw(a.image, a.x, a.y)
d=xdistancia(l, a)
if(d-60<(a.R+l.R)) then -- debug cuando estan cerca
print(a.fila .. ">" .. d .. " * " .. a.R+l.R)
if(d<(a.R+l.R)) then -- si la distancia entre objetos es menos que la suma de sus radios... BOOM!
print("impacto!" .. d .. "; RadioA:" .. a.R .. ", RadioL:" ..l.R)
player1.kills = player1.kills + 1 -- incrementa contador de muertes
-- destruye el laser
table.remove(player1.lasers, ldel)
-- destruye alien
table.remove(aliens, adel)
break
end
end
end
end
end
function xcolisionesred()
-- comprobamos si alguno de los laserred impacta en player1
-- para ello, tenemos que recorrer los lasersred
p=player1
for ldel, l in ipairs(lasers) do
d=xdistancia(l, p)
if(d-60<(p.R+l.R)) then -- debug cuando estan cerca
if(d<(p.R+l.R)) then -- si la distancia entre objetos es menos que la suma de sus radios... BOOM!
print("impacto!" .. d .. "; RadioA:" .. p.R .. ", RadioL:" ..l.R)
p.damage = p.damage + 1 -- incrementa contador de daño
-- destruye el laser
table.remove(lasers, ldel)
break
end
end
end
end
function love.load()
xInit()
xloadobjs()
background.sound:setVolume(0.3) -- 30% del volumen general
background.sound:setLooping(true)
background.sound:play()
end
function xdrawpoints()
if not circle_visible then
return
end
wpoint=6
-- Draw player
love.graphics.setColor(80, 255, 0)
love.graphics.rectangle("fill", player1.cx, player1.cy, wpoint, wpoint)
love.graphics.circle("line", player1.cx, player1.cy, player1.R)
-- Draw enemies
love.graphics.setColor(200, 0, 200)
for _,a in pairs(aliens) do
love.graphics.rectangle("fill", a.cx, a.cy, wpoint, wpoint)
love.graphics.circle("line", a.cx, a.cy, a.R)
end
-- Draw lasers
love.graphics.setColor(190, 170, 0)
for _,l in pairs(player1.lasers) do
love.graphics.circle("line", l.cx, l.cy, l.R)
end
-- Draw lasers red
love.graphics.setColor(240, 20, 20)
for _,l in pairs(lasers) do
love.graphics.circle("line", l.cx, l.cy, l.R)
end
end
function xdrawpngs()
if not pngs_visible then
return
end
-- Draw player
love.graphics.draw(player1.image, player1.x, player1.y)
love.graphics.print(player1.kills .. " kills", 30, 10)
love.graphics.print("lvl:" .. player1.level, WINDOWW-160, 10)
love.graphics.print("damage:" .. player1.damage, 30, 30)
-- Draw enemies
for _,a in pairs(aliens) do
love.graphics.draw(a.image, a.x, a.y)
end
-- Draw lasers
for _,l in pairs(player1.lasers) do
love.graphics.draw(laserblue01.image, l.x, l.y)
end
for _,l in pairs(lasers) do
love.graphics.draw(laserred01.image, l.x, l.y)
end
end
function love.draw()
-- Draw background
love.graphics.setColor(255, 255, 255)
love.graphics.draw(background.image, 0, 0, 0, WINDOWW/background.W, WINDOWH/background.H)
xdrawpngs()
xdrawpoints()
end
function xmoveplayer(x, y, dt)
player1.x = player1.x + (x * player1.v * dt)
xpuntocentral(player1)
if player1.ctrshoot>0 then
player1.ctrshoot = player1.ctrshoot - 1000 * dt
end
end
-- Move Aliens
function xmovealiens(dt)
for _,a in pairs(aliens) do
a.x = a.x + (a.xdir * a.v * dt)
-- si llega a los bordes de la ventana cambia la dirección
if a.x > WINDOWW-a.W then
a.x = WINDOWW-a.W
a.xdir = -1
elseif a.x<0 then
a.x = 0
a.xdir = 1
end
if a.x > player1.x-20 and a.x<player1.x+20 then
xalienshoot(a)
end
if a.ctrshoot>0 then
a.ctrshoot = a.ctrshoot - 1000 * dt
end
xpuntocentral(a)
end
end
function xmovelasers(dt)
for ldel, laser in ipairs(player1.lasers) do
if laser.y > -5 then
laser.y= laser.y - 500 * dt
xpuntocentralL(laser)
else
-- si llega a la parte superior de la ventana se elimina
table.remove(player1.lasers, ldel)
player1.ctrshoot=-1 -- temporal
end
end
end
function xmovelasersred(dt)
-- estos lasers van d arriba a abajo
for ldel, laser in ipairs(lasers) do
if laser.y < WINDOWH then
laser.y = laser.y + 500 * dt
xpuntocentralL(laser)
else
-- si llega a la parte superior de la ventana se elimina
table.remove(lasers, ldel)
end
end
end
function xplayershoot()
if player1.ctrshoot < 0 then
player1.ctrshoot=10
laser={}
laser.W = laserblue01.W
laser.H = laserblue01.H
laser.x= player1.x + (player1.W/2) - (laserblue01.W/2)
laser.y= player1.y - laserblue01.H
laser.R = laserblue01.W/2
table.insert(player1.lasers, laser)
-- reproduce sonido laser
laserblue01.sound:stop()
laserblue01.sound:play()
end
end
function xalienshoot(a)
-- el alien establece el punto de partida del laser, pero a partir de ese momento
-- el laser es independiente, no se vincula al alien ya que aunque este ultimo sea destruido, el laser seguira existiendo.
if a.ctrshoot < 0 then
a.ctrshoot=200
laser={}
laser.W = laserred01.W
laser.H = laserred01.H
laser.x= a.x + (a.W/2) - (laserred01.W/2)
laser.y= a.y + a.H
laser.R = laserred01.W/2
table.insert(lasers, laser)
-- reproduce sonido laser
laserred01.sound:stop()
laserred01.sound:play()
end
end
function love.update(dt)
-- aqui ajustamos el tiempo
dtR=dt*1
if pause_down then
return
end
if left_down then
xmoveplayer(-1,0,dtR)
end
if right_down then
xmoveplayer(1,0,dtR)
end
xmovelasers(dtR)
xmovealiens(dtR)
xmovelasersred(dtR)
xcolisiones()
xcolisionesred()
xcompruebajuego()
end
function xnewlevel()
player1.level = player1.level + 1
print("bien hecho!")
xnewalien(0)
xnewalien(1)
xnewalien(3)
end
function xcompruebajuego()
if(#aliens==0) then
xnewlevel()
end
end
function love.keypressed(key)
if key == 'left' then
left_down = true
elseif key == 'right' then
right_down = true
end
if key == " " then
xplayershoot()
end
if key == "p" then
-- cada pulsación de P activa o desactiva la pausa
pause_down = not pause_down
end
if key == "7" then
-- muestra oculta imagenes
pngs_visible = not pngs_visible
end
if key == "8" then
-- muestra oculta circulos detección colisiones
circle_visible = not circle_visible
end
if key == "escape" then
love.event.quit()
end
end
function love.keyreleased(key, unicode)
if key == 'left' then
left_down = false
elseif key == 'right' then
right_down = false
end
if key == " " then
space_down = false
end
end
function love.quit()
print("¡Hasta pronto!")
end