
В предыдущей статье мы создали простую заготовку для нашего будущего приложения на движке Love2D. Никаких полезных действий она ещё не умеет делать, просто запускает бесконечный цыкл и ждёт закрытия окна. В этой статье мы рассмотрим обработку ввода с клавиатуры и мыши. Было принято решение повременить с мобильными устройствами, так что приложение будет работать пока только на Desktop платформах.
Для удобства было решено запихнуть обработчик ввода в отдельную систему, чтобы не использовать функции движка love.keypressed, love.mousepressed и другие.
Наша система ввода будет отслеживать следующие состояния:
Весь код мы будем добавлять в файл App.lua из предыдущей статьи, так как в нём находится обработка всех событий. В конце статьи как всегда будут выложены исходные коды с доработками.
Для начала нам нужно создать новый объект и добавить к нему несколько переменных. Назовём его Input.
...
-- Система ввода
Input = {}
App.Input = {}
App.Input._keys = {}
App.Input._buttons = {}
App.Input._wheel = 0
App.Input._mousePos = Vector( 0, 0 )
...
В массив _keys будут заноситься все нажатые клавиши клавиатуры, а в массив _buttons соответственно все нажатые кнопки мыши. Переменная _wheel будет принимать значение -1 или 1 в зависимости от того, в какую сторону была осуществлена прокрутка колёсиком мыши. В конце цикла мы будем выставлять ей значение 0. В векторе _mousePos будут находиться текущие координаты курсора мыши.
Теперь нам нужно модифицировать функцию App.run(). Для начала определим текущие координаты курсора мыши и занесём их в вектор _mousePos:
... function App.run() local mouseX, mouseY = love.mouse.getPosition() App.Input._mousePos = Vector( mouseX, mouseY ) ... end
В месте обработки событий мы будем определять какие нажаты клавиши и заносить их в соответствующие массивы. Там же будем определять однократное нажатие или удерживание клавиш. Делается это следующим кодом:
...
function App.run()
...
if love.event then
love.event.pump()
local _keys = {}
local _btns = {}
for e, a, b, c, d in love.event.poll() do
if e == "quit" then
App.RUNNING = false
elseif e == "keypressed" then
App.Input._keys[a] = 1
_keys[ #_keys + 1 ] = a
elseif e == "keyreleased" then
App.Input._keys[a] = 0
_keys[ #_keys + 1 ] = a
elseif e == "mousepressed" then
App.Input._buttons[c] = 1
_btns[ #_btns + 1 ] = c
if c == "wu" then
App.Input._wheel = 1
elseif c == "wd" then
App.Input._wheel = -1
end
elseif e == "mousereleased" then
App.Input._buttons[c] = 0
_btns[ #_btns + 1 ] = c
end
for key, value in pairs( App.Input._keys ) do
if value == 0 then
local keyFound = false
for i = 1, #_keys do
if _keys[i] == key then
keyFound = true
break
end
end
if not keyFound then
App.Input._keys[ key ] = -1
end
elseif value == 1 then
local keyFound = false
for i = 1, #_keys do
if _keys[i] == key then
keyFound = true
break
end
end
if not keyFound then
App.Input._keys[ key ] = 2
end
end
end
for key, value in pairs( App.Input._buttons ) do
if value == 0 then
local keyFound = false
for i = 1, #_btns do
if _btns[i] == key then
keyFound = true
break
end
end
if not keyFound then
App.Input._buttons[ key ] = -1
end
elseif value == 1 then
local keyFound = false
for i = 1, #_btns do
if _btns[i] == key then
keyFound = true
break
end
end
if not keyFound then
App.Input._buttons[ key ] = 2
end
end
end
love.handlers[e]( a, b, c, d )
end
end
...
end
Здесь думаю всё понятно и подробно расписывать не имеет смысла. Осталось добавить понятные функции, к которым можно будет обращаться для получения наших значений. Вот они:
...
function Input.reset()
App.Input._wheel = 0
end
function Input.pressed()
for _, v in pairs( App.Input._keys ) do
if v > 0 then
return true
end
end
return false
end
function Input.clicked()
for _, v in pairs( App.Input._buttons ) do
if v > 0 then
return true
end
end
return false
end
function Input.keyDown( key )
return App.Input._keys[ key ] == 1
end
function Input.keyUp( key )
return App.Input._keys[ key ] == 0
end
function Input.keyHeld( key )
return App.Input._keys[ key ] == 2
end
function Input.mouseDown( button )
return App.Input._buttons[ button ] == 1
end
function Input.mouseUp( button )
return App.Input._buttons[ button ] == 0
end
function Input.mouseHeld( button )
return App.Input._buttons[ button ] == 2
end
function Input.wheelUp()
return App.Input._wheel == 1
end
function Input.wheelDown()
return App.Input._wheel == -1
end
function Input.mousePos()
return App.Input._mousePos
end
...
В конечном итоге наш файл App.lua будет выглядеть следующим образом:
App = {}
App.OS = love.system.getOS()
App.WIDTH = love.graphics.getWidth()
App.HEIGHT = love.graphics.getHeight()
App.RUNNING = true
-- Система ввода
Input = {}
App.Input = {}
App.Input._keys = {}
App.Input._buttons = {}
App.Input._wheel = 0
App.Input._mousePos = Vector( 0, 0 )
function App.run()
local mouseX, mouseY = love.mouse.getPosition()
App.Input._mousePos = Vector( mouseX, mouseY )
if love.event then
love.event.pump()
local _keys = {}
local _btns = {}
for e, a, b, c, d in love.event.poll() do
if e == "quit" then
App.RUNNING = false
elseif e == "keypressed" then
App.Input._keys[a] = 1
_keys[ #_keys + 1 ] = a
elseif e == "keyreleased" then
App.Input._keys[a] = 0
_keys[ #_keys + 1 ] = a
elseif e == "mousepressed" then
App.Input._buttons[c] = 1
_btns[ #_btns + 1 ] = c
if c == "wu" then
App.Input._wheel = 1
elseif c == "wd" then
App.Input._wheel = -1
end
elseif e == "mousereleased" then
App.Input._buttons[c] = 0
_btns[ #_btns + 1 ] = c
end
for key, value in pairs( App.Input._keys ) do
if value == 0 then
local keyFound = false
for i = 1, #_keys do
if _keys[i] == key then
keyFound = true
break
end
end
if not keyFound then
App.Input._keys[ key ] = -1
end
elseif value == 1 then
local keyFound = false
for i = 1, #_keys do
if _keys[i] == key then
keyFound = true
break
end
end
if not keyFound then
App.Input._keys[ key ] = 2
end
end
end
for key, value in pairs( App.Input._buttons ) do
if value == 0 then
local keyFound = false
for i = 1, #_btns do
if _btns[i] == key then
keyFound = true
break
end
end
if not keyFound then
App.Input._buttons[ key ] = -1
end
elseif value == 1 then
local keyFound = false
for i = 1, #_btns do
if _btns[i] == key then
keyFound = true
break
end
end
if not keyFound then
App.Input._buttons[ key ] = 2
end
end
end
love.handlers[e]( a, b, c, d )
end
end
return App.RUNNING
end
function App.close()
App.RUNNING = false
end
-- >> INPUT
function Input.reset()
App.Input._wheel = 0
end
function Input.pressed()
for _, v in pairs( App.Input._keys ) do
if v > 0 then
return true
end
end
return false
end
function Input.clicked()
for _, v in pairs( App.Input._buttons ) do
if v > 0 then
return true
end
end
return false
end
function Input.keyDown( key )
return App.Input._keys[ key ] == 1
end
function Input.keyUp( key )
return App.Input._keys[ key ] == 0
end
function Input.keyHeld( key )
return App.Input._keys[ key ] == 2
end
function Input.mouseDown( button )
return App.Input._buttons[ button ] == 1
end
function Input.mouseUp( button )
return App.Input._buttons[ button ] == 0
end
function Input.mouseHeld( button )
return App.Input._buttons[ button ] == 2
end
function Input.wheelUp()
return App.Input._wheel == 1
end
function Input.wheelDown()
return App.Input._wheel == -1
end
function Input.mousePos()
return App.Input._mousePos
end
-- << INPUT
return App
Теперь из любого места приложения мы можем обратиться к объекту Input и получить нужные нам данные.
Для проверки работоспособности данной системы мы выведем на экран спрайт и заставим его поворачиваться в сторону курсора. По нажатию клавиши ESC приложение будет закрыто. Для этого в папке с проектом нужно создать дополнительную директорию data, в которую нужно будет положить нужный нам спрайт. Вывод спрайта будет осуществляться в файле main.lua, так что настало самое время его открыть и слегка модифицировать.
require "app"
function love.load()
-- изображение
mac = love.graphics.newImage("data/mac.png")
-- размер изображения
size = Vector( mac:getWidth(), mac:getHeight() )
-- координаты
pos = Vector( 400, 300 )
-- текущий угол поворота
angle = 0
end
function love.run()
if love.math then
love.math.setRandomSeed( os.time() )
end
if love.event then
love.event.pump()
end
if love.load then
love.load( arg )
end
if not (love.window and love.graphics and love.window.isCreated() and love.timer) then
return
end
while App.run() do
love.timer.step()
local dt = love.timer.getDelta()
-- update
if Input.keyDown("escape") then
App.close()
end
local mousePos = Input.mousePos()
local dir = pos - mousePos
angle = -math.atan2( dir.x, dir.y ) / (math.pi / 180)
Input.reset()
love.graphics.clear()
love.graphics.origin()
-- draw
love.graphics.draw( mac,
pos.x,
pos.y,
math.rad( angle ),
1,
1,
size.x / 2,
size.y / 2 )
love.graphics.present()
love.timer.sleep( 0.001 )
end
end
Скриншот результата:

На этом создание нашей системы управления вводом окончено. Приветствуются любые комментарии и пожелания :)
Исходный код статьи: