diff --git a/Sources/Voxelotl/FloatExtensions.swift b/Sources/Voxelotl/FloatExtensions.swift index 25d9e82..b8f41b9 100644 --- a/Sources/Voxelotl/FloatExtensions.swift +++ b/Sources/Voxelotl/FloatExtensions.swift @@ -8,3 +8,16 @@ public extension FloatingPoint { @inline(__always) func clamp(_ a: Self, _ b: Self) -> Self { min(max(self, a), b) } @inline(__always) var saturated: Self { self.clamp(0, 1) } } + +extension SIMD3 where Scalar: FloatingPoint { + @inline(__always) static var X: Self { Self(1, 0, 0) } + @inline(__always) static var Y: Self { Self(0, 1, 0) } + @inline(__always) static var Z: Self { Self(0, 0, 1) } + + @inline(__always) static var up: Self { Y } + @inline(__always) static var down: Self { -Y } + @inline(__always) static var left: Self { -X } + @inline(__always) static var right: Self { X } + @inline(__always) static var forward: Self { -Z } + @inline(__always) static var back: Self { Z } +} diff --git a/Sources/Voxelotl/Game.swift b/Sources/Voxelotl/Game.swift index 32d4c68..22c32e3 100644 --- a/Sources/Voxelotl/Game.swift +++ b/Sources/Voxelotl/Game.swift @@ -70,8 +70,9 @@ class Game: GameDelegate { let deltaTime = min(Float(time.delta.asFloat), 1.0 / 15) if let pad = GameController.current?.state { + // Delete block underneath player if pad.pressed(.south) { - chunk.setBlockInternally(at: SIMD3(player.position - SIMD3(0, 2, 0)), type: .air) + chunk.setBlockInternally(at: SIMD3(player.position + .down * 0.2), type: .air) } // Player reset if pad.pressed(.back) { @@ -95,9 +96,7 @@ class Game: GameDelegate { player.update(deltaTime: deltaTime, boxes: boxes) camera.position = player.eyePosition - camera.rotation = - simd_quatf(angle: player.rotation.y, axis: .init(1, 0, 0)) * - simd_quatf(angle: player.rotation.x, axis: .init(0, 1, 0)) + camera.rotation = player.eyeRotation } func draw(_ renderer: Renderer, _ time: GameTime) { diff --git a/Sources/Voxelotl/Player.swift b/Sources/Voxelotl/Player.swift index 6cd8c2c..cc97058 100644 --- a/Sources/Voxelotl/Player.swift +++ b/Sources/Voxelotl/Player.swift @@ -2,7 +2,7 @@ import simd struct Player { static let height: Float = 1.8 - static let radius: Float = 0.4 + static let radius: Float = 0.22 static let bounds = AABB( from: .init(-Self.radius, 0, -Self.radius), to: .init(Self.radius, Self.height, Self.radius)) @@ -10,9 +10,12 @@ struct Player { static let eyeLevel: Float = 1.4 static let epsilon = Float.ulpOfOne * 10 - static let accelerationCoeff: Float = 7.5 - static let gravityCoeff: Float = 12 - static let jumpVelocity: Float = 9 + static let accelerationCoeff: Float = 75 + static let airAccelCoeff: Float = 3 + static let gravityCoeff: Float = 20 + static let frictionCoeff: Float = 22 + static let jumpVelocity: Float = 7 + static let maxVelocity: Float = 160 private var _position = SIMD3.zero private var _velocity = SIMD3.zero @@ -24,11 +27,14 @@ struct Player { public var velocity: SIMD3 { get { self._velocity } set { self._velocity = newValue } } public var rotation: SIMD2 { get { self._rotation } set { self._rotation = newValue } } - public var eyePosition: SIMD3 { self._position + .init(0, Self.eyeLevel, 0) } + public var eyePosition: SIMD3 { self._position + .up * Self.eyeLevel } + public var eyeRotation: simd_quatf { + .init(angle: self._rotation.y, axis: .right) * + .init(angle: self._rotation.x, axis: .up) + } mutating func update(deltaTime: Float, boxes: [Box]) { if let pad = GameController.current?.state { - // Turning input let turning = pad.rightStick.radialDeadzone(min: 0.1, max: 1) _rotation += turning * deltaTime * 3.0 @@ -39,25 +45,21 @@ struct Player { } self._rotation.y = self._rotation.y.clamp(-.pi * 0.5, .pi * 0.5) - // Movement (slower in air than on ground) - let movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1) - let rotc = cos(self._rotation.x), rots = sin(self._rotation.x) - let movementScale: Float = self._onGround ? 1.0 : 0.4 - self._velocity.x = ( - movement.x * rotc - movement.y * rots - ) * Self.accelerationCoeff * movementScale - self._velocity.z = ( - movement.y * rotc + movement.x * rots - ) * Self.accelerationCoeff * movementScale - + // Jumping if self._onGround { - // Jumping if pad.pressed(.east) { self._velocity.y = Self.jumpVelocity self._onGround = false } } + // Movement (slower in air than on ground) + let movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1) + let rotc = cos(self._rotation.x), rots = sin(self._rotation.x) + let coeff = self._onGround ? Self.accelerationCoeff : Self.airAccelCoeff + self._velocity.x += (movement.x * rotc - movement.y * rots) * coeff * deltaTime + self._velocity.z += (movement.y * rotc + movement.x * rots) * coeff * deltaTime + // Flying and unflying self._velocity.y += (pad.rightTrigger - pad.leftTrigger) * 36 * deltaTime } @@ -75,27 +77,27 @@ struct Player { } return nil } - self._position.x += _velocity.x * deltaTime + self._position.x += self._velocity.x * deltaTime if let aabb = checkCollision(self._position) { - if _velocity.x < 0 { + if self._velocity.x < 0 { self._position.x = aabb.right + Self.radius + Self.epsilon } else { self._position.x = aabb.left - Self.radius - Self.epsilon } self._velocity.x = 0 } - self._position.z += _velocity.z * deltaTime + self._position.z += self._velocity.z * deltaTime if let aabb = checkCollision(self._position) { - if _velocity.z < 0 { + if self._velocity.z < 0 { self._position.z = aabb.near + Self.radius + Self.epsilon } else { self._position.z = aabb.far - Self.radius - Self.epsilon } self._velocity.z = 0 } - self._position.y += _velocity.y * deltaTime - if let aabb = checkCollision(self._position) { - if _velocity.y < 0 { + self._position.y += self._velocity.y * deltaTime + if let aabb = checkCollision(self._velocity.y > 0 ? self._position + .down * Self.epsilon : self.position) { + if self._velocity.y < 0 { self._position.y = aabb.top + Self.epsilon self._onGround = true } else { @@ -109,20 +111,14 @@ struct Player { // Ground friction if self._onGround { - self._velocity.x = 0 - self._velocity.z = 0 + self._velocity.x /= 1.0 + Self.frictionCoeff * deltaTime + self._velocity.z /= 1.0 + Self.frictionCoeff * deltaTime } - if self._velocity.x > 10 { - self._velocity.x = 10 - } - - if self._velocity.y > 10 { - self._velocity.y = 10 - } - - if abs(self._velocity.y) > 40 { - self._velocity.y = Float(signOf: self._velocity.y, magnitudeOf: 40.0) + // Limit maximum velocity + let velocityLen = simd_length(self._velocity) + if velocityLen > Self.maxVelocity { + self._velocity = self._velocity / velocityLen * Self.maxVelocity } } }