Last active
November 22, 2022 21:35
-
-
Save iam4722202468/b8e94545d8f32a4a5839c5e33af5f0ea to your computer and use it in GitHub Desktop.
improved entity -> entity collisions
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
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