Skip to content

Instantly share code, notes, and snippets.

@iam4722202468
Last active November 22, 2022 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iam4722202468/b8e94545d8f32a4a5839c5e33af5f0ea to your computer and use it in GitHub Desktop.
Save iam4722202468/b8e94545d8f32a4a5839c5e33af5f0ea to your computer and use it in GitHub Desktop.
improved entity -> entity collisions
package net.minestom.server.collision;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.PlayerExtended;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.tag.Tag;
import net.worldseed.events.CollideEvent;
import java.util.Arrays;
import java.util.Collection;
public class EntityCollisionUtils {
final static Vec power = new Vec(0.05, 0.05, 0.05);
final static boolean[] isCollidable = new boolean[EntityType.values().size()];
static {
Arrays.fill(isCollidable, true);
isCollidable[EntityType.ITEM.id()] = false;
isCollidable[EntityType.ITEM_FRAME.id()] = false;
isCollidable[EntityType.PAINTING.id()] = false;
isCollidable[EntityType.GLOW_ITEM_FRAME.id()] = false;
isCollidable[EntityType.VILLAGER.id()] = false;
isCollidable[EntityType.LLAMA_SPIT.id()] = false;
isCollidable[EntityType.EXPERIENCE_ORB.id()] = false;
isCollidable[EntityType.PAINTING.id()] = false;
isCollidable[EntityType.ARMOR_STAND.id()] = false;
isCollidable[EntityType.END_CRYSTAL.id()] = false;
isCollidable[EntityType.AREA_EFFECT_CLOUD.id()] = false;
isCollidable[EntityType.LIGHTNING_BOLT.id()] = false;
isCollidable[EntityType.ARROW.id()] = false;
isCollidable[EntityType.SPECTRAL_ARROW.id()] = false;
isCollidable[EntityType.SHULKER_BULLET.id()] = false;
isCollidable[EntityType.SNOWBALL.id()] = false;
isCollidable[EntityType.FIREBALL.id()] = false;
isCollidable[EntityType.DRAGON_FIREBALL.id()] = false;
isCollidable[EntityType.SMALL_FIREBALL.id()] = false;
isCollidable[EntityType.EGG.id()] = false;
isCollidable[EntityType.TNT.id()] = false;
isCollidable[EntityType.ENDER_PEARL.id()] = false;
isCollidable[EntityType.EYE_OF_ENDER.id()] = false;
isCollidable[EntityType.FALLING_BLOCK.id()] = false;
isCollidable[EntityType.FISHING_BOBBER.id()] = false;
}
public static Vec calculateEntityCollisions(Entity entity) {
BoundingBox bb = entity.getBoundingBox();
double bbFurthestCorner = Math.sqrt(bb.depth() + bb.height() + bb.width());
if (!isCollidable[entity.getEntityType().id()]
|| entity.hasTag(Tag.String("WSEE"))
|| !entity.isHasCollision())
return Vec.ZERO;
if (entity instanceof PlayerExtended player) if (player.getGameMode() == GameMode.SPECTATOR) return Vec.ZERO;
if (entity.getInstance() == null) return Vec.ZERO;
int[] vecAcc = new int[] {0, 0, 0};
Collection<Entity> foundNearby = entity.getInstance().getNearbyEntities(entity.getPosition(), bbFurthestCorner)
.stream()
.filter(e ->
isCollidable[e.getEntityType().id()] &&
!e.hasTag(Tag.String("WSEE")) &&
e.isHasCollision()
).toList();
for (Entity nearby : foundNearby) {
if (nearby == entity) continue;
if (nearby instanceof PlayerExtended player && player.getGameMode() == GameMode.SPECTATOR) return Vec.ZERO;
BoundingBox collisionCheckBB = nearby.getBoundingBox();
if (collisionCheckBB.intersectBox(nearby.getPosition().sub(entity.getPosition()), bb)) {
CollideEvent event = new CollideEvent(entity, nearby);
MinecraftServer.getGlobalEventHandler().call(event);
// Find shortest resolution to collision by calculating the two faces with the shortest distance
// Only solve collision for X and Z. Y doesn't matter because gravity
// double overlapX, overlapZ;
double currentDistanceX, currentDistanceZ;
// X
{
// Nearby left of entity
currentDistanceX = entity.getPosition().x() - nearby.getPosition().x();
// // Min distance without overlap
// double minDistance = collisionCheckBB.width() / 2 + bb.width() / 2;
// // Could be used to calculate how much of a movement to make
// overlapX = minDistance - Math.abs(currentDistanceX);
}
// If y is implemented, min distance calculation isn't h1 / 2 + h2 / 2, because entity position is from bottom of bounding box, not centre
// Z
{
// Nearby left of entity
currentDistanceZ = entity.getPosition().z() - nearby.getPosition().z();
// // Min distance without overlap
// double minDistance = collisionCheckBB.depth() / 2 + bb.depth() / 2;
// // Could be used to calculate how much of a movement to make
// overlapZ = minDistance - Math.abs(currentDistanceZ);
}
if (Math.abs(currentDistanceX) > Math.abs(currentDistanceZ)) {
vecAcc[0] += (currentDistanceX > 0 ? 1 : -1);
} else {
vecAcc[2] += (currentDistanceX > 0 ? 1 : -1);
}
}
}
if (vecAcc[0] == 0 && vecAcc[2] == 0) return Vec.ZERO;
return new Vec(vecAcc[0], vecAcc[1], vecAcc[2]).normalize().mul(power);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment