В предыдущей статье мы создали простую заготовку для нашего будущего приложения на движке 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
Скриншот результата:
На этом создание нашей системы управления вводом окончено. Приветствуются любые комментарии и пожелания :)
Исходный код статьи: