mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-06 16:37:38 +01:00
Refactor physics to simplify code (#1315)
This commit is contained in:
parent
f62d4e47fc
commit
4a79a3af26
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user