Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,16 @@ class PlayerControlComponent: GKComponent {
// Running into terrain pushes the player back, causing them to 'slide' along the block.
// For more info, look up 'AABB sliding collision response'.
let feetPos = position
let headPos = feetPos + Vec3(y: 1)
var finalVelocity = requestedVelocity
var iterations = 0

if gameMode.enablesGravityAndCollisions {
while let hit = worldNode?.hitTestWithSegment(from: SCNVector3(feetPos), to: SCNVector3(feetPos + finalVelocity)).first, iterations < maxCollisionIterations {
while iterations < maxCollisionIterations {
let hit = [feetPos, headPos]
.flatMap { worldNode?.hitTestWithSegment(from: SCNVector3($0), to: SCNVector3($0 + finalVelocity)) ?? [] }
.first { !(world?.block(at: BlockPos3(rounding: $0.node.position))?.type.isLiquid ?? false) }
guard let hit = hit else { break }
let normal = Vec3(hit.worldNormal)
let repulsion = normal * abs(finalVelocity.dot(normal))
finalVelocity += repulsion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import SceneKit

/// Accelerates the associated node downwards (i.e. in negative-y direction).
class PlayerGravityComponent: GKComponent {
var acceleration: Double = -0.4
private let baseAcceleration: Double = -0.4
private let submergedFactor: Double = 0.5

private var throttler = Throttler(interval: 0.1)

private var world: World? {
Expand Down Expand Up @@ -31,10 +33,18 @@ class PlayerGravityComponent: GKComponent {
var velocity = playerInfo!.velocity

let y = position.y
let yBound = world.height(below: BlockPos3(rounding: position)).map { $0 + 1 }
let blockPos = BlockPos3(rounding: position)
let yBound = world.height(below: blockPos, includeLiquids: false).map { $0 + 1 }

let isSubmerged = world.block(at: blockPos)?.type.isLiquid ?? false
let willBeOnGround = !playerInfo!.leavesGround && yBound.map { y + velocity.y <= Double($0) } ?? false

var acceleration = baseAcceleration

if isSubmerged {
acceleration *= submergedFactor
}

if willBeOnGround {
velocity.y = 0
if !playerInfo!.isOnGround {
Expand Down
4 changes: 2 additions & 2 deletions MiniBlocks.playground/Sources/Model/Strip.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ struct Strip: Hashable, Codable, Sequence {
set { blocks[y] = newValue }
}

func block(below y: Int?) -> (y: Int, Block)? {
func block(below y: Int?, includeLiquids: Bool = true) -> (y: Int, Block)? {
blocks
.filter { $0.key <= (y ?? .max) }
.filter { $0.key <= (y ?? .max) && (includeLiquids || !$0.value.type.isLiquid) }
.max { $0.key < $1.key }
.map { (y: $0.key, block: $0.value) }
}
Expand Down
4 changes: 2 additions & 2 deletions MiniBlocks.playground/Sources/Model/World.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ struct World: Codable, Sequence {
}

/// Fetches the height below the given position. O(n) where n is the number of blocks in the strip at the given (x, z) coordinates.
func height(below pos: BlockPos3) -> Int? {
self[pos.asVec2].block(below: pos.y)?.y
func height(below pos: BlockPos3, includeLiquids: Bool = true) -> Int? {
self[pos.asVec2].block(below: pos.y, includeLiquids: includeLiquids)?.y
}

/// Fetches the block at the given position. O(1).
Expand Down