From 7d69e1db4a4fba40978846471e5731d59deb6e5d Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Wed, 10 Jul 2024 13:14:06 +0200 Subject: [PATCH] fix psyhics not a typo --- src/main/java/net/pivipi/ball/BallKicker.java | 2 +- .../java/net/pivipi/physics/Collision.java | 132 ++++++++++++++---- .../net/pivipi/physics/CollisionData.java | 32 ++++- src/main/java/net/pivipi/physics/Physics.java | 75 +++++----- 4 files changed, 178 insertions(+), 63 deletions(-) diff --git a/src/main/java/net/pivipi/ball/BallKicker.java b/src/main/java/net/pivipi/ball/BallKicker.java index c9da3c8..3918ad3 100644 --- a/src/main/java/net/pivipi/ball/BallKicker.java +++ b/src/main/java/net/pivipi/ball/BallKicker.java @@ -87,7 +87,7 @@ public class BallKicker { // TODO apply physics settings here Player player = event.getPlayer(); CollisionData collisionData = new CollisionData(); - Collision.pushOutside(player, stadium.ball, collisionData); + Collision.pushOutside(player, stadium.ball, false, collisionData); double distance = collisionData.distance.withY(0).distance(0, 0, 0); if (collisionData.distance.y() < verticalReach && distance < reach) { diff --git a/src/main/java/net/pivipi/physics/Collision.java b/src/main/java/net/pivipi/physics/Collision.java index ca9b6d6..ec1c752 100644 --- a/src/main/java/net/pivipi/physics/Collision.java +++ b/src/main/java/net/pivipi/physics/Collision.java @@ -1,5 +1,7 @@ package net.pivipi.physics; +import java.lang.foreign.ValueLayout.OfBoolean; + import net.minestom.server.collision.BoundingBox; import net.minestom.server.collision.SweepResult; import net.minestom.server.collision.VisibleSweepResult; @@ -7,31 +9,42 @@ import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Player; +import net.minestom.server.listener.common.KeepAliveListener; public class Collision { + private static CollisionData willCollide(Pos pos, BoundingBox boundingBox, Entity moving, Vec movement) { + SweepResult sweepResult = new SweepResult(1, 0, 0, 0, null, 0, 0, 0); + boolean collides = boundingBox.intersectBoxSwept(moving.getPosition(), movement, pos, moving.getBoundingBox(), sweepResult); + VisibleSweepResult visibleSweepResult = new VisibleSweepResult(sweepResult); + + CollisionData collisionData = new CollisionData(); + collisionData.swept = true; + collisionData.percentage = visibleSweepResult.res; + collisionData.collidedPosition = new Vec(visibleSweepResult.collidedPositionX, visibleSweepResult.collidedPositionY, visibleSweepResult.collidedPositionZ); + collisionData.normal = new Vec(visibleSweepResult.normalX, visibleSweepResult.normalY, visibleSweepResult.normalZ); + + return collides ? collisionData : null; + } /** * - * @param entity + * @param entity the static entity * @param moving the moving entity - * @param velocity the applied velocity + * @param movement the movement * @return null if no collision */ - public static VisibleSweepResult willCollideWithEntity(Entity entity, Entity moving, Vec movement) { - SweepResult sweepResult = new SweepResult(1, 0, 0, 0, null, 0, 0, 0); - boolean collided = entity.getBoundingBox().intersectBoxSwept(moving.getPosition(), movement, entity.getPosition(), moving.getBoundingBox(), sweepResult); - return collided ? new VisibleSweepResult(sweepResult) : null; + public static CollisionData willCollideWithEntity(Entity entity, Entity moving, Vec movement) { + return willCollide(entity.getPosition(), entity.getBoundingBox(), moving, movement); } /** * * @param groundY ground Y * @param moving the moving entity + * @param movement the movement * @return null if no collision */ - public static VisibleSweepResult willCollideWithGround(double groundY, Entity moving, Vec movement) { - SweepResult sweepResult = new SweepResult(1, 0, 0, 0, null, 0, 0, 0); - boolean collided = new BoundingBox(10, 1, 10).intersectBoxSwept(moving.getPosition(), movement, moving.getPosition().withY(groundY - 1).sub(5, 0, 5), moving.getBoundingBox(), sweepResult); - return collided ? new VisibleSweepResult(sweepResult) : null; + public static CollisionData willCollideWithGround(double groundY, Entity moving, Vec movement) { + return willCollide(new Pos(Double.MIN_VALUE, 0, Double.MIN_VALUE), new BoundingBox(Double.MAX_VALUE, 64, Double.MAX_VALUE), moving, movement); } public static boolean collidesWithEntity(Entity entity1, Entity entity2) { @@ -42,32 +55,40 @@ public class Collision { return new BoundingBox(10, 1, 10).intersectEntity(entity.getPosition().withY(groundY - 1).sub(5, 0, 5), entity); } - /** - * - * @param pusher the entity that's pushing like a player - * @param pushed the entity that's being pushed like a ball - * @return offset you must apply to push the entity outside, null if doesn't collide - */ - public static Vec pushOutside(Entity pusher, Entity pushed) { - return pushOutside(pusher, pushed, null); + public static boolean collidesWithBlock(Pos blockPos, Entity entity) { + return new BoundingBox(1, 1, 1).intersectEntity(blockPos, entity); } /** * * @param pusher the entity that's pushing like a player * @param pushed the entity that's being pushed like a ball - * @param collisionData puts data in this object * @return offset you must apply to push the entity outside, null if doesn't collide */ - public static Vec pushOutside(Entity pusher, Entity pushed, CollisionData collisionData) { + public static Vec pushOutside(Entity pusher, Entity pushed, boolean y, CollisionData collisionData) { + return pushOutside(pusher.getBoundingBox(), pusher.getPosition(), pushed, y, collisionData); + } + + /** + * + * @param pusher the entity that's pushing like a player + * @param pushed the entity that's being pushed like a ball + * @return offset you must apply to push the entity outside, null if doesn't collide + */ + public static Vec pushOutside(Pos blockPos, Entity pushed, boolean y, CollisionData collisionData) { + return pushOutside(new BoundingBox(1, 1, 1), blockPos, pushed, y, collisionData); + } + + + /*public static Vec pushOutside(BoundingBox pusherBox, Pos pusherPos, Entity pushed, CollisionData collisionData) { double ballXSize = pushed.getBoundingBox().width(); - double playerXSize = pusher.getBoundingBox().width(); + double playerXSize = pusherBox.width(); double ballZSize = pushed.getBoundingBox().depth(); - double playerZSize = pusher.getBoundingBox().depth(); + double playerZSize = pusherBox.depth(); double ballHeight = pushed.getBoundingBox().height(); - double playerHeight = pusher.getBoundingBox().height(); + double playerHeight = pusherBox.height(); Pos ballCenterPos = pushed.getPosition(); - Pos playerCenterPos = pusher.getPosition(); + Pos playerCenterPos = pusherPos; Pos ballPos = ballCenterPos.sub(ballXSize / 2, 0, ballZSize / 2); // corner Pos playerPos = playerCenterPos.sub(playerXSize / 2, 0, playerZSize / 2); @@ -107,5 +128,68 @@ public class Collision { return diffX < diffZ ? newBallOffset.withX(0) : newBallOffset.withZ(0); return null; + }*/ + /** + * + * @param pusherBox the entity that's pushing like a player or a block, its bounding box + * @param pusherPos the position, bottom center + * @param pushed the entity that's being pushed like a ball + * @param collisionData puts data in this object + * @return offset you must apply to push the entity outside, null if doesn't collide + */ + private static Vec pushOutside(BoundingBox pusherBox, Pos pusherPos, Entity pushed, boolean y, CollisionData collisionData) { + double ballXSize = pushed.getBoundingBox().width(); + double playerXSize = pusherBox.width(); + double ballZSize = pushed.getBoundingBox().depth(); + double playerZSize = pusherBox.depth(); + double ballHeight = pushed.getBoundingBox().height(); + double playerHeight = pusherBox.height(); + + Pos ballCenterPos = pushed.getPosition(); + Pos playerCenterPos = pusherPos; + + Pos ballPos = ballCenterPos.sub(ballXSize / 2, 0, ballZSize / 2); + Pos playerPos = playerCenterPos.sub(playerXSize / 2, 0, playerZSize / 2); + Vec ballDistance = ballCenterPos.sub(playerCenterPos).asVec(); + + if (collisionData != null) { + collisionData.distance = ballDistance; + } + + double diffX = ballDistance.x() > 0 + ? ballPos.x() - (playerPos.x() + playerXSize) + : playerPos.x() - (ballPos.x() + ballXSize); + double diffY = ballDistance.y() > 0 + ? ballPos.y() - (playerPos.y() + playerHeight) + : playerPos.y() - (ballPos.y() + ballHeight); + double diffZ = ballDistance.z() > 0 + ? ballPos.z() - (playerPos.z() + playerZSize) + : playerPos.z() - (ballPos.z() + ballZSize); + + Vec newBallOffset = new Vec(0); + Vec offset = null; + + if (diffX < 0 && diffZ < 0 && diffY < 0) { + newBallOffset = newBallOffset.withX(ballDistance.x() > 0 ? -diffX : diffX); + newBallOffset = newBallOffset.withY(ballDistance.x() > 0 ? -diffY : diffY); + newBallOffset = newBallOffset.withZ(ballDistance.z() > 0 ? -diffZ : diffZ); + + if (!y) diffY = Double.POSITIVE_INFINITY; + + if (diffX < diffZ && diffX < diffY) + offset = newBallOffset.mul(0, 0, 1); + else if (diffY < diffX && diffY < diffZ) + offset = newBallOffset.mul(0, 1, 0); + else if (diffZ < diffX && diffZ < diffY) + offset = newBallOffset.mul(1, 0, 0); + } + + if (collisionData != null) { + collisionData.distance = ballDistance; + collisionData.offset = offset; + } + + return offset; } + } diff --git a/src/main/java/net/pivipi/physics/CollisionData.java b/src/main/java/net/pivipi/physics/CollisionData.java index 2cf2ba7..ca26f35 100644 --- a/src/main/java/net/pivipi/physics/CollisionData.java +++ b/src/main/java/net/pivipi/physics/CollisionData.java @@ -1,10 +1,40 @@ package net.pivipi.physics; import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; public class CollisionData { + public boolean swept; + /** - * distance between centers of entities (but bottom y) + * distance between centers of entities (but bottom y) + * only when not swept */ public Vec distance; + + /** + * the offset to the entity to to resolve the collision + * only when not swept + */ + public Vec offset; + + /** + * percentage of movement until collision (only when swept) + */ + public double percentage; + + /** + * the position where the collision takes place (only when swept) + */ + public Vec collidedPosition; + + /** + * I don't know what this is but it looks useful (only when swept) + */ + public Vec normal; + + /** + * used to track what entity collided + */ + public Entity entity; } diff --git a/src/main/java/net/pivipi/physics/Physics.java b/src/main/java/net/pivipi/physics/Physics.java index 2e517e7..94d64d6 100644 --- a/src/main/java/net/pivipi/physics/Physics.java +++ b/src/main/java/net/pivipi/physics/Physics.java @@ -1,5 +1,7 @@ package net.pivipi.physics; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import net.minestom.server.collision.VisibleSweepResult; @@ -60,61 +62,60 @@ public class Physics { private Vec applyPhysics(double delta, Set players) { velocity = velocity.sub(0, gravity * delta, 0).mul(1 - (airFriction * delta), 1, 1 - (airFriction * delta)); + ArrayList collisions = new ArrayList<>(); if (Collision.collidesWithGround(groundY, entity)) { entity.teleport(entity.getPosition().withY(groundY)); velocity = velocity.mul(1 - (groundFriction * delta), -blockBounciness, 1 - (groundFriction * delta)); } else { for (Player player : players) { - boolean collides = Collision.collidesWithEntity(player, entity); - if (collides) { - handlePlayerTouch(player); - - break; + CollisionData collisionData = new CollisionData(); + Vec offset = Collision.pushOutside(player, entity, false, collisionData); // TODO predict the collision + + if (offset != null) { + collisionData.entity = player; + collisions.add(collisionData); } } } + for (CollisionData collisionData : collisions) { + if (collisionData.entity instanceof Player) { + handlePlayerTouch((Player) collisionData.entity, collisionData); + } + } + return velocity; } - private void handlePlayerTouch(Player player) { - CollisionData collisionData = new CollisionData(); - Vec offset = Collision.pushOutside(player, entity, collisionData); // TODO predict the collision + private void handlePlayerTouch(Player player, CollisionData collisionData) { + Vec offset = collisionData.offset; - if (offset != null) { - Pos movementPos = player.getPosition().sub(player.getPreviousPosition()); + Pos movementPos = player.getPosition().sub(player.getPreviousPosition()); - boolean oppositeX = (collisionData.distance.x() > 0 ? velocity.x() < 0 : velocity.x() > 0); - boolean oppositeZ = (collisionData.distance.z() > 0 ? velocity.z() < 0 : velocity.z() > 0); - double headOffsetY = player.getBoundingBox().height() - collisionData.distance.y(); + boolean oppositeX = (collisionData.distance.x() > 0 ? velocity.x() < 0 : velocity.x() > 0); + boolean oppositeZ = (collisionData.distance.z() > 0 ? velocity.z() < 0 : velocity.z() > 0); + double headOffsetY = player.getBoundingBox().height() - collisionData.distance.y(); - System.out.println(offset); - System.out.println(oppositeX); - System.out.println(velocity.x()); + System.out.println(offset); + System.out.println(oppositeX); + System.out.println(velocity.x()); - if (headOffsetY < 0.6) { // landed on head, magic value head height - if (movementPos.y() > 0) { - double headPitchDiff = Math.abs(movementPos.pitch()); - System.out.println(headPitchDiff); - addVelocity(player.getPosition().direction().mul(headPitchDiff / headNodStep).withY(headJumpStrength)); - } else velocity = velocity.mul(1, player.isSneaking() ? -playerSneakingHeadBounciness : -playerHeadBounciness, 1); - entity.teleport(entity.getPosition().withY(y -> y + headOffsetY)); - } else { - System.out.println("tping"); - entity.teleport(entity.getPosition().add(offset.mul(1.1))); // magic value to make some space in case of desync - double bodyBounciness = player.isSneaking() ? -playerSneakingBodyBounciness : -playerBodyBounciness; - - System.out.println(velocity); - - velocity = velocity - .withX(x -> oppositeX ? x * bodyBounciness : x + movementPos.x() * playerCarryStrength) - .withY(y -> collisionData.distance.y() < 1.0 ? movementPos.y() * jumpStrength : y) // magic value legs - .withZ(z -> oppositeZ ? z * bodyBounciness : z + movementPos.z() * playerCarryStrength); - - System.out.println(velocity); - } + if (headOffsetY < 0.6) { // landed on head, magic value head height + if (movementPos.y() > 0) { + double headPitchDiff = Math.abs(movementPos.pitch()); + System.out.println(headPitchDiff); + addVelocity(player.getPosition().direction().mul(headPitchDiff / headNodStep).withY(headJumpStrength)); + } else velocity = velocity.mul(1, player.isSneaking() ? -playerSneakingHeadBounciness : -playerHeadBounciness, 1); + entity.teleport(entity.getPosition().withY(y -> y + headOffsetY)); + } else { + entity.teleport(entity.getPosition().add(offset.mul(1.1))); // magic value to make some space in case of desync + double bodyBounciness = player.isSneaking() ? -playerSneakingBodyBounciness : -playerBodyBounciness; + velocity = velocity + .withX(x -> oppositeX ? x * bodyBounciness : x + movementPos.x() * playerCarryStrength) + .withY(y -> collisionData.distance.y() < 1.0 ? movementPos.y() * jumpStrength : y) // magic value legs + .withZ(z -> oppositeZ ? z * bodyBounciness : z + movementPos.z() * playerCarryStrength); } }