Refactor physics to simplify code (#1315)

This commit is contained in:
iam 2022-08-07 18:25:08 -04:00 committed by GitHub
parent f62d4e47fc
commit 4a79a3af26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 204 deletions

View File

@ -10,6 +10,7 @@ import net.minestom.server.entity.Player;
import net.minestom.server.entity.metadata.other.ArmorStandMeta;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import net.minestom.server.utils.block.BlockIterator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -132,7 +133,7 @@ final class BlockCollision {
@NotNull Block.Getter getter,
@Nullable PhysicsResult lastPhysicsResult) {
// Allocate once and update values
SweepResult finalResult = new SweepResult(1, 0, 0, 0, null);
SweepResult finalResult = new SweepResult(1 - Vec.EPSILON, 0, 0, 0, null);
boolean foundCollisionX = false, foundCollisionY = false, foundCollisionZ = false;
Point collisionYBlock = null;
@ -148,7 +149,6 @@ final class BlockCollision {
&& velocity.x() == 0 && velocity.z() == 0
&& entityPosition.samePoint(lastPhysicsResult.newPosition())
&& lastPhysicsResult.blockTypeY() != Block.AIR) {
velocity = velocity.withY(0);
foundCollisionY = true;
collisionYBlock = lastPhysicsResult.collidedBlockY();
blockYType = lastPhysicsResult.blockTypeY();
@ -171,7 +171,7 @@ final class BlockCollision {
// Looping until there are no collisions will allow the entity to move in axis other than the collision axis after a collision.
while (res.collisionX() || res.collisionY() || res.collisionZ()) {
// Reset final result
finalResult.res = 1;
finalResult.res = 1 - Vec.EPSILON;
finalResult.normalX = 0;
finalResult.normalY = 0;
finalResult.normalZ = 0;
@ -261,16 +261,31 @@ final class BlockCollision {
} else {
// When large moves are done we need to ray-cast to find all blocks that could intersect with the movement
for (Vec point : allFaces) {
RayUtils.RaycastCollision(velocity, point.add(entityPosition), getter, boundingBox, entityPosition, finalResult);
BlockIterator iterator = new BlockIterator(Vec.fromPoint(point.add(entityPosition)), velocity, 0, (int) Math.ceil(velocity.length()));
while (iterator.hasNext()) {
Point p = iterator.next();
// sqrt 3 (1.733) is the maximum error
if (Vec.fromPoint(p.sub(entityPosition)).length() > (finalResult.res * velocity.length() + 1.733))
break;
if (checkBoundingBox(p.blockX(), p.blockY(), p.blockZ(), velocity, entityPosition, boundingBox, getter, finalResult))
break;
}
}
}
final boolean collisionX = finalResult.normalX != 0;
final boolean collisionY = finalResult.normalY != 0;
final boolean collisionZ = finalResult.normalZ != 0;
final double deltaX = finalResult.res * velocity.x();
final double deltaY = finalResult.res * velocity.y();
final double deltaZ = finalResult.res * velocity.z();
boolean collisionX = finalResult.normalX != 0;
boolean collisionY = finalResult.normalY != 0;
boolean collisionZ = finalResult.normalZ != 0;
double deltaX = finalResult.res * velocity.x();
double deltaY = finalResult.res * velocity.y();
double deltaZ = finalResult.res * velocity.z();
if (Math.abs(deltaX) < Vec.EPSILON) deltaX = 0;
if (Math.abs(deltaY) < Vec.EPSILON) deltaY = 0;
if (Math.abs(deltaZ) < Vec.EPSILON) deltaZ = 0;
final Pos finalPos = entityPosition.add(deltaX, deltaY, deltaZ);

View File

@ -36,9 +36,9 @@ public final class BoundingBox implements Shape {
@Override
@ApiStatus.Experimental
public boolean intersectBox(@NotNull Point positionRelative, @NotNull BoundingBox boundingBox) {
return (minX() + positionRelative.x() <= boundingBox.maxX() && maxX() + positionRelative.x() >= boundingBox.minX()) &&
(minY() + positionRelative.y() <= boundingBox.maxY() && maxY() + positionRelative.y() >= boundingBox.minY()) &&
(minZ() + positionRelative.z() <= boundingBox.maxZ() && maxZ() + positionRelative.z() >= boundingBox.minZ());
return (minX() + positionRelative.x() <= boundingBox.maxX() - Vec.EPSILON / 2 && maxX() + positionRelative.x() >= boundingBox.minX() + Vec.EPSILON / 2) &&
(minY() + positionRelative.y() <= boundingBox.maxY() - Vec.EPSILON / 2 && maxY() + positionRelative.y() >= boundingBox.minY() + Vec.EPSILON / 2) &&
(minZ() + positionRelative.z() <= boundingBox.maxZ() - Vec.EPSILON / 2 && maxZ() + positionRelative.z() >= boundingBox.minZ() + Vec.EPSILON / 2);
}
@Override

View File

@ -74,7 +74,7 @@ public final class CollisionUtils {
BoundingBox.ZERO,
Pos.fromPoint(start), Vec.fromPoint(end.sub(start)),
null);
return shape.intersectBox(end.sub(result.newPosition()), BoundingBox.ZERO);
return shape.intersectBox(end.sub(result.newPosition()).sub(Vec.EPSILON), BoundingBox.ZERO);
}
public static PhysicsResult handlePhysics(@NotNull Entity entity, @NotNull Vec entityVelocity) {

View File

@ -3,104 +3,8 @@ package net.minestom.server.collision;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.instance.block.Block;
final class RayUtils {
public static void RaycastCollision(Vec rayDirection, Point rayStart, Block.Getter getter, BoundingBox boundingBox, Pos entityCentre, SweepResult finalResult) {
// This works by finding all the x, y and z grid line intersections and calculating the value of the point at that intersection
// Finding all the intersections will give us all the full blocks that are traversed by the ray
if (rayDirection.x() != 0) {
// Which direction we're stepping the block boundary in
double xStep = rayDirection.x() < 0 ? -1 : 1;
// If we are going in the positive direction, the block that we stepped over is the one we want
int xFix = rayDirection.x() > 0 ? 1 : 0;
// Total number of axis block boundaries that will be passed
int xStepCount = (int) Math.ceil((rayDirection.x()) / xStep) + xFix;
int xStepsCompleted = xFix;
while (xStepsCompleted <= xStepCount) {
// Get the axis value
int xi = (int) (xStepsCompleted * xStep + rayStart.blockX());
double factor = (xi - rayStart.x()) / rayDirection.x();
if (Math.abs(rayDirection.x() * finalResult.res) - Math.abs(rayStart.x() - (xi)) < -2) break;
// Solve for y and z
int yi = (int) Math.floor(rayDirection.y() * factor + rayStart.y());
// If the y distance is much greater than the collision point that is currently being used, break
if (Math.abs(rayDirection.y() * finalResult.res) - Math.abs(rayStart.y() - (yi)) < -2) break;
int zi = (int) Math.floor(rayDirection.z() * factor + rayStart.z());
if (Math.abs(rayDirection.z() * finalResult.res) - Math.abs(rayStart.z() - (zi)) < -2) break;
xi -= xFix;
xStepsCompleted++;
// Check for collisions with the found block
// If a collision was found, break
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, getter, finalResult))
break;
}
}
if (rayDirection.z() != 0) {
double zStep = rayDirection.z() < 0 ? -1 : 1;
int zFix = rayDirection.z() > 0 ? 1 : 0;
int zStepsCompleted = zFix;
int zStepCount = (int) Math.ceil((rayDirection.z()) / zStep) + zFix;
while (zStepsCompleted <= zStepCount) {
int zi = (int) (zStepsCompleted * zStep + rayStart.blockZ());
double factor = (zi - rayStart.z()) / rayDirection.z();
if (Math.abs(rayDirection.z() * finalResult.res) - Math.abs(rayStart.z() - (zi)) < -2) break;
int xi = (int) Math.floor(rayDirection.x() * factor + rayStart.x());
if (Math.abs(rayDirection.x() * finalResult.res) - Math.abs(rayStart.x() - (xi)) < -2) break;
int yi = (int) Math.floor(rayDirection.y() * factor + rayStart.y());
if (Math.abs(rayDirection.y() * finalResult.res) - Math.abs(rayStart.y() - (yi)) < -2) break;
zi -= zFix;
zStepsCompleted++;
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, getter, finalResult))
break;
}
}
if (rayDirection.y() != 0) {
int yFix = rayDirection.y() > 0 ? 1 : 0;
double yStep = rayDirection.y() < 0 ? -1 : 1;
int yStepsCompleted = yFix;
int yStepCount = (int) Math.ceil((rayDirection.y()) / yStep) + yFix;
while (yStepsCompleted <= yStepCount) {
int yi = (int) (yStepsCompleted * yStep + rayStart.blockY());
double factor = (yi - rayStart.y()) / rayDirection.y();
if (Math.abs(rayDirection.y() * finalResult.res) - Math.abs(rayStart.y() - (yi)) < -2) break;
int xi = (int) Math.floor(rayDirection.x() * factor + rayStart.x());
if (Math.abs(rayDirection.x() * finalResult.res) - Math.abs(rayStart.x() - (xi)) < -2) break;
int zi = (int) Math.floor(rayDirection.z() * factor + rayStart.z());
if (Math.abs(rayDirection.z() * finalResult.res) - Math.abs(rayStart.z() - (zi)) < -2) break;
yi -= yFix;
yStepsCompleted++;
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, getter, finalResult))
break;
}
}
}
/**
* Check if a bounding box intersects a ray
*
@ -110,12 +14,12 @@ final class RayUtils {
* @return true if an intersection between the ray and the bounding box was found
*/
public static boolean BoundingBoxIntersectionCheck(BoundingBox moving, Point rayStart, Point rayDirection, BoundingBox collidableStatic, Point staticCollidableOffset) {
Point bbCentre = new Pos(moving.minX() + moving.width() / 2, moving.minY() + moving.height() / 2, moving.minZ() + moving.depth() / 2);
Point bbCentre = new Vec(moving.minX() + moving.width() / 2, moving.minY() + moving.height() / 2 + Vec.EPSILON, moving.minZ() + moving.depth() / 2);
Point rayCentre = rayStart.add(bbCentre);
// Translate bounding box
Vec bbOffMin = Vec.Operator.EPSILON.apply(collidableStatic.minX() - rayCentre.x() + staticCollidableOffset.x() - moving.width() / 2, collidableStatic.minY() - rayCentre.y() + staticCollidableOffset.y() - moving.height() / 2, collidableStatic.minZ() - rayCentre.z() + staticCollidableOffset.z() - moving.depth() / 2);
Vec bbOffMax = Vec.Operator.EPSILON.apply(collidableStatic.maxX() - rayCentre.x() + staticCollidableOffset.x() + moving.width() / 2, collidableStatic.maxY() - rayCentre.y() + staticCollidableOffset.y() + moving.height() / 2, collidableStatic.maxZ() - rayCentre.z() + staticCollidableOffset.z() + moving.depth() / 2);
Vec bbOffMin = new Vec(collidableStatic.minX() - rayCentre.x() + staticCollidableOffset.x() - moving.width() / 2, collidableStatic.minY() - rayCentre.y() + staticCollidableOffset.y() - moving.height() / 2, collidableStatic.minZ() - rayCentre.z() + staticCollidableOffset.z() - moving.depth() / 2);
Vec bbOffMax = new Vec(collidableStatic.maxX() - rayCentre.x() + staticCollidableOffset.x() + moving.width() / 2, collidableStatic.maxY() - rayCentre.y() + staticCollidableOffset.y() + moving.height() / 2, collidableStatic.maxZ() - rayCentre.z() + staticCollidableOffset.z() + moving.depth() / 2);
// This check is done in 2d. it can be visualised as a rectangle (the face we are checking), and a point.
// If the point is within the rectangle, we know the vector intersects the face.
@ -127,14 +31,13 @@ final class RayUtils {
// Intersect X
if (rayDirection.x() != 0) {
// Left side of bounding box
{
if (rayDirection.x() > 0) {
double xFac = bbOffMin.x() / rayDirection.x();
double yix = rayDirection.y() * xFac + rayCentre.y();
double zix = rayDirection.z() * xFac + rayCentre.z();
// Check if ray passes through y/z plane
if (rayDirection.x() > 0
&& ((yix - rayCentre.y()) * signumRayY) >= 0
if (((yix - rayCentre.y()) * signumRayY) >= 0
&& ((zix - rayCentre.z()) * signumRayZ) >= 0
&& yix >= collidableStatic.minY() + staticCollidableOffset.y() - moving.height() / 2
&& yix <= collidableStatic.maxY() + staticCollidableOffset.y() + moving.height() / 2
@ -144,13 +47,12 @@ final class RayUtils {
}
}
// Right side of bounding box
{
if (rayDirection.x() < 0) {
double xFac = bbOffMax.x() / rayDirection.x();
double yix = rayDirection.y() * xFac + rayCentre.y();
double zix = rayDirection.z() * xFac + rayCentre.z();
if (rayDirection.x() < 0
&& ((yix - rayCentre.y()) * signumRayY) >= 0
if (((yix - rayCentre.y()) * signumRayY) >= 0
&& ((zix - rayCentre.z()) * signumRayZ) >= 0
&& yix >= collidableStatic.minY() + staticCollidableOffset.y() - moving.height() / 2
&& yix <= collidableStatic.maxY() + staticCollidableOffset.y() + moving.height() / 2
@ -163,13 +65,12 @@ final class RayUtils {
// Intersect Z
if (rayDirection.z() != 0) {
{
if (rayDirection.z() > 0) {
double zFac = bbOffMin.z() / rayDirection.z();
double xiz = rayDirection.x() * zFac + rayCentre.x();
double yiz = rayDirection.y() * zFac + rayCentre.y();
if (rayDirection.z() > 0
&& ((yiz - rayCentre.y()) * signumRayY) >= 0
if (((yiz - rayCentre.y()) * signumRayY) >= 0
&& ((xiz - rayCentre.x()) * signumRayX) >= 0
&& xiz >= collidableStatic.minX() + staticCollidableOffset.x() - moving.width() / 2
&& xiz <= collidableStatic.maxX() + staticCollidableOffset.x() + moving.width() / 2
@ -178,13 +79,12 @@ final class RayUtils {
return true;
}
}
{
if (rayDirection.z() < 0) {
double zFac = bbOffMax.z() / rayDirection.z();
double xiz = rayDirection.x() * zFac + rayCentre.x();
double yiz = rayDirection.y() * zFac + rayCentre.y();
if (rayDirection.z() < 0
&& ((yiz - rayCentre.y()) * signumRayY) >= 0
if (((yiz - rayCentre.y()) * signumRayY) >= 0
&& ((xiz - rayCentre.x()) * signumRayX) >= 0
&& xiz >= collidableStatic.minX() + staticCollidableOffset.x() - moving.width() / 2
&& xiz <= collidableStatic.maxX() + staticCollidableOffset.x() + moving.width() / 2
@ -197,13 +97,12 @@ final class RayUtils {
// Intersect Y
if (rayDirection.y() != 0) {
{
if (rayDirection.y() > 0) {
double yFac = bbOffMin.y() / rayDirection.y();
double xiy = rayDirection.x() * yFac + rayCentre.x();
double ziy = rayDirection.z() * yFac + rayCentre.z();
if (rayDirection.y() > 0
&& ((ziy - rayCentre.z()) * signumRayZ) >= 0
if (((ziy - rayCentre.z()) * signumRayZ) >= 0
&& ((xiy - rayCentre.x()) * signumRayX) >= 0
&& xiy >= collidableStatic.minX() + staticCollidableOffset.x() - moving.width() / 2
&& xiy <= collidableStatic.maxX() + staticCollidableOffset.x() + moving.width() / 2
@ -212,13 +111,12 @@ final class RayUtils {
return true;
}
}
{
if (rayDirection.y() < 0) {
double yFac = bbOffMax.y() / rayDirection.y();
double xiy = rayDirection.x() * yFac + rayCentre.x();
double ziy = rayDirection.z() * yFac + rayCentre.z();
if (rayDirection.y() < 0
&& ((ziy - rayCentre.z()) * signumRayZ) >= 0
if (((ziy - rayCentre.z()) * signumRayZ) >= 0
&& ((xiy - rayCentre.x()) * signumRayX) >= 0
&& xiy >= collidableStatic.minX() + staticCollidableOffset.x() - moving.width() / 2
&& xiy <= collidableStatic.maxX() + staticCollidableOffset.x() + moving.width() / 2

View File

@ -11,6 +11,7 @@ import net.minestom.server.entity.metadata.other.SlimeMeta;
import net.minestom.server.instance.block.Block;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@ -33,6 +34,16 @@ public class EntityBlockPhysicsIntegrationTest {
assertEquals(expected.z(), actual.z(), PRECISION.z());
}
private static void assertPossiblePoints(List<Point> expected, Point actual) {
for (Point point : expected) {
if (checkPoints(point, actual)) {
return;
}
}
fail("Expected one of the following points: " + expected);
}
@Test
public void entityPhysicsCheckCollision(Env env) {
var instance = env.createFlatInstance();
@ -251,13 +262,8 @@ public class EntityBlockPhysicsIntegrationTest {
PhysicsResult res = CollisionUtils.handlePhysics(entity, new Vec(10, 0, 10));
boolean isFirst = checkPoints(new Pos(10, 42, 0.7), res.newPosition());
boolean isSecond = checkPoints(new Pos(0.7, 42, 10), res.newPosition());
// First and second are both valid, it depends on the implementation
// If x collision is checked first then isFirst will be true
// If z collision is checked first then isSecond will be true
assertTrue(isFirst || isSecond);
assertPossiblePoints(List.of(new Pos(10, 42, 0.7), new Pos(0.7, 42, 10)), res.newPosition());
}
@Test
@ -395,13 +401,8 @@ public class EntityBlockPhysicsIntegrationTest {
PhysicsResult res = CollisionUtils.handlePhysics(entity, new Vec(0.702, 0, 0.702));
boolean isFirst = checkPoints(new Pos(1.402, 42, 0.7), res.newPosition());
boolean isSecond = checkPoints(new Pos(0.7, 42, 1.402), res.newPosition());
// First and second are both valid, it depends on the implementation
// If x collision is checked first then isFirst will be true
// If z collision is checked first then isSecond will be true
assertTrue(isFirst || isSecond);
assertPossiblePoints(List.of(new Pos(1.402, 42, 0.7), new Pos(0.7, 42, 1.402)), res.newPosition());
}
@Test
@ -592,10 +593,8 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(1, 42, 0, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(0.7, 42, 0.5)).join();
assertEquals(instance, entity.getInstance());
@ -609,10 +608,8 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(0, 42, 1, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(0.5, 42, 0.7)).join();
assertEquals(instance, entity.getInstance());
@ -626,10 +623,8 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(1, 42, 1, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(0.8, 42, 1.3)).join();
assertEquals(instance, entity.getInstance());
@ -643,10 +638,8 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(0, 42, 0, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(0.7, 42, 1.1)).join();
assertEquals(instance, entity.getInstance());
@ -660,10 +653,8 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(0, 42, 1, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(1.1, 42, 1.3)).join();
assertEquals(instance, entity.getInstance());
@ -677,10 +668,8 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(1, 42, 0, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(1.3, 42, 1.1)).join();
assertEquals(instance, entity.getInstance());
@ -694,10 +683,8 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(0, 42, 0, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(1.1, 42, 0.7)).join();
assertEquals(instance, entity.getInstance());
@ -711,10 +698,8 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(1, 42, 1, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(1.3, 42, 0.8)).join();
assertEquals(instance, entity.getInstance());
@ -729,16 +714,14 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(1, 43, 0, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(0.51, 42.51, 0.5)).join();
assertEquals(instance, entity.getInstance());
PhysicsResult res = CollisionUtils.handlePhysics(entity, new Vec(0.57, 0.57, 0.57));
assertEqualsPoint(new Pos(1.08, 43, 1.07), res.newPosition());
assertPossiblePoints(List.of(new Pos(1.08, 43, 1.07), new Pos(1.0, 43.08, 1.07)), res.newPosition());
}
@Test
@ -746,16 +729,14 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(0, 43, 1, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(0.50, 42.51, 0.51)).join();
assertEquals(instance, entity.getInstance());
PhysicsResult res = CollisionUtils.handlePhysics(entity, new Vec(0.57, 0.57, 0.57));
assertEqualsPoint(new Pos(1.07, 43, 1.08), res.newPosition());
assertPossiblePoints(List.of(new Pos(1.07, 43, 1.08), new Pos(1.07, 43.08, 1.0)), res.newPosition());
}
@Test
@ -763,16 +744,15 @@ public class EntityBlockPhysicsIntegrationTest {
var instance = env.createFlatInstance();
instance.setBlock(1, 43, 1, Block.STONE);
BoundingBox bb = new BoundingBox(0, 0, 0);
var entity = new Entity(EntityType.ZOMBIE);
entity.setBoundingBox(bb);
entity.setBoundingBox(BoundingBox.ZERO);
entity.setInstance(instance, new Pos(0.51, 42.50, 0.51)).join();
assertEquals(instance, entity.getInstance());
PhysicsResult res = CollisionUtils.handlePhysics(entity, new Vec(0.57, 0.57, 0.57));
assertEqualsPoint(new Pos(1.08, 43, 1.08), res.newPosition());
assertPossiblePoints(List.of(new Pos(1.0, 43.08, 1.08), new Pos(1.08, 43.0, 1.08), new Pos(1.08, 43.08, 1.0)), res.newPosition());
}
@Test
@ -827,7 +807,7 @@ public class EntityBlockPhysicsIntegrationTest {
PhysicsResult nz = CollisionUtils.handlePhysics(entity, new Vec(0, 0, -10));
assertEqualsPoint(new Pos(0.7, 42, 0.5), px.newPosition());
assertEqualsPoint(new Pos(0.5, 42.04, 0.5), py.newPosition());
assertEqualsPoint(new Pos(0.5, 42.05, 0.5), py.newPosition());
assertEqualsPoint(new Pos(0.5, 42, 0.7), pz.newPosition());
assertEqualsPoint(new Pos(0.3, 42, 0.5), nx.newPosition());
@ -878,4 +858,36 @@ public class EntityBlockPhysicsIntegrationTest {
PhysicsResult res = CollisionUtils.handlePhysics(entity, Vec.ZERO);
assertEqualsPoint(new Pos(5, 42, 5), res.newPosition());
}
@Test
public void entityPhysicsRepeatedCollision(Env env) {
var instance = env.createFlatInstance();
PhysicsResult previousResult = null;
instance.setBlock(0, 41, 0, Block.STONE);
instance.setBlock(1, 42, 0, Block.STONE);
instance.setBlock(0, 42, 1, Block.STONE);
instance.setBlock(0, 42, -1, Block.STONE);
instance.setBlock(-1, 42, 0, Block.STONE);
instance.setBlock(1, 43, 0, Block.STONE);
instance.setBlock(0, 43, 1, Block.STONE);
instance.setBlock(0, 43, -1, Block.STONE);
instance.setBlock(-1, 43, 0, Block.STONE);
var entity = new Entity(EntityType.ZOMBIE);
entity.setInstance(instance, new Pos(0.5, 43.1, 0.5)).join();
PhysicsResult res = CollisionUtils.handlePhysics(entity, new Vec(0, 0, 0));
entity.teleport(res.newPosition()).join();
while ((previousResult == null || !previousResult.newPosition().samePoint(res.newPosition())) && entity.getPosition().y() >= 42) {
previousResult = res;
res = CollisionUtils.handlePhysics(entity, new Vec(0.1, -0.01, 0));
entity.teleport(res.newPosition()).join();
}
assertEqualsPoint(new Pos(0.7, 42, 0.5), res.newPosition());
}
}

View File

@ -1,23 +0,0 @@
package net.minestom.server.collision;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class RayUtilsTest {
@Test
void manyHeightIntersectionChecks() {
final Pos rayStart = new Pos(0.5, 1.0, 0.5);
final Vec rayDirection = new Vec(0.273, -0.0784, 0.0);
final BoundingBox collidableStatic = new BoundingBox(1, 1, 1);
final Vec staticCollidableOffset = new Vec(1, 0.0, 0.0);
for(double y = 1; y < 10; y += 0.001D) {
final BoundingBox moving = new BoundingBox(0.6, y, 0.6);
assertTrue(RayUtils.BoundingBoxIntersectionCheck(moving, rayStart, rayDirection, collidableStatic,
staticCollidableOffset), moving.toString());
}
}
}

View File

@ -106,7 +106,7 @@ public class EntityVelocityIntegrationTest {
var player = env.createPlayer(instance, new Pos(0, 42, 0));
env.tick();
final double epsilon = 0.0000001;
final double epsilon = 0.00001;
assertEquals(player.getVelocity().y(), -1.568, epsilon);
double previousVelocity = player.getVelocity().y();