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!!!

Temario del curso.

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

Deja tu comentario