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

Character template: /input_mapper.lua — code sample

Code

local InputMapper = Appkit.InputMapper
local Util = Appkit.Util

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

local mouse_pan_speed = 1
local pad_pan_speed = 1000

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

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

function InputMapper:if_jump()
    return self.input.jump
end

function InputMapper:if_crouch()
    return self.input.crouch
end

function InputMapper:if_run()
    return self.input.run
end

function InputMapper:if_aim()
    return self.input.aim
end

function InputMapper:if_fire()
    return self.input.fire
end

function InputMapper:if_center_camera()
    return self.input.center_camera
end

function InputMapper:if_toggle_camera()
    return self.input.toggle_camera
end

function InputMapper:if_switch_perspective()
    return self.input.switch_perspective
end

function InputMapper:if_exit_level()
    return self.input.exit_level
end

local function update_button_input(self, dt)
    local move = Vector3(0, 0, 0)
    local pan = Vector3(0, 0, 0)
    local input = self.input

    if Util.is_pc() then
        pan = Mouse.axis(Mouse.axis_id("mouse")) * mouse_pan_speed
        move = Vector3(
            Keyboard.button(Keyboard.button_id("d")) - Keyboard.button(Keyboard.button_id("a")),
            Keyboard.button(Keyboard.button_id("w")) - Keyboard.button(Keyboard.button_id("s")),
            0
        )

        input.jump = Keyboard.pressed(Keyboard.button_id("space"))
        input.crouch = Keyboard.button(Keyboard.button_id("left ctrl")) > 0
        input.run = Keyboard.button(Keyboard.button_id("left shift")) > 0
        input.aim = Mouse.button(Mouse.button_id("right")) > 0
        input.fire = Mouse.pressed(Mouse.button_id("left"))
        input.center_camera = Keyboard.pressed(Keyboard.button_id("v"))
        input.toggle_camera = Keyboard.pressed(Keyboard.button_id("f3"))
        input.switch_perspective = Keyboard.pressed(Keyboard.button_id("f2"))
        input.exit_level = Keyboard.pressed(Keyboard.button_id("f5")) or Keyboard.pressed(Keyboard.button_id("esc"))

        if Pad1 and Pad1.active() then
            if Vector3.equal(pan, Vector3.zero()) then
                pan = Pad1.axis(Pad1.axis_id("right"))
                -- apply an acceleration curve so that we move faster as player presses stick further
                -- apply dt since gamepad is frame-dependent
                Vector3.set_x(pan, pan.x^3 * dt * pad_pan_speed)
                Vector3.set_y(pan, -pan.y^3 * dt * pad_pan_speed)
            end
            if Vector3.equal(move, Vector3.zero()) then
                move = Pad1.axis(Pad1.axis_id("left"))
            end
            input.jump = input.jump or Pad1.pressed(Pad1.button_id(Util.plat(nil, "a", nil, "cross")))
            input.crouch = input.crouch or Pad1.button(Pad1.button_id(Util.plat(nil, "b", nil, "circle"))) > 0
            input.run = input.run or Pad1.button(Pad1.button_id(Util.plat(nil, "left_thumb", nil, "l3" ))) > 0
            input.aim = input.aim or Pad1.button(Pad1.button_id(Util.plat(nil, "left_trigger", nil, "l1"))) > 0
            input.fire = input.fire or Pad1.pressed(Pad1.button_id(Util.plat(nil, "right_trigger", nil, "r1")))
            input.center_camera = input.center_camera or Pad1.pressed(Pad1.button_id(Util.plat(nil, "right_thumb", nil, "r3")))
            input.toggle_camera = input.toggle_camera or Pad1.pressed(Pad1.button_id(Util.plat(nil, "d_left", nil, "left")))
            input.switch_perspective = input.switch_perspective or Pad1.pressed(Pad1.button_id(Util.plat(nil, "d_down", nil, "down")))
        end
    -- Todo: multiple controller support
    elseif Application.platform() == "xb1" or Application.platform() == "ps4" then
        pan = Pad1.axis(Pad1.axis_id("right"))
        -- apply an acceleration curve so that we move faster as player presses stick further
        -- apply dt since gamepad is frame-dependent
        Vector3.set_x(pan, pan.x^3 * dt * pad_pan_speed)
        Vector3.set_y(pan, -pan.y^3 * dt * pad_pan_speed)
        move = Pad1.axis(Pad1.axis_id("left"))

        input.jump = Pad1.pressed(Pad1.button_id(Util.plat(nil, "a", nil, "cross")))
        input.crouch = Pad1.button(Pad1.button_id(Util.plat(nil, "b", nil, "circle"))) > 0
        input.run = Pad1.button(Pad1.button_id(Util.plat(nil, "left_thumb", nil, "l3" ))) > 0
        input.aim = Pad1.button(Pad1.button_id(Util.plat(nil, "left_trigger", nil, "l1"))) > 0
        input.fire = Pad1.pressed(Pad1.button_id(Util.plat(nil, "right_trigger", nil, "r1")))
        input.center_camera = Pad1.pressed(Pad1.button_id(Util.plat(nil, "right_thumb", nil, "r3")))
        input.toggle_camera = Pad1.pressed(Pad1.button_id(Util.plat(nil, "d_left", nil, "left")))
        input.switch_perspective = Pad1.pressed(Pad1.button_id(Util.plat(nil, "d_down", nil, "down")))      
    end

    input.move:store(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 scaleform and Util.use_touch() then 
        return
    elseif Util.use_touch() then
        update_touch_input(self, dt)
    else
        update_button_input(self, dt)
    end
end

return InputMapper