mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-03 18:49:38 +00:00 
			
		
		
		
	add mouse support
This commit is contained in:
		@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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/")
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
							
								
								
									
										81
									
								
								Sources/Voxelotl/Input/Mouse.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								Sources/Voxelotl/Input/Mouse.swift
									
									
									
									
									
										Normal 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) }
 | 
			
		||||
}
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user