mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-14 20:21:53 +01:00
Entity Physics are behaving a bit better
Still possible to go through walls though
This commit is contained in:
parent
69e7496fbb
commit
1aefbca70a
@ -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 -> {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user