diff --git a/MiniBlocks.playground/Sources/Component/PlayerControlComponent.swift b/MiniBlocks.playground/Sources/Component/PlayerControlComponent.swift index 636f9dd..d95cb04 100644 --- a/MiniBlocks.playground/Sources/Component/PlayerControlComponent.swift +++ b/MiniBlocks.playground/Sources/Component/PlayerControlComponent.swift @@ -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 diff --git a/MiniBlocks.playground/Sources/Component/PlayerGravityComponent.swift b/MiniBlocks.playground/Sources/Component/PlayerGravityComponent.swift index 9b752ae..0cd89aa 100644 --- a/MiniBlocks.playground/Sources/Component/PlayerGravityComponent.swift +++ b/MiniBlocks.playground/Sources/Component/PlayerGravityComponent.swift @@ -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? { @@ -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 { diff --git a/MiniBlocks.playground/Sources/Model/Strip.swift b/MiniBlocks.playground/Sources/Model/Strip.swift index efd294c..1f0972e 100644 --- a/MiniBlocks.playground/Sources/Model/Strip.swift +++ b/MiniBlocks.playground/Sources/Model/Strip.swift @@ -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) } } diff --git a/MiniBlocks.playground/Sources/Model/World.swift b/MiniBlocks.playground/Sources/Model/World.swift index 423f8fd..c977333 100644 --- a/MiniBlocks.playground/Sources/Model/World.swift +++ b/MiniBlocks.playground/Sources/Model/World.swift @@ -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).