add mouse support

This commit is contained in:
a dinosaur 2024-08-24 13:52:32 +10:00
parent e087ed682f
commit 7a417e0701
6 changed files with 111 additions and 11 deletions

View File

@ -71,6 +71,7 @@ public class Application {
private func beginHandleEvents() {
Keyboard.instance.newFrame()
GameController.instance.newFrame()
Mouse.instance.newFrame(window!)
}
private func handleEvent(_ event: SDL_Event) -> ApplicationExecutionState {
@ -108,6 +109,17 @@ public class Application {
btn: SDL_GamepadButton(Int32(event.gbutton.button)), state: event.gbutton.state)
return .running
case SDL_EVENT_MOUSE_BUTTON_DOWN, SDL_EVENT_MOUSE_BUTTON_UP:
Mouse.instance.buttonEvent(
btn: UInt32(event.button.button),
state: event.button.state)
return .running
case SDL_EVENT_MOUSE_MOTION:
Mouse.instance.motionEvent(
absolute: SIMD2(event.motion.x, event.motion.y),
relative: SIMD2(event.motion.xrel, event.motion.yrel))
return .running
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
let backBufferSize = Size(Int(event.window.data1), Int(event.window.data2))
self.renderer!.resize(size: backBufferSize)

View File

@ -26,12 +26,15 @@ add_executable(Voxelotl MACOSX_BUNDLE
# Resource classes
NSImageLoader.swift
# Input wrappers
Input/Keyboard.swift
Input/GameController.swift
Input/Mouse.swift
# Core utility classes
Color.swift
Camera.swift
Renderer.swift
Keyboard.swift
GameController.swift
FPSCalculator.swift
GameDelegate.swift
Application.swift
@ -81,3 +84,4 @@ source_group("Resources" FILES Assets.xcassets test.png)
source_group("Source Files" REGULAR_EXPRESSION "\\.(swift|metal)$")
source_group("Source Files\\Random" REGULAR_EXPRESSION "Random/")
source_group("Source Files\\Math" REGULAR_EXPRESSION "Math/")
source_group("Source Files\\Input" REGULAR_EXPRESSION "Input/")

View File

@ -36,7 +36,7 @@ public class Keyboard {
private static let _REPEAT = UInt8(0b100)
private static let _PRESS: UInt8 = _DOWN | _IMPULSE
private static let _RELEASE: UInt8 = _UP | _IMPULSE
private var _state = [UInt8](repeating: _UP, count: Int(SDL_NUM_SCANCODES.rawValue))
internal func keyDownEvent(scan: SDL_Scancode, repeat rep: Bool) {

View File

@ -0,0 +1,81 @@
import SDL3
public class Mouse {
public struct Buttons: OptionSet {
public let rawValue: UInt32
public init(rawValue: UInt32) { self.rawValue = rawValue }
static let left = Self(rawValue: UInt32(SDL_BUTTON_LEFT).buttonMask)
static let middle = Self(rawValue: UInt32(SDL_BUTTON_MIDDLE).buttonMask)
static let right = Self(rawValue: UInt32(SDL_BUTTON_RIGHT).buttonMask)
static let button4 = Self(rawValue: UInt32(SDL_BUTTON_X1).buttonMask)
static let button5 = Self(rawValue: UInt32(SDL_BUTTON_X2).buttonMask)
}
public static var capture: Bool {
get { self._instance.getCapture() }
set { self._instance.setCapture(newValue) }
}
public static var position: SIMD2<Float> { self._instance.getAbsolute() }
public static var relative: SIMD2<Float> { self._instance.getDelta() }
public static func down(_ btn: Buttons) -> Bool {
btn.isSubset(of: self._instance._btns)
}
public static func pressed(_ btn: Buttons) -> Bool {
btn.isSubset(of: self._instance._btns.intersection(self._instance._btnImpulse))
}
public static func released(_ btn: Buttons) -> Bool {
btn.isSubset(of: self._instance._btnImpulse.subtracting(self._instance._btns))
}
//MARK: - Private
private static let _instance = Mouse()
public static var instance: Mouse { self._instance }
private var _window: OpaquePointer!
private var _captured: Bool = false
private var _abs: SIMD2<Float> = .zero, _delta: SIMD2<Float> = .zero
private var _btns: Buttons = [], _btnImpulse: Buttons = []
private func getCapture() -> Bool { self._captured }
private func setCapture(_ toggle: Bool) {
let sdlBool = toggle ? SDL_TRUE : SDL_FALSE
if SDL_SetRelativeMouseMode(sdlBool) >= 0 && SDL_SetWindowMouseGrab(self._window, sdlBool) >= 0 {
self._captured = toggle
}
}
private func getAbsolute() -> SIMD2<Float> { self._abs }
private func getDelta() -> SIMD2<Float> { self._delta }
internal func buttonEvent(btn: UInt32, state: UInt8) {
if state == SDL_PRESSED {
self._btns.formUnion(.init(rawValue: btn.buttonMask))
} else {
self._btns.subtract(.init(rawValue: btn.buttonMask))
}
self._btnImpulse.formUnion(.init(rawValue: btn.buttonMask))
}
internal func motionEvent(absolute: SIMD2<Float>, relative: SIMD2<Float>) {
self._abs = absolute
self._delta += relative
}
internal func newFrame(_ window: OpaquePointer) {
self._window = window
let grabbedFlag = SDL_WindowFlags(SDL_WINDOW_MOUSE_GRABBED)
self._captured = (SDL_GetWindowFlags(window) & grabbedFlag) == grabbedFlag
self._delta = .zero
self._btnImpulse = []
}
}
fileprivate extension UInt32 {
var buttonMask: UInt32 { 1 &<< (self &- 1) }
}

View File

@ -50,7 +50,7 @@ struct Player {
// Read controller input (if one is plugged in)
if let pad = GameController.current?.state {
turning += pad.rightStick.radialDeadzone(min: 0.1, max: 1)
movement += pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
flying += (pad.down(.rightBumper) ? 1 : 0) - (pad.down(.leftBumper) ? 1 : 0)
if pad.pressed(.east) {
jumpInput = .press
@ -72,19 +72,22 @@ struct Player {
if Keyboard.down(.s) { movement.y += 1 }
if Keyboard.down(.a) { movement.x -= 1 }
if Keyboard.down(.d) { movement.x += 1 }
if Keyboard.down(.up) { turning.y -= 1 }
if Keyboard.down(.down) { turning.y += 1 }
if Keyboard.down(.left) { turning.x -= 1 }
if Keyboard.down(.right) { turning.x += 1 }
if Keyboard.down(.tab) { flying += 1 }
if Keyboard.pressed(.q) { place = true }
if Keyboard.pressed(.e) { destroy = true }
if Keyboard.down(.q) { flying += 1 }
if Keyboard.down(.e) { flying -= 1 }
if Keyboard.pressed(.tab) { Mouse.capture = !Mouse.capture }
if Keyboard.pressed(.space) {
jumpInput = .press
} else if jumpInput != .press && Keyboard.down(.space) {
jumpInput = .held
}
// Read mouse input
if Mouse.pressed(.left) { destroy = true }
if Mouse.pressed(.right) { place = true }
if Mouse.capture {
self._rotation += Mouse.relative / 2048 * Float.pi
}
// Turning input
self._rotation += turning * deltaTime * 3.0
if self._rotation.x < 0.0 {