Created
January 10, 2024 20:02
-
-
Save iam4722202468/dc6933a4bbfa2b414091b52c6c875a77 to your computer and use it in GitHub Desktop.
entity collision utils
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_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; | |
isCollidable[EntityType.ITEM_DISPLAY.id()] = false; | |
isCollidable[EntityType.INTERACTION.id()] = false; | |
} | |
private final static Tag<String> wseeTag = Tag.String("WSEE"); | |
public static Vec calculateEntityCollisions(Entity entity) { | |
if (!isCollidable[entity.getEntityType().id()] | |
|| entity.getEntityType() == EntityType.GLOW_ITEM_FRAME | |
|| entity.hasTag(wseeTag) | |
|| entity instanceof Player | |
|| (entity instanceof LivingEntity l && l.isDead()) | |
|| entity.isRemoved() | |
|| !entity.hasCollision()) | |
return Vec.ZERO; | |
if (entity instanceof PlayerExtended player) if (player.getGameMode() == GameMode.SPECTATOR) return Vec.ZERO; | |
if (entity.getInstance() == null) return Vec.ZERO; | |
BoundingBox bb = entity.getBoundingBox(); | |
double bbFurthestCorner = Math.sqrt(bb.depth() + bb.height() + bb.width()); | |
Stream<Entity> foundNearby = entity.getInstance().getNearbyEntities(entity.getPosition(), bbFurthestCorner) | |
.filter(e -> | |
isCollidable[e.getEntityType().id()] && | |
e.hasCollision() && | |
!e.isRemoved() | |
); | |
int[] vecAcc = foundNearby.reduce(new int[3], (acc, nearby) -> { | |
if (nearby == entity) return acc; | |
if (nearby instanceof PlayerExtended player && player.getGameMode() == GameMode.SPECTATOR) return acc; | |
BoundingBox collisionCheckBB = nearby.getBoundingBox(); | |
if (collisionCheckBB.intersectBox(nearby.getPosition().sub(entity.getPosition()), bb)) { | |
CollideEvent event = new CollideEvent(entity, nearby); | |
MinecraftServer.getGlobalEventHandler().call(event); | |
double collisionWeight = entity.collisionWeight(); | |
if (collisionWeight == 0) return acc; | |
// 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)) { | |
acc[0] += (currentDistanceX > 0 ? 1 : -1) * collisionWeight; | |
} else { | |
acc[2] += (currentDistanceZ > 0 ? 1 : -1) * collisionWeight; | |
} | |
} | |
return acc; | |
}, (a, b) -> { | |
a[0] += b[0]; | |
a[1] += b[1]; | |
a[2] += b[2]; | |
return a; | |
}); | |
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