The Skung Cave commit
This commit is contained in:
157
Sources/JolkEngine/Input/GamePad.swift
Normal file
157
Sources/JolkEngine/Input/GamePad.swift
Normal file
@ -0,0 +1,157 @@
|
||||
import Foundation
|
||||
import SDL2
|
||||
|
||||
|
||||
public class GamePad
|
||||
{
|
||||
public enum Axes
|
||||
{
|
||||
case leftStickX, leftStickY
|
||||
case rightStickX, rightStickY
|
||||
case leftTrigger, rightTrigger
|
||||
}
|
||||
|
||||
public enum Buttons
|
||||
{
|
||||
case east, south, north, west
|
||||
case select, start, guide, mic, touchPad
|
||||
case leftStick, rightStick
|
||||
case leftBumper, rightBumper
|
||||
case dpadLeft, dpadRight, dpadUp, dpadDown
|
||||
case paddle1, paddle2, paddle3, paddle4
|
||||
}
|
||||
|
||||
public struct State
|
||||
{
|
||||
public func down(_ btn: Buttons) -> Bool { btnState(btn) & GamePad._DOWN == GamePad._DOWN }
|
||||
public func pressed(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._PRESS }
|
||||
public func released(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._RELEASE }
|
||||
|
||||
public func axis(_ axis: Axes) -> Float
|
||||
{
|
||||
let axisRescale = 1.0 / Float(Int16.max)
|
||||
return Float(rawAxis(axis)) * axisRescale
|
||||
}
|
||||
|
||||
@inline(__always) func rawAxis(_ axis: Axes) -> Int16
|
||||
{
|
||||
_axes[Int(axis.sdlEnum.rawValue)]
|
||||
}
|
||||
|
||||
@inline(__always) private func btnState(_ btn: Buttons) -> UInt8
|
||||
{
|
||||
_btns[Int(btn.sdlEnum.rawValue)]
|
||||
}
|
||||
|
||||
private let _btns: [UInt8], _axes: [Int16]
|
||||
|
||||
internal init(btns: [UInt8], axes: [Int16])
|
||||
{
|
||||
self._btns = btns
|
||||
self._axes = axes
|
||||
}
|
||||
}
|
||||
|
||||
public var state: State
|
||||
{
|
||||
.init(btns: _btns, axes: _axes)
|
||||
}
|
||||
|
||||
@inline(__always) public static var current: GamePad?
|
||||
{
|
||||
let input = Input.instance
|
||||
return if let id = input.firstPadID { input.getPad(id: id) } else { nil }
|
||||
}
|
||||
|
||||
private static let _UP = UInt8(0b00), _DOWN = UInt8(0b10), _IMPULSE = UInt8(0b01)
|
||||
private static let _PRESS = _DOWN | _IMPULSE
|
||||
private static let _RELEASE = _UP | _IMPULSE
|
||||
|
||||
private var _btns = [UInt8](repeating: 0, count: Int(SDL_CONTROLLER_BUTTON_MAX.rawValue))
|
||||
private var _axes = [Int16](repeating: 0, count: Int(SDL_CONTROLLER_AXIS_MAX.rawValue))
|
||||
private var _sdlPad: OpaquePointer
|
||||
|
||||
internal init(pad: OpaquePointer)
|
||||
{
|
||||
self._sdlPad = pad
|
||||
}
|
||||
|
||||
internal func newTick()
|
||||
{
|
||||
_btns = _btns.map({ $0 & ~Self._IMPULSE })
|
||||
}
|
||||
|
||||
internal func close()
|
||||
{
|
||||
SDL_GameControllerClose(_sdlPad)
|
||||
}
|
||||
|
||||
internal func buttonPressEvent(_ btn: UInt8)
|
||||
{
|
||||
_btns[Int(btn)] = Self._PRESS
|
||||
}
|
||||
|
||||
internal func buttonReleaseEvent(_ btn: UInt8)
|
||||
{
|
||||
_btns[Int(btn)] = Self._RELEASE
|
||||
}
|
||||
|
||||
internal func axisEvent(_ axis: UInt8, _ value: Int16)
|
||||
{
|
||||
_axes[Int(axis)] = value
|
||||
}
|
||||
}
|
||||
|
||||
public extension GamePad.State
|
||||
{
|
||||
var leftStick: Vec2f { Vec2f(axis(.leftStickX), axis(.leftStickY)) }
|
||||
var rightStick: Vec2f { Vec2f(axis(.rightStickX), axis(.rightStickY)) }
|
||||
}
|
||||
|
||||
|
||||
internal extension GamePad.Axes
|
||||
{
|
||||
var sdlEnum: SDL_GameControllerAxis
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .leftStickX: SDL_CONTROLLER_AXIS_LEFTX
|
||||
case .leftStickY: SDL_CONTROLLER_AXIS_LEFTY
|
||||
case .rightStickX: SDL_CONTROLLER_AXIS_RIGHTX
|
||||
case .rightStickY: SDL_CONTROLLER_AXIS_RIGHTY
|
||||
case .leftTrigger: SDL_CONTROLLER_AXIS_TRIGGERLEFT
|
||||
case .rightTrigger: SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension GamePad.Buttons
|
||||
{
|
||||
var sdlEnum: SDL_GameControllerButton
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .east: SDL_CONTROLLER_BUTTON_B
|
||||
case .south: SDL_CONTROLLER_BUTTON_A
|
||||
case .west: SDL_CONTROLLER_BUTTON_Y
|
||||
case .north: SDL_CONTROLLER_BUTTON_X
|
||||
case .start: SDL_CONTROLLER_BUTTON_START
|
||||
case .select: SDL_CONTROLLER_BUTTON_BACK
|
||||
case .guide: SDL_CONTROLLER_BUTTON_GUIDE
|
||||
case .mic: SDL_CONTROLLER_BUTTON_MISC1
|
||||
case .touchPad: SDL_CONTROLLER_BUTTON_TOUCHPAD
|
||||
case .leftStick: SDL_CONTROLLER_BUTTON_LEFTSTICK
|
||||
case .rightStick: SDL_CONTROLLER_BUTTON_RIGHTSTICK
|
||||
case .leftBumper: SDL_CONTROLLER_BUTTON_LEFTSHOULDER
|
||||
case .rightBumper: SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
|
||||
case .dpadUp: SDL_CONTROLLER_BUTTON_DPAD_UP
|
||||
case .dpadDown: SDL_CONTROLLER_BUTTON_DPAD_DOWN
|
||||
case .dpadLeft: SDL_CONTROLLER_BUTTON_DPAD_LEFT
|
||||
case .dpadRight: SDL_CONTROLLER_BUTTON_DPAD_RIGHT
|
||||
case .paddle1: SDL_CONTROLLER_BUTTON_PADDLE1
|
||||
case .paddle2: SDL_CONTROLLER_BUTTON_PADDLE2
|
||||
case .paddle3: SDL_CONTROLLER_BUTTON_PADDLE3
|
||||
case .paddle4: SDL_CONTROLLER_BUTTON_PADDLE4
|
||||
}
|
||||
}
|
||||
}
|
62
Sources/JolkEngine/Input/Input.swift
Normal file
62
Sources/JolkEngine/Input/Input.swift
Normal file
@ -0,0 +1,62 @@
|
||||
import SDL2
|
||||
|
||||
|
||||
public class Input
|
||||
{
|
||||
private static let _instance = Input()
|
||||
public static var instance: Input { _instance }
|
||||
|
||||
public var keyboard: Keyboard { _keyboard }
|
||||
public func getPad(id: Int) -> GamePad? { _pads[Int32(id)] }
|
||||
public var firstPadID: Int? { _firstJoyID >= 0 ? Int(_firstJoyID) : nil }
|
||||
|
||||
private var _keyboard = Keyboard()
|
||||
private var _pads = Dictionary<Int32, GamePad>()
|
||||
private var _firstJoyID: Int32 = -1
|
||||
|
||||
private init() {}
|
||||
|
||||
internal func pressEvent(scan: SDL_Scancode, repeat r: Bool) { keyboard.pressEvent(scan: scan, repeat: r) }
|
||||
internal func releaseEvent(scan: SDL_Scancode) { keyboard.releaseEvent(scan: scan) }
|
||||
|
||||
internal func padConnectedEvent(index: Int32)
|
||||
{
|
||||
guard let sdlPad = SDL_GameControllerOpen(index)
|
||||
else { return }
|
||||
let id = SDL_JoystickGetDeviceInstanceID(index)
|
||||
_pads[id] = GamePad(pad: sdlPad)
|
||||
print("Using gamepad #\(id), \"\(String(cString: SDL_GameControllerName(sdlPad)))\"")
|
||||
if _firstJoyID < 0 { _firstJoyID = id }
|
||||
}
|
||||
|
||||
internal func padRemovedEvent(index: Int32)
|
||||
{
|
||||
let id = SDL_JoystickGetDeviceInstanceID(index)
|
||||
_pads.removeValue(forKey: id)
|
||||
if id == _firstJoyID
|
||||
{
|
||||
_firstJoyID = _pads.keys.first ?? -1
|
||||
}
|
||||
}
|
||||
|
||||
internal func padButtonPressEvent(id: Int32, btn: UInt8)
|
||||
{
|
||||
_pads[id]?.buttonPressEvent(btn)
|
||||
}
|
||||
|
||||
internal func padButtonReleaseEvent(id: Int32, btn: UInt8)
|
||||
{
|
||||
_pads[id]?.buttonReleaseEvent(btn)
|
||||
}
|
||||
|
||||
internal func padAxisEvent(id: Int32, axis: UInt8, value: Int16)
|
||||
{
|
||||
_pads[id]?.axisEvent(axis, value)
|
||||
}
|
||||
|
||||
internal func newTick()
|
||||
{
|
||||
_keyboard.newTick()
|
||||
for pad in _pads.values { pad.newTick() }
|
||||
}
|
||||
}
|
137
Sources/JolkEngine/Input/Keyboard.swift
Normal file
137
Sources/JolkEngine/Input/Keyboard.swift
Normal file
@ -0,0 +1,137 @@
|
||||
import SDL2
|
||||
|
||||
|
||||
public class Keyboard
|
||||
{
|
||||
public enum Keys
|
||||
{
|
||||
case a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
|
||||
case right, left, down, up
|
||||
}
|
||||
|
||||
public func keyDown(_ key: Keys) -> Bool
|
||||
{
|
||||
_keys[Int(key.sdlScancode.rawValue)] & Self._DOWN == Self._DOWN
|
||||
}
|
||||
|
||||
public func keyPressed(_ key: Keys, repeat rep: Bool = false) -> Bool
|
||||
{
|
||||
var state = _keys[Int(key.sdlScancode.rawValue)]
|
||||
if rep { state &= ~Self._REPEAT }
|
||||
return state == Self._PRESS
|
||||
}
|
||||
|
||||
public func keyReleased(_ key: Keys) -> Bool
|
||||
{
|
||||
_keys[Int(key.sdlKeycode.rawValue)] == Self._RELEASE
|
||||
}
|
||||
|
||||
private static let _UP = UInt8(0b000), _DOWN = UInt8(0b010), _IMPULSE = UInt8(0b001)
|
||||
private static let _REPEAT = UInt8(0b100)
|
||||
private static let _PRESS: UInt8 = _DOWN | _IMPULSE
|
||||
private static let _RELEASE: UInt8 = _UP | _IMPULSE
|
||||
|
||||
private var _keys = [UInt8](repeating: _UP, count: Int(SDL_NUM_SCANCODES.rawValue))
|
||||
|
||||
internal func newTick()
|
||||
{
|
||||
_keys = _keys.map({ $0 & ~(Self._IMPULSE | Self._REPEAT) })
|
||||
}
|
||||
|
||||
internal func pressEvent(scan: SDL_Scancode, repeat rep: Bool)
|
||||
{
|
||||
var newState = Self._PRESS
|
||||
if rep { newState |= Self._REPEAT }
|
||||
_keys[Int(scan.rawValue)] = newState
|
||||
}
|
||||
|
||||
internal func releaseEvent(scan: SDL_Scancode)
|
||||
{
|
||||
_keys[Int(scan.rawValue)] = Self._RELEASE
|
||||
}
|
||||
}
|
||||
|
||||
extension Keyboard.Keys
|
||||
{
|
||||
internal var sdlKeycode: SDL_KeyCode
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .a: SDLK_a
|
||||
case .b: SDLK_b
|
||||
case .c: SDLK_c
|
||||
case .d: SDLK_d
|
||||
case .e: SDLK_e
|
||||
case .f: SDLK_f
|
||||
case .g: SDLK_g
|
||||
case .h: SDLK_h
|
||||
case .i: SDLK_i
|
||||
case .j: SDLK_j
|
||||
case .k: SDLK_k
|
||||
case .l: SDLK_l
|
||||
case .m: SDLK_m
|
||||
case .n: SDLK_n
|
||||
case .o: SDLK_o
|
||||
case .p: SDLK_p
|
||||
case .q: SDLK_q
|
||||
case .r: SDLK_r
|
||||
case .s: SDLK_s
|
||||
case .t: SDLK_t
|
||||
case .u: SDLK_u
|
||||
case .v: SDLK_v
|
||||
case .w: SDLK_w
|
||||
case .x: SDLK_x
|
||||
case .y: SDLK_y
|
||||
case .z: SDLK_z
|
||||
case .left: SDLK_LEFT
|
||||
case .right: SDLK_RIGHT
|
||||
case .up: SDLK_UP
|
||||
case .down: SDLK_DOWN
|
||||
}
|
||||
}
|
||||
|
||||
internal var sdlScancode: SDL_Scancode
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .a: SDL_SCANCODE_A
|
||||
case .b: SDL_SCANCODE_B
|
||||
case .c: SDL_SCANCODE_C
|
||||
case .d: SDL_SCANCODE_D
|
||||
case .e: SDL_SCANCODE_E
|
||||
case .f: SDL_SCANCODE_F
|
||||
case .g: SDL_SCANCODE_G
|
||||
case .h: SDL_SCANCODE_H
|
||||
case .i: SDL_SCANCODE_I
|
||||
case .j: SDL_SCANCODE_J
|
||||
case .k: SDL_SCANCODE_K
|
||||
case .l: SDL_SCANCODE_L
|
||||
case .m: SDL_SCANCODE_M
|
||||
case .n: SDL_SCANCODE_N
|
||||
case .o: SDL_SCANCODE_O
|
||||
case .p: SDL_SCANCODE_P
|
||||
case .q: SDL_SCANCODE_Q
|
||||
case .r: SDL_SCANCODE_R
|
||||
case .s: SDL_SCANCODE_S
|
||||
case .t: SDL_SCANCODE_T
|
||||
case .u: SDL_SCANCODE_U
|
||||
case .v: SDL_SCANCODE_V
|
||||
case .w: SDL_SCANCODE_W
|
||||
case .x: SDL_SCANCODE_X
|
||||
case .y: SDL_SCANCODE_Y
|
||||
case .z: SDL_SCANCODE_Z
|
||||
case .left: SDL_SCANCODE_LEFT
|
||||
case .right: SDL_SCANCODE_RIGHT
|
||||
case .up: SDL_SCANCODE_UP
|
||||
case .down: SDL_SCANCODE_DOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SDL_KeyCode
|
||||
{
|
||||
internal init(scancode: SDL_Scancode)
|
||||
{
|
||||
self.init(scancode.rawValue | UInt32(SDLK_SCANCODE_MASK))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user