mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-04 10:59:39 +00:00 
			
		
		
		
	allow camera movement with the keyboard
This commit is contained in:
		@ -69,6 +69,7 @@ public class Application {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private func beginHandleEvents() {
 | 
					  private func beginHandleEvents() {
 | 
				
			||||||
 | 
					    Keyboard.instance.newFrame()
 | 
				
			||||||
    GameController.instance.newFrame()
 | 
					    GameController.instance.newFrame()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -82,10 +83,14 @@ public class Application {
 | 
				
			|||||||
      case SDLK_ESCAPE:
 | 
					      case SDLK_ESCAPE:
 | 
				
			||||||
        return .exitSuccess
 | 
					        return .exitSuccess
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        break
 | 
					        Keyboard.instance.keyDownEvent(scan: event.key.scancode, repeat: event.key.repeat != 0)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return .running
 | 
					      return .running
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case SDL_EVENT_KEY_UP:
 | 
				
			||||||
 | 
					      Keyboard.instance.keyUpEvent(scan: event.key.scancode)
 | 
				
			||||||
 | 
					      return .running
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case SDL_EVENT_GAMEPAD_ADDED:
 | 
					    case SDL_EVENT_GAMEPAD_ADDED:
 | 
				
			||||||
      if SDL_IsGamepad(event.gdevice.which) != SDL_FALSE {
 | 
					      if SDL_IsGamepad(event.gdevice.which) != SDL_FALSE {
 | 
				
			||||||
        GameController.instance.connectedEvent(id: event.gdevice.which)
 | 
					        GameController.instance.connectedEvent(id: event.gdevice.which)
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
 | 
				
			|||||||
  Color.swift
 | 
					  Color.swift
 | 
				
			||||||
  Camera.swift
 | 
					  Camera.swift
 | 
				
			||||||
  Renderer.swift
 | 
					  Renderer.swift
 | 
				
			||||||
 | 
					  Keyboard.swift
 | 
				
			||||||
  GameController.swift
 | 
					  GameController.swift
 | 
				
			||||||
  FPSCalculator.swift
 | 
					  FPSCalculator.swift
 | 
				
			||||||
  GameDelegate.swift
 | 
					  GameDelegate.swift
 | 
				
			||||||
 | 
				
			|||||||
@ -27,11 +27,16 @@ class Game: GameDelegate {
 | 
				
			|||||||
  var chunk = Chunk(position: .zero)
 | 
					  var chunk = Chunk(position: .zero)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  init() {
 | 
					  init() {
 | 
				
			||||||
    self.player.position = SIMD3(0.5, Float(Chunk.chunkSize) + 0.5, 0.5)
 | 
					    self.resetPlayer()
 | 
				
			||||||
    self.player.rotation = .init(.pi, 0)
 | 
					 | 
				
			||||||
    self.generateWorld()
 | 
					    self.generateWorld()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private func resetPlayer() {
 | 
				
			||||||
 | 
					    self.player.position = .init(repeating: 0.5) + .init(0, Float(Chunk.chunkSize), 0)
 | 
				
			||||||
 | 
					    self.player.velocity = .zero
 | 
				
			||||||
 | 
					    self.player.rotation = .init(.pi, 0)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private func generateWorld() {
 | 
					  private func generateWorld() {
 | 
				
			||||||
#if true
 | 
					#if true
 | 
				
			||||||
    let newSeed = UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32
 | 
					    let newSeed = UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32
 | 
				
			||||||
@ -70,9 +75,7 @@ class Game: GameDelegate {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Player reset
 | 
					      // Player reset
 | 
				
			||||||
      if pad.pressed(.back) {
 | 
					      if pad.pressed(.back) {
 | 
				
			||||||
        self.player.position = .init(repeating: 0.5) + .init(0, Float(Chunk.chunkSize), 0)
 | 
					        self.resetPlayer()
 | 
				
			||||||
        self.player.velocity = .zero
 | 
					 | 
				
			||||||
        self.player.rotation = .init(.pi, 0)
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Regenerate
 | 
					      // Regenerate
 | 
				
			||||||
@ -81,6 +84,13 @@ class Game: GameDelegate {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if Keyboard.pressed(.r) {
 | 
				
			||||||
 | 
					      self.resetPlayer()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if Keyboard.pressed(.g) {
 | 
				
			||||||
 | 
					      self.generateWorld()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self.player.update(deltaTime: deltaTime, chunk: chunk)
 | 
					    self.player.update(deltaTime: deltaTime, chunk: chunk)
 | 
				
			||||||
    self.camera.position = player.eyePosition
 | 
					    self.camera.position = player.eyePosition
 | 
				
			||||||
    self.camera.rotation = player.eyeRotation
 | 
					    self.camera.rotation = player.eyeRotation
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										137
									
								
								Sources/Voxelotl/Keyboard.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								Sources/Voxelotl/Keyboard.swift
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,137 @@
 | 
				
			|||||||
 | 
					import SDL3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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, up, down
 | 
				
			||||||
 | 
					    case space
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static func down(_ key: Keys) -> Bool {
 | 
				
			||||||
 | 
					     keyState(key) & Self._DOWN == Self._DOWN
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static func pressed(_ key: Keys, repeat rep: Bool = false) -> Bool {
 | 
				
			||||||
 | 
					    var state = keyState(key)
 | 
				
			||||||
 | 
					    if rep {
 | 
				
			||||||
 | 
					      state &= ~Self._REPEAT
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return state == Self._PRESS
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static func released(_ key: Keys) -> Bool {
 | 
				
			||||||
 | 
					    keyState(key) == Self._RELEASE
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //MARK: - Private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static let _instance = Keyboard()
 | 
				
			||||||
 | 
					  public static var instance: Keyboard { _instance }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @inline(__always) private static func keyState(_ key: Keys) -> UInt8 {
 | 
				
			||||||
 | 
					    self._instance._state[Int(key.sdlScancode.rawValue)]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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 _state = [UInt8](repeating: _UP, count: Int(SDL_NUM_SCANCODES.rawValue))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func keyDownEvent(scan: SDL_Scancode, repeat rep: Bool) {
 | 
				
			||||||
 | 
					    var newState = Self._PRESS
 | 
				
			||||||
 | 
					    if rep {
 | 
				
			||||||
 | 
					      newState |= Self._REPEAT
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    self._state[Int(scan.rawValue)] = newState
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func keyUpEvent(scan: SDL_Scancode) {
 | 
				
			||||||
 | 
					    self._state[Int(scan.rawValue)] = Self._RELEASE
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func newFrame() {
 | 
				
			||||||
 | 
					    self._state = self._state.map({ $0 & ~(Self._IMPULSE | Self._REPEAT) })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					internal extension Keyboard.Keys {
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 | 
					    case .space: SDLK_SPACE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 | 
					    case .space: SDL_SCANCODE_SPACE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fileprivate extension SDL_Keycode {
 | 
				
			||||||
 | 
					  init(scancode: SDL_Scancode) {
 | 
				
			||||||
 | 
					    self.init(scancode.rawValue | UInt32(SDLK_SCANCODE_MASK))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -14,6 +14,7 @@ struct Player {
 | 
				
			|||||||
  static let airAccelCoeff: Float = 3
 | 
					  static let airAccelCoeff: Float = 3
 | 
				
			||||||
  static let gravityCoeff: Float = 20
 | 
					  static let gravityCoeff: Float = 20
 | 
				
			||||||
  static let frictionCoeff: Float = 22
 | 
					  static let frictionCoeff: Float = 22
 | 
				
			||||||
 | 
					  static let flySpeedCoeff: Float = 36
 | 
				
			||||||
  static let jumpVelocity: Float = 7
 | 
					  static let jumpVelocity: Float = 7
 | 
				
			||||||
  static let maxVelocity: Float = 160
 | 
					  static let maxVelocity: Float = 160
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,11 +35,45 @@ struct Player {
 | 
				
			|||||||
    .init(angle: self._rotation.x, axis: .up)
 | 
					    .init(angle: self._rotation.x, axis: .up)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  enum JumpInput { case off, press, held }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mutating func update(deltaTime: Float, chunk: Chunk) {
 | 
					  mutating func update(deltaTime: Float, chunk: Chunk) {
 | 
				
			||||||
 | 
					    var turning: SIMD2<Float> = .zero
 | 
				
			||||||
 | 
					    var movement: SIMD2<Float> = .zero
 | 
				
			||||||
 | 
					    var flying: Float = .zero
 | 
				
			||||||
 | 
					    var jumpInput: JumpInput = .off
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Read controller input (if one is plugged in)
 | 
				
			||||||
    if let pad = GameController.current?.state {
 | 
					    if let pad = GameController.current?.state {
 | 
				
			||||||
 | 
					      turning += pad.rightStick.radialDeadzone(min: 0.1, max: 1)
 | 
				
			||||||
 | 
					      movement += pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
 | 
				
			||||||
 | 
					      flying += pad.rightTrigger.axisDeadzone(0.01, 1) - pad.leftTrigger.axisDeadzone(0.01, 1)
 | 
				
			||||||
 | 
					      if pad.pressed(.east) {
 | 
				
			||||||
 | 
					        jumpInput = .press
 | 
				
			||||||
 | 
					      } else if jumpInput != .press && pad.down(.east) {
 | 
				
			||||||
 | 
					        jumpInput = .held
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Read keyboard input
 | 
				
			||||||
 | 
					    if Keyboard.down(.w) { movement.y -= 1 }
 | 
				
			||||||
 | 
					    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(.q) { flying += 1 }
 | 
				
			||||||
 | 
					    if Keyboard.down(.e) { flying -= 1 }
 | 
				
			||||||
 | 
					    if Keyboard.pressed(.space) {
 | 
				
			||||||
 | 
					      jumpInput = .press
 | 
				
			||||||
 | 
					    } else if jumpInput != .press && Keyboard.down(.space) {
 | 
				
			||||||
 | 
					      jumpInput = .held
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Turning input
 | 
					    // Turning input
 | 
				
			||||||
      let turning = pad.rightStick.radialDeadzone(min: 0.1, max: 1)
 | 
					    self._rotation += turning * deltaTime * 3.0
 | 
				
			||||||
      _rotation += turning * deltaTime * 3.0
 | 
					 | 
				
			||||||
    if self._rotation.x < 0.0 {
 | 
					    if self._rotation.x < 0.0 {
 | 
				
			||||||
      self._rotation.x += .pi * 2
 | 
					      self._rotation.x += .pi * 2
 | 
				
			||||||
    } else if _rotation.x > .pi * 2 {
 | 
					    } else if _rotation.x > .pi * 2 {
 | 
				
			||||||
@ -47,10 +82,10 @@ struct Player {
 | 
				
			|||||||
    self._rotation.y = self._rotation.y.clamp(-.pi * 0.5, .pi * 0.5)
 | 
					    self._rotation.y = self._rotation.y.clamp(-.pi * 0.5, .pi * 0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Jumping
 | 
					    // Jumping
 | 
				
			||||||
      if pad.pressed(.east) {
 | 
					    if jumpInput == .press {
 | 
				
			||||||
      self._shouldJump = 0.3
 | 
					      self._shouldJump = 0.3
 | 
				
			||||||
    } else if self._shouldJump != .none {
 | 
					    } else if self._shouldJump != .none {
 | 
				
			||||||
        if pad.down(.east) {
 | 
					      if jumpInput == .held {
 | 
				
			||||||
        self._shouldJump! -= deltaTime
 | 
					        self._shouldJump! -= deltaTime
 | 
				
			||||||
        if self._shouldJump! <= 0.0 {
 | 
					        if self._shouldJump! <= 0.0 {
 | 
				
			||||||
          self._shouldJump = .none
 | 
					          self._shouldJump = .none
 | 
				
			||||||
@ -66,15 +101,18 @@ struct Player {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Movement (slower in air than on ground)
 | 
					    // Movement (slower in air than on ground)
 | 
				
			||||||
      let movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
 | 
					    let movementMagnitude = simd_length(movement)
 | 
				
			||||||
 | 
					    if movementMagnitude > 1.0 {
 | 
				
			||||||
 | 
					      movement /= movementMagnitude
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    let rotc = cos(self._rotation.x), rots = sin(self._rotation.x)
 | 
					    let rotc = cos(self._rotation.x), rots = sin(self._rotation.x)
 | 
				
			||||||
    let coeff = self._onGround ? Self.accelerationCoeff : Self.airAccelCoeff
 | 
					    let coeff = self._onGround ? Self.accelerationCoeff : Self.airAccelCoeff
 | 
				
			||||||
    self._velocity.x += (movement.x * rotc - movement.y * rots) * coeff * deltaTime
 | 
					    self._velocity.x += (movement.x * rotc - movement.y * rots) * coeff * deltaTime
 | 
				
			||||||
    self._velocity.z += (movement.y * rotc + movement.x * rots) * coeff * deltaTime
 | 
					    self._velocity.z += (movement.y * rotc + movement.x * rots) * coeff * deltaTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Flying and unflying
 | 
					    // Flying and unflying
 | 
				
			||||||
      self._velocity.y += (pad.rightTrigger - pad.leftTrigger) * 36 * deltaTime
 | 
					    flying = flying.clamp(-1, 1)
 | 
				
			||||||
    }
 | 
					    self._velocity.y += flying * Self.flySpeedCoeff * deltaTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Apply gravity
 | 
					    // Apply gravity
 | 
				
			||||||
    self._velocity.y -= Self.gravityCoeff * deltaTime
 | 
					    self._velocity.y -= Self.gravityCoeff * deltaTime
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user