Entity Physics are behaving a bit better

Still possible to go through walls though
This commit is contained in:
jglrxavpok 2020-05-02 23:34:09 +02:00
parent 69e7496fbb
commit 1aefbca70a
7 changed files with 148 additions and 97 deletions

View File

@ -92,13 +92,13 @@ public class PlayerInit {
creature.damage(DamageType.fromPlayer(player), -1);
Vector velocity = player.getPosition().clone().getDirection().multiply(6);
velocity.setY(4f);
entity.setVelocity(velocity, 150);
entity.setVelocity(velocity);
player.sendMessage("You attacked an entity!");
} else if (entity instanceof Player) {
Player target = (Player) entity;
Vector velocity = player.getPosition().clone().getDirection().multiply(4);
velocity.setY(3.5f);
target.setVelocity(velocity, 150);
target.setVelocity(velocity);
target.damage(DamageType.fromPlayer(player), 5);
player.sendMessage("ATTACK");
}
@ -143,7 +143,7 @@ public class PlayerInit {
itemEntity.refreshPosition(player.getPosition().clone().add(0, 1.5f, 0));
itemEntity.setInstance(player.getInstance());
Vector velocity = player.getPosition().clone().getDirection().multiply(6);
itemEntity.setVelocity(velocity, 500);
itemEntity.setVelocity(velocity);
});
player.addEventCallback(PlayerDisconnectEvent.class, event -> {

View File

@ -41,7 +41,7 @@ public class ChickenCreature extends EntityCreature {
boolean unmount = vehicleInformation.shouldUnmount();
if (jump && isOnGround()) {
setVelocity(new Vector(0, 6, 0), 500);
setVelocity(new Vector(0, 6, 0));
}
boolean updateView = forward > 0;

View File

@ -8,6 +8,7 @@ import net.minestom.server.utils.Position;
* See https://wiki.vg/Entity_metadata#Mobs_2
*/
public class BoundingBox {
// TODO:
private Entity entity;
private float x, y, z;
@ -68,15 +69,15 @@ public class BoundingBox {
return new BoundingBox(entity, this.x - x, this.y - y, this.z - z);
}
public float getX() {
public float getWidth() {
return x;
}
public float getY() {
public float getHeight() {
return y;
}
public float getZ() {
public float getDepth() {
return z;
}

View File

@ -1,40 +1,98 @@
package net.minestom.server.collision;
import net.minestom.server.entity.Entity;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;
public class CollisionUtils {
public static Position entity(Instance instance, BoundingBox boundingBox, Position currentPosition, Position targetPosition) {
/**
* Moves an entity with physics applied (ie checking against blocks)
* @param entity the entity to move
* @param positionOut the Position object in which the new position will be saved
* @param velocityOut the Vector object in which the new velocity will be saved
* @return whether this entity is on the ground
*/
public static boolean handlePhysics(Entity entity, Vector deltaPosition, Position positionOut, Vector velocityOut) {
Instance instance = entity.getInstance();
Position currentPosition = entity.getPosition();
BoundingBox boundingBox = entity.getBoundingBox();
float currentX = currentPosition.getX();
float currentY = currentPosition.getY();
float currentZ = currentPosition.getZ();
float targetX = targetPosition.getX();
float targetY = targetPosition.getY();
float targetZ = targetPosition.getZ();
// target_WithBB is the target_ with the length in the _ direction of the bounding box added. Used to determinate block intersections
// step Y
float targetY = entity.getPosition().getY() + deltaPosition.getY();
float targetYWithBB = targetY;
if(deltaPosition.getY() > 0) {
targetYWithBB += boundingBox.getHeight();
}
BlockPosition yBlock = new BlockPosition(currentX, (int) targetYWithBB, currentZ);
boolean yAir = !Block.fromId(instance.getBlockId(yBlock)).isSolid();
boolean yIntersect = boundingBox.intersect(yBlock);
BlockPosition xBlock = new BlockPosition(targetX, (int) currentY, currentZ);
BlockPosition yBlock = new BlockPosition(currentX, (int) targetY, currentZ);
BlockPosition zBlock = new BlockPosition(currentX, (int) currentY, targetZ);
boolean yCollision = true;
if(yAir || !yIntersect)
yCollision = false;
float newY = yCollision ? currentY : targetY;
if(yCollision) {
if(deltaPosition.getY() < 0) {
newY = (float) (Math.ceil(newY)+0.01f); // TODO: custom block bounding boxes
} else if(deltaPosition.getY() > 0) {
newY = (float) (Math.floor(newY)-0.01f); // TODO: custom block bounding boxes
}
}
// step X/Z
float targetX = entity.getPosition().getX() + deltaPosition.getX();
float targetXWithBB = targetX+Math.signum(deltaPosition.getX()) * boundingBox.getWidth()/2f;
float targetZ = entity.getPosition().getZ() + deltaPosition.getZ();
float targetZWithBB = targetZ+Math.signum(deltaPosition.getZ()) * boundingBox.getDepth()/2f;
BlockPosition xBlock = new BlockPosition(targetXWithBB, (int) newY, currentZ);
BlockPosition zBlock = new BlockPosition(currentX, (int) newY, targetZWithBB);
boolean xAir = !Block.fromId(instance.getBlockId(xBlock)).isSolid();
boolean yAir = !Block.fromId(instance.getBlockId(yBlock)).isSolid();
boolean zAir = !Block.fromId(instance.getBlockId(zBlock)).isSolid();
boolean xIntersect = boundingBox.intersect(xBlock);
boolean yIntersect = boundingBox.intersect(yBlock);
boolean zIntersect = boundingBox.intersect(zBlock);
float newX = xAir ? targetX : xIntersect ? currentX : targetX;
float newY = yAir ? targetY : yIntersect ? currentY : targetY;
float newZ = zAir ? targetZ : zIntersect ? currentZ : targetZ;
boolean xCollision = true;
if(xAir || !xIntersect)
xCollision = false;
boolean zCollision = true;
if(zAir || !zIntersect)
zCollision = false;
float newX = xCollision ? currentX : targetX;
float newZ = zCollision ? currentZ : targetZ;
return new Position(newX, newY, newZ);
velocityOut.copy(entity.getVelocity());
if(xCollision) {
velocityOut.setX(0f);
}
if(yCollision) {
velocityOut.setY(0f);
}
if(zCollision) {
velocityOut.setZ(0f);
}
positionOut.setX(newX);
positionOut.setY(newY);
positionOut.setZ(newZ);
return yCollision && deltaPosition.getY() < 0;
}
}

View File

@ -65,7 +65,7 @@ public abstract class Entity implements Viewable, DataContainer {
private int entityType;
private long lastUpdate;
private Map<Class<? extends Event>, List<EventCallback>> eventCallbacks = new ConcurrentHashMap<>();
protected long velocityTime; // Reset velocity to 0 after countdown
protected long lastVelocityUpdateTime; // Reset velocity to 0 after countdown
// Synchronization
private long synchronizationDelay = 1500; // In ms
@ -86,6 +86,8 @@ public abstract class Entity implements Viewable, DataContainer {
protected boolean silent;
protected boolean noGravity;
protected Pose pose = Pose.STANDING;
private long velocityUpdatePeriod;
protected boolean onGround;
public Entity(int entityType, Position spawnPosition) {
this.id = generateId();
@ -96,6 +98,7 @@ public abstract class Entity implements Viewable, DataContainer {
setBoundingBox(0, 0, 0);
entityById.put(id, this);
setVelocityUpdatePeriod(5);
}
public Entity(int entityType) {
@ -117,7 +120,32 @@ public abstract class Entity implements Viewable, DataContainer {
public abstract void spawn();
public boolean isOnGround() {
return EntityUtils.isOnGround(this);
return onGround || EntityUtils.isOnGround(this) /* backup for levitating entities */;
}
/**
* Checks if now is a good time to send a velocity update packet
* @return
* @param time
*/
protected boolean shouldSendVelocityUpdate(long time) {
return (time-lastVelocityUpdateTime) >= velocityUpdatePeriod;
}
/**
* Gets the period, in ms, between two velocity update packets
* @return period, in ms, between two velocity update packets
*/
public long getVelocityUpdatePeriod() {
return velocityUpdatePeriod;
}
/**
* Sets the period, in ms, between two velocity update packets
* @param velocityUpdatePeriod period, in ms, between two velocity update packets
*/
public void setVelocityUpdatePeriod(long velocityUpdatePeriod) {
this.velocityUpdatePeriod = velocityUpdatePeriod;
}
public void teleport(Position position, Runnable callback) {
@ -209,74 +237,43 @@ public abstract class Entity implements Viewable, DataContainer {
this.lastUpdate = time;
// Velocity
if (velocityTime != 0) {
if (this instanceof Player) {
sendPacketToViewersAndSelf(getVelocityPacket());
if (!(this instanceof Player)) {
final float tps = MinecraftServer.TICK_PER_SECOND;
float newX = position.getX() + velocity.getX() / tps;
float newY = position.getY() + velocity.getY() / tps;
float newZ = position.getZ() + velocity.getZ() / tps;
Position newPosition = new Position(newX, newY, newZ);
if (!(this instanceof Player) && !noGravity) { // players handle gravity by themselves
velocity.setY(velocity.getY() - gravityDragPerTick*tps);
}
Position newPositionOut = new Position();
Vector newVelocityOut = new Vector();
Vector deltaPos = new Vector(
getVelocity().getX() / tps,
getVelocity().getY() / tps,
getVelocity().getZ() / tps
);
onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
refreshPosition(newPosition);
velocity.copy(newVelocityOut);
float drag;
if(onGround) {
drag = 0.5f; // ground drag
} else {
final float tps = MinecraftServer.TICK_PER_SECOND;
float newX = position.getX() + velocity.getX() / tps;
float newY = position.getY() + velocity.getY() / tps;
float newZ = position.getZ() + velocity.getZ() / tps;
Position newPosition = new Position(newX, newY, newZ);
newPosition = CollisionUtils.entity(getInstance(), getBoundingBox(), getPosition(), newPosition);
refreshPosition(newPosition);
if (this instanceof ObjectEntity) {
// FIXME velocity/gravity
//sendPacketToViewers(getVelocityPacket());
teleport(getPosition());
} else if (this instanceof EntityCreature) {
teleport(getPosition());
}
drag = 0.98f; // air drag
}
if (time >= velocityTime) {
sendSynchronization(); // Send synchronization after velocity ended
resetVelocity();
}
}
velocity.multiply(drag);
// Gravity
if (!(this instanceof Player) && !noGravity && gravityDragPerTick != 0) { // Players do manage gravity client-side
Position position = getPosition();
//System.out.println("on ground: "+isOnGround());
if (!isOnGround()) {
float strength = gravityDragPerTick * gravityTickCounter;
boolean foundBlock = false;
int firstBlock = 0;
for (int y = (int) position.getY(); y > 0; y--) {
BlockPosition blockPosition = new BlockPosition(position.getX(), y, position.getZ());
short blockId = instance.getBlockId(blockPosition);
//if (y == 70)
// System.out.println("id: " + blockId);
if (blockId != 0) {
firstBlock = y;
foundBlock = true;
//System.out.println("first block: " + y);
break;
}
}
float newY = position.getY() - strength;
//System.out.println("REFRESH Y " + newY);
// allow entities to go below the world
if (foundBlock) {
newY = Math.max(newY, firstBlock);
}
refreshPosition(position.getX(), newY, position.getZ());
gravityTickCounter++;
if (isOnGround()) { // Round Y axis when gravity movement is done
//System.out.println("ROUND " + position.getY());
refreshPosition(position.getX(), Math.round(position.getY()), position.getZ());
gravityTickCounter = 0;
// System.out.println("recheck: " + isOnGround() + " : " + getPosition());
}
if (this instanceof EntityCreature) {
teleport(getPosition());
}
sendSynchronization();
if (shouldSendVelocityUpdate(time)) {
sendPacketToViewers(getVelocityPacket());
lastVelocityUpdateTime = time;
}
}
@ -377,14 +374,8 @@ public abstract class Entity implements Viewable, DataContainer {
return velocity;
}
public void setVelocity(Vector velocity, long velocityTime) {
this.velocity = velocity;
this.velocityTime = System.currentTimeMillis() + velocityTime;
}
public void resetVelocity() {
this.velocity = new Vector();
this.velocityTime = 0;
public void setVelocity(Vector velocity) {
this.velocity.copy(velocity);
}
public void setGravity(float gravityDragPerTick) {

View File

@ -49,9 +49,9 @@ public abstract class EntityCreature extends LivingEntity {
float newX = position.getX() + x;
float newY = position.getY() + y;
float newZ = position.getZ() + z;
Position newPosition = new Position(newX, newY, newZ);
Position newPosition = new Position();
// Calculate collisions boxes
newPosition = CollisionUtils.entity(getInstance(), getBoundingBox(), position, newPosition);
onGround = CollisionUtils.handlePhysics(this, new Vector(x, y, z), newPosition, new Vector());
// Refresh target position
newX = newPosition.getX();
newY = newPosition.getY();
@ -133,7 +133,7 @@ public abstract class EntityCreature extends LivingEntity {
public void jump(float height) {
// FIXME magic value
Vector velocity = new Vector(0, height * 10, 0);
setVelocity(velocity, 200);
setVelocity(velocity);
}
public void setPathTo(Position position, int maxCheck, Consumer<Boolean> resultConsumer) {

View File

@ -35,6 +35,7 @@ public class EntityUtils {
Position entityPosition = entity.getPosition();
// TODO: check entire bounding box
BlockPosition blockPosition = entityPosition.toBlockPosition();
blockPosition = blockPosition.subtract(0, 1, 0);
try {