Continuando la anterior parte del tutorial, vamos a crear a nuestro héroe. Añadiento eventos de teclado de Love2d, colisiones para que no se salga del nivel, y animación para realizar el caminado.
Héroe
Creamos en scripts un archivo llamado hero.lua, y definiremos su estructura.
local hero = {}
function hero.load()
end
function hero.update(dt)
end
function hero.draw()
end
return hero
Y lo añadimos a main.lua.
local game = require 'assets/scripts/game'
local level = require 'assets/scripts/level'
local hero = require 'assets/scripts/hero'
-- Cargar
function love.load()
game.load()
level.load()
hero.load()
end
-- Actualizar
function love.update(dt)
-- Por ahora no tenemos nada que actualizar
end
-- Pintar
function love.draw()
level.draw()
hero.draw()
end
-- Eventos
function love.keypressed(key, scancode, isrepeat)
-- Eventos de teclado: apretando una tecla
end
function love.keyreleased(key, scancode)
-- Eventos de teclado: Soltando una tecla
end
En hero.lua cargamos y dibujamos el sprite.
local hero = {}
function hero.load()
hero.img = love.graphics.newImage('assets/sprites/hero/hero.png')
hero.x = 100
hero.y = 400
end
function hero.update(dt)
end
function hero.draw()
love.graphics.draw(hero.img, hero.x, hero.y)
end
return hero
Nos encontramos con un traspié, se muestran las 4 imágenes de la animación. Lo gestionaremos, pero antes debemos tener claro para que sirve cada una.
- 1: Estado en reposo.
- 2 y 3: Caminando.
- 4: Atacando.
Para facilitarnos la vida vamos a utilizar un plugin ya realizado y compartido por un bondadoso usuario: anim8
Descargaremos anim8.lua y lo dejaremos en vendor. Editamos hero.lua.
local anim8 = require 'assets/scripts/vendor/anim8'
local hero = {}
function hero.load()
hero.img = love.graphics.newImage('assets/sprites/hero/hero.png')
hero.x = 100
hero.y = 400
hero.num_frames = 4 -- Tenemos 4 posiciones a animar
hero.width = hero.img:getWidth() / hero.num_frames -- Debemos indicarle cuanto mide un cuadro de ancho. En nuestro caso es sencillo: la anchura de la imagen dividida por 4, o el número de frames
hero.height = hero.img:getHeight() -- También con la altura. Al ser horizontal nos vale la altura de la imagen.
g = anim8.newGrid(hero.width, hero.height, hero.img:getWidth(), hero.img:getHeight()) -- Creamos el grid de anim8
-- Animaciones
hero.animations = {}
hero.animations.stop = anim8.newAnimation(g('1-1', 1), 1) -- Creamos la animación: Irá del frame 1 al 1, estará en la posición 1 dentro de anim8 y durará 1 seg (no podemos decirle que dure 0)
end
function hero.update(dt)
hero.animations.stop:update(dt) -- Declaramos anim8 para que haga sus cálculos internos.
end
function hero.draw()
hero.animations.stop:draw(hero.img, hero.x, hero.y) -- Mostramos la animación.
end
return hero
Antes de seguir animándolo, vamos a moverlo con el teclado. Editamos hero.lua.
local anim8 = require 'assets/scripts/vendor/anim8'
local hero = {}
function hero.load()
hero.img = love.graphics.newImage('assets/sprites/hero/hero.png')
hero.x = 100
hero.y = 400
hero.num_frames = 4 -- Tenemos 4 posiciones a animar
hero.width = hero.img:getWidth() / hero.num_frames -- Debemos indicarle cuanto mide un cuadro de ancho. En nuestro caso es sencillo: la anchura de la imagen dividida por 4, o el número de frames
hero.height = hero.img:getHeight() -- También con la altura. Al ser horizontal nos vale la altura de la imagen.
g = anim8.newGrid(hero.width, hero.height, hero.img:getWidth(), hero.img:getHeight()) -- Creamos el grid de anim8
-- Animaciones
hero.animations = {}
hero.animations.stop = anim8.newAnimation(g('1-1', 1), 1) -- Creamos la animación: Irá del frame 1 al 1, estará en la posición 1 dentro de anim8 y durará 1 seg (no podemos decirle que dure 0)
-- Controls
BUTTON_RIGHT = false
end
function hero.update(dt)
hero.animations.stop:update(dt) -- Declaramos anim8 para que haga sus cálculos internos.
if BUTTON_RIGHT then
hero.x = hero.x + 1 -- Le indicamos que cuando esté pulsado el botón aumente en x la posición del personaje
end
end
function hero.draw()
hero.animations.stop:draw(hero.img, hero.x, hero.y) -- Mostramos la animación. En este caso esta parada.
end
function hero.keypressed(key, scancode, isrepeat) -- Que haremos cuando se pulse un botón
if key == 'right' then -- Si la tecla pulsado es 'right' (flecha derecha del teclado)
BUTTON_RIGHT = true -- Indicamos que se ha pulsado
end
end
function hero.keyreleased(key, scancode) -- Que haremos cuando se suelte una tecla
if key == 'right' then
BUTTON_RIGHT = false -- Indicamos que se ha soltado
end
end
return hero
Y para reflejar los cambios en main.lua.
local game = require 'assets/scripts/game'
local level = require 'assets/scripts/level'
local hero = require 'assets/scripts/hero'
-- Cargar
function love.load()
game.load()
level.load()
hero.load()
end
-- Actualizar
function love.update(dt)
hero.update(dt)
end
-- Pintar
function love.draw()
level.draw()
hero.draw()
end
-- Eventos
function love.keypressed(key, scancode, isrepeat)
hero.keypressed(key, scancode, isrepeat)
end
function love.keyreleased(key, scancode)
hero.keyreleased(key, scancode, isrepeat)
end
Repetimos con el resto de las direcciones.
local anim8 = require 'assets/scripts/vendor/anim8'
local hero = {}
function hero.load()
hero.img = love.graphics.newImage('assets/sprites/hero/hero.png')
hero.x = 100
hero.y = 400
hero.num_frames = 4 -- Tenemos 4 posiciones a animar
hero.width = hero.img:getWidth() / hero.num_frames -- Debemos indicarle cuanto mide un cuadro de ancho. En nuestro caso es sencillo: la anchura de la imagen dividida por 4, o el número de frames
hero.height = hero.img:getHeight() -- También con la altura. Al ser horizontal nos vale la altura de la imagen.
g = anim8.newGrid(hero.width, hero.height, hero.img:getWidth(), hero.img:getHeight()) -- Creamos el grid de anim8
-- Animaciones
hero.animations = {}
hero.animations.stop = anim8.newAnimation(g('1-1', 1), 1) -- Creamos la animación: Irá del frame 1 al 1, estará en la posición 1 dentro de anim8 y durará 1 seg (no podemos decirle que dure 0)
-- Controls
BUTTON_RIGHT = false
BUTTON_LEFT = false
BUTTON_UP = false
BUTTON_DOWN = false
end
function hero.update(dt)
hero.animations.stop:update(dt) -- Declaramos anim8 para que haga sus cálculos internos.
if BUTTON_RIGHT then
hero.x = hero.x + 1 -- Le indicamos que cuando esté pulsado el botón aumente en x la posición del personaje
elseif BUTTON_LEFT then
hero.x = hero.x - 1
elseif BUTTON_UP then
hero.y = hero.y - 1
elseif BUTTON_DOWN then
hero.y = hero.y + 1
end
end
function hero.draw()
hero.animations.stop:draw(hero.img, hero.x, hero.y) -- Mostramos la animación. En este caso esta parada.
end
function hero.keypressed(key, scancode, isrepeat) -- Que haremos cuando se pulse un botón
if key == 'right' then -- Si la tecla pulsado es 'right' (flecha derecha del teclado)
BUTTON_RIGHT = true -- Indicamos que se ha pulsado
elseif key == 'left' then
BUTTON_LEFT = true
elseif key == 'up' then
BUTTON_UP = true
elseif key == 'down' then
BUTTON_DOWN = true
end
end
function hero.keyreleased(key, scancode) -- Que haremos cuando se suelte una tecla
if key == 'right' then -- Si la tecla pulsado es 'right' (flecha derecha del teclado)
BUTTON_RIGHT = false -- Indicamos que se ha pulsado
elseif key == 'left' then
BUTTON_LEFT = false
elseif key == 'up' then
BUTTON_UP = false
elseif key == 'down' then
BUTTON_DOWN = false
end
end
return hero
Ahora le aplicamos la animación de andar.
local anim8 = require 'assets/scripts/vendor/anim8'
local hero = {}
function hero.load()
hero.img = love.graphics.newImage('assets/sprites/hero/hero.png')
hero.x = 100
hero.y = 400
hero.num_frames = 4 -- Tenemos 4 posiciones a animar
hero.width = hero.img:getWidth() / hero.num_frames -- Debemos indicarle cuanto mide un cuadro de ancho. En nuestro caso es sencillo: la anchura de la imagen dividida por 4, o el número de frames
hero.height = hero.img:getHeight() -- También con la altura. Al ser horizontal nos vale la altura de la imagen.
g = anim8.newGrid(hero.width, hero.height, hero.img:getWidth(), hero.img:getHeight()) -- Creamos el grid de anim8
-- Animaciones
hero.animations = {}
hero.animations.stop = anim8.newAnimation(g('1-1', 1), 1) -- Creamos la animación: Irá del frame 1 al 1, estará en la posición 1 dentro de anim8 y durará 1 seg (no podemos decirle que dure 0)
hero.animations.walk = anim8.newAnimation(g('2-3', 1), 0.5) -- Creamos la animación de andar: su intervalo será entre frame y frame de 0.5 segundos
-- Controls
BUTTON_RIGHT = false
BUTTON_LEFT = false
BUTTON_UP = false
BUTTON_DOWN = false
end
function hero.update(dt)
hero.animations.stop:update(dt) -- Declaramos anim8 para que haga sus cálculos internos.
hero.animations.walk:update(dt)
if BUTTON_RIGHT then
hero.x = hero.x + 1 -- Le indicamos que cuando esté pulsado el botón aumente en x la posición del personaje
elseif BUTTON_LEFT then
hero.x = hero.x - 1
elseif BUTTON_UP then
hero.y = hero.y - 1
elseif BUTTON_DOWN then
hero.y = hero.y + 1
end
end
function hero.draw()
if BUTTON_DOWN or BUTTON_UP or BUTTON_LEFT or BUTTON_RIGHT then
hero.animations.walk:draw(hero.img, hero.x, hero.y) -- Si se pulsa algún botón, mostramos la animación de caminado.
else
hero.animations.stop:draw(hero.img, hero.x, hero.y) -- En caso contrario solo mostrarmos la animación de parado
end
end
function hero.keypressed(key, scancode, isrepeat) -- Que haremos cuando se pulse un botón
if key == 'right' then -- Si la tecla pulsado es 'right' (flecha derecha del teclado)
BUTTON_RIGHT = true -- Indicamos que se ha pulsado
elseif key == 'left' then
BUTTON_LEFT = true
elseif key == 'up' then
BUTTON_UP = true
elseif key == 'down' then
BUTTON_DOWN = true
end
end
function hero.keyreleased(key, scancode) -- Que haremos cuando se suelte una tecla
if key == 'right' then -- Si la tecla pulsado es 'right' (flecha derecha del teclado)
BUTTON_RIGHT = false -- Indicamos que se ha pulsado
elseif key == 'left' then
BUTTON_LEFT = false
elseif key == 'up' then
BUTTON_UP = false
elseif key == 'down' then
BUTTON_DOWN = false
end
end
return hero
La única pega es que al ir hacia atrás nos hará el moonwalker. Tendremos que volvearlo hacia un lado o al otro cuando pulsemos en las teclas de right o de left.
Primero clonaremos la animación, pero dándole la vuelta en vertical con flip. Después añadiremos una variable llamada flip para controlar si esta dado la vuelta o no. E iremos cambiando la animación dependiendo de esa variable.
También añadiremos hero.vel para controlar la velocidad del personaje. Lo he subido a 2 ya que iba muy lento.
local anim8 = require 'assets/scripts/vendor/anim8'
local hero = {}
function hero.load()
hero.img = love.graphics.newImage('assets/sprites/hero/hero.png')
hero.x = 100
hero.y = 400
hero.num_frames = 4 -- Tenemos 4 posiciones a animar
hero.vel = 2
hero.width = hero.img:getWidth() / hero.num_frames -- Debemos indicarle cuanto mide un cuadro de ancho. En nuestro caso es sencillo: la anchura de la imagen dividida por 4, o el número de frames
hero.height = hero.img:getHeight() -- También con la altura. Al ser horizontal nos vale la altura de la imagen.
g = anim8.newGrid(hero.width, hero.height, hero.img:getWidth(), hero.img:getHeight()) -- Creamos el grid de anim8
-- Animaciones
hero.animations = {}
hero.animations.stop = anim8.newAnimation(g('1-1', 1), 1) -- Creamos la animación: Irá del frame 1 al 1, estará en la posición 1 dentro de anim8 y durará 1 seg (no podemos decirle que dure 0)
hero.animations.stop_flip = hero.animations.stop:clone():flipH() -- Clonamos y volteamos la animación
hero.animations.walk = anim8.newAnimation(g('2-3', 1), 0.5) -- Creamos la animación de andar: su intervalo será entre frame y frame de 0.5 segundos
hero.animations.walk_flip = hero.animations.walk:clone():flipH() -- Clonamos y volteamos la animación
-- Controls
BUTTON_RIGHT = false
BUTTON_LEFT = false
BUTTON_UP = false
BUTTON_DOWN = false
flip = false -- Con esta variable controlaremos si esta volteado o no
end
function hero.update(dt)
hero.animations.stop:update(dt) -- Declaramos anim8 para que haga sus cálculos internos.
hero.animations.stop_flip:update(dt)
hero.animations.walk:update(dt)
hero.animations.walk_flip:update(dt)
if BUTTON_RIGHT then
hero.x = hero.x + hero.vel -- Le indicamos que cuando esté pulsado el botón aumente en x la posición del personaje
elseif BUTTON_LEFT then
hero.x = hero.x - hero.vel
elseif BUTTON_UP then
hero.y = hero.y - hero.vel
elseif BUTTON_DOWN then
hero.y = hero.y + hero.vel
end
end
function hero.draw()
if BUTTON_DOWN or BUTTON_UP or BUTTON_LEFT or BUTTON_RIGHT then
if flip then
hero.animations.walk_flip:draw(hero.img, hero.x, hero.y) -- Si se pulsa algún botón, mostramos la animación de caminado.
else
hero.animations.walk:draw(hero.img, hero.x, hero.y) -- Si se pulsa algún botón, mostramos la animación de caminado.
end
else
if flip then
hero.animations.stop_flip:draw(hero.img, hero.x, hero.y) -- En caso contrario solo mostrarmos la animación de parado
else
hero.animations.stop:draw(hero.img, hero.x, hero.y) -- En caso contrario solo mostrarmos la animación de parado
end
end
end
function hero.keypressed(key, scancode, isrepeat) -- Que haremos cuando se pulse un botón
if key == 'right' then -- Si la tecla pulsado es 'right' (flecha derecha del teclado)
BUTTON_RIGHT = true -- Indicamos que se ha pulsado
flip = false
elseif key == 'left' then
BUTTON_LEFT = true
flip = true
elseif key == 'up' then
BUTTON_UP = true
elseif key == 'down' then
BUTTON_DOWN = true
end
end
function hero.keyreleased(key, scancode) -- Que haremos cuando se suelte una tecla
if key == 'right' then -- Si la tecla pulsado es 'right' (flecha derecha del teclado)
BUTTON_RIGHT = false -- Indicamos que se ha pulsado
elseif key == 'left' then
BUTTON_LEFT = false
elseif key == 'up' then
BUTTON_UP = false
elseif key == 'down' then
BUTTON_DOWN = false
end
end
return hero
Ya solo quedará obligar a que no se salga fuera de la calle.
Espero que este ejercicio te haya servido como introducción a la programación de videojuegos y a Love2D.
Si hay muchos comentarios prometo realizar más partes.
{{ comments.length }} comentarios