Appkit: /input_mapper.lua — code sample - Stingray Lua API Reference

Appkit: /input_mapper.lua — code sample

Code

--[[

A basic Input utility with controls for move and pan. Used to support simple
input in the templates.

]]--

require 'core/appkit/lua/util'
require 'core/appkit/lua/class'

Appkit.InputMapper = Appkit.class(Appkit.InputMapper)

local InputMapper = Appkit.InputMapper

local Application = stingray.Application
local Keyboard = stingray.Keyboard
local Pad1 = stingray.Pad1
local Vector3 = stingray.Vector3
local Vector3Box = stingray.Vector3Box

InputMapper.pc_azerty_move_keys = {
    forward = 'z',
    backward = 's',
    left = 'q',
    right = 'd'
}

InputMapper.pc_qwerty_move_keys = {
    forward = 'w',
    backward = 's',
    left = 'a',
    right = 'd'
}

function InputMapper:set_pc_move_keys(pc_move_keys)
    self.pc_move_keys = pc_move_keys
end

function InputMapper:init()
    self:set_pc_move_keys(InputMapper.pc_qwerty_move_keys)
    self.input = {
        pan = Vector3Box(Vector3(0, 0, 0)),
        move = Vector3Box(Vector3(0, 0, 0))
    }

    self.touch_state = {
        -- Contact id for pan controller
        pan_id = nil,
        -- Contact id for move controller
        move_id = nil,
        pan_ref = Vector3Box(0, 0, 0)
    }
end

function InputMapper:get_motion_input()
    local input = self.input
    return {move = input.move:unbox(), pan = input.pan:unbox()}
end

local function update_button_input(self)
    local move = Vector3(0, 0, 0)
    local pan = Vector3(0, 0, 0)
    local input = self.input
    if Appkit.Util.is_pc() then
        pan = stingray.Mouse.axis(stingray.Mouse.axis_id("mouse"))
        move = Vector3(
            Keyboard.button(Keyboard.button_id(self.pc_move_keys.right)) -
            Keyboard.button(Keyboard.button_id(self.pc_move_keys.left)),
            Keyboard.button(Keyboard.button_id(self.pc_move_keys.forward)) -
            Keyboard.button(Keyboard.button_id(self.pc_move_keys.backward)),
            0
        )

        input.jump = Keyboard.pressed(Keyboard.button_id("space"))
        input.crouch = Keyboard.pressed(Keyboard.button_id("left ctrl"))
        input.run = Keyboard.button(Keyboard.button_id("left shift")) > 0

        if Pad1 and Pad1.active() then
            if pan.x == 0 and pan.y ==0 and pan.z == 0 then
                pan = Pad1.axis(Pad1.axis_id("right")) * 10
                Vector3.set_y(pan, -pan.y)
            end
            if move.x == 0 and move.y ==0 and move.z == 0 then
                move = Pad1.axis(Pad1.axis_id("left"))
            end
            input.jump = (input.jump == true) or Pad1.pressed(Pad1.button_id(Appkit.Util.plat(nil, "a", nil, "cross")))
            input.crouch = (input.crouch == true) or Pad1.pressed(Pad1.button_id(Appkit.Util.plat(nil, "b", nil, "circle")))
            input.run = (input.run == true) or Pad1.button(Pad1.button_id(Appkit.Util.plat(nil, "x", nil, "square" ))) > 0
        end
    --Todo: multiple controller support
    elseif Application.platform() == "xb1" or Application.platform() == "ps4" then
        pan = Pad1.axis(Pad1.axis_id("right")) * 10
        Vector3.set_y(pan, -pan.y)
        move = Pad1.axis(Pad1.axis_id("left"))

        input.jump = Pad1.pressed(Pad1.button_id(Appkit.Util.plat(nil, "a", nil, "cross")))
        input.crouch = Pad1.pressed(Pad1.button_id(Appkit.Util.plat(nil, "b", nil, "circle")))
        input.run = Pad1.button(Pad1.button_id(Appkit.Util.plat(nil, "x", nil, "square" ))) > 0
    end

    input.move:store(Vector3.normalize(move))
    input.pan:store(pan)
end

local function update_touch_input(self)
    local move = Vector3(0, 0, 0)
    local pan = Vector3(0, 0, 0)
    local input = self.input
    local state = self.touch_state
    local util = Appkit.Util
    local touch = util.touch_interface()
    local has_pan_contact = state.pan_id and touch.has_contact(state.pan_id)
    local has_move_contact = state.move_id and touch.has_contact(state.move_id)

    -- Remove lifted sticks and handle tapping
    if state.pan_id and (not has_pan_contact or touch.is_touch_up(state.pan_id)) then
        local dt = Application.time_since_launch() - state.pan_t
        if has_pan_contact then
            local delta = util.location(touch, state.pan_id) - state.pan_ref:unbox()
            if dt < 0.5 and Vector3.length(delta) < 5 then
                input.jump = true
            end
        end
        state.pan_id = nil
    end
    if state.move_id and (not has_move_contact or touch.is_touch_up(state.move_id)) then
        local dt = Application.time_since_launch() - state.move_t
        if has_move_contact then
            local delta = util.location(touch, state.move_id) - state.move_ref:unbox()
            if dt < 0.5 and Vector3.length(delta) < 5 then
                input.crouch = true
            end
        end
        state.move_id = nil
    end

    -- Handle new touches
    local contacts = {touch.contacts()}
    for _, id in ipairs(contacts) do
        if touch.is_touch_down(id) then
            local pos = util.location(touch, id)
            local w, h = stingray.Gui.resolution()
            if pos.x > w / 2 and pos.y < h / 2 and not state.move_id then
                state.move_id = id
                state.move_ref = Vector3Box(pos)
                state.move_t = Application.time_since_launch()
            elseif pos.x < w / 2 and pos.y < h / 2 and not state.pan_id then
                state.pan_id = id
                state.pan_ref:store(pos)
                state.pan_t = Application.time_since_launch()
            end
        end
    end

    -- Track pan and move
    if state.move_id then
        local delta = util.location(touch, state.move_id) - state.move_ref:unbox()
        delta = delta / 100
        move = delta
    end
    if state.pan_id then
        local delta = util.location(touch, state.pan_id) - state.pan_ref:unbox()
        delta = delta / 50
        delta.y = -delta.y / 4
        pan = delta
    end

    input.move:store(Vector3.normalize(move))
    input.pan:store(pan)
end

-- Updates the cached input state
function InputMapper:update(dt)
    if Appkit.Util.use_touch() then
        update_touch_input(self)
    else
        update_button_input(self)
    end
end

return InputMapper