-
Notifications
You must be signed in to change notification settings - Fork 320
Entity Movement And Physics
Analysis is based on the decompiled source of the vanilla b1.7.3 client.
Note: Minecraft Entity movement and physics code is a tangled mess. I will need to refer to specific method/variable in order to explain it well. Some of these names are the original MCP names (because I couldn't think of a different one that conveys the same meaning), others I have changed.
Minecraft lacks a discreet physics system. Gravity, collision, and translation of input into movement are often handled in the same methods as non-physics entity updates such as dealing fire damage when inside of a burning block.
Collision detection and resolution is primarily handled in the Entity::moveEntity method. Given the entity's current bounding box and a proposed deltaX/Y/Z (translation) for said bounding box, this method determines what blocks or entities exist in the in-between space and modifies the deltaX/Y/Z to avoid a collision. It then translates the entity's current bounding box by the (potentially modified) deltaX/Y/Z thereby moving the entity to its new position.
- Create a copy of the entity's current bounding box. Extend this bounding box by the proposed deltaX/Y/Z using the implementation shown below:
BoundingBox::extend(dx, dy, dz)
{
if dx < 0.0 then
minimumX := minimumX + dx;
if dx > 0.0 then
maximumX := maximumX + dx;
if dy < 0.0 then
minimumY := minimumY + dy;
if dy > 0.0 then
maximumY := maximumY + dy;
if dz < 0.0 then
minimumZ := minimumZ + dz;
if dz > 0.0 then
maximumZ := maximumZ + dz;
}
-
Collect all potentially colliding bounding boxes from blocks or entities within the extended bounding box from [1]. When searching the world for potentially colliding entities, Notch temporarily expands the query bounding box (from [1]) by 0.25 in all directions. In Minecraft, entities can have both a bounding box and a collision bounding box, though the latter appears to only be used for resolving some implementation issues with Boats and Minecarts. Both of these bounding boxes checked for intersection with the query bounding box.
-
Iterate over all the bounding boxes from the previous step. For each bounding box, determine whether its distance to the bounding box from [1] along the Y-axis is less than the proposed deltaY using the implementation shown below. If the computed distance is less than the proposed deltaY, reduce deltaY to the computed distance. At the end of this step, deltaY should be equal to the distance to the nearest bound box from the previous step along the Y-axis. Offset the entities bounding box (not the copy) by the final deltaY. Repeat this step for the X-axis and Z-axis in that order.
// NearbyBoundingBox is the bounding box from the current iteration
// QueryBoundingBox is the bounding box from [1].
BoundingBox::calculateYOffset(NearbyBoundingBox, QueryBoundingBox, deltaY)
{
// Bail out if not within the same X/Z plane.
if QueryBoundingBox.maxX <= NearbyBoundingBox.minX or QueryBoundingBox.minX >= NearbyBoundingBox.maxX then
return deltaY
if QueryBoundingBox.maxZ <= NearbyBoundingBox.minZ or QueryBoundingBox.minZ >= NearbyBoundingBox.maxZ then
return deltaY
// The entity is moving UP and is currently below NearbyBoundingBox.
if deltaY > 0 and QueryBoundingBox.maxY <= NearbyBoundingBox.minY then
{
let difference := NearbyBoundingBox.minY - QueryBoundingBox.maxY
if difference < deltaY then
deltaY := difference
}
// The entity is moving DOWN and is currently above NearbyBoundingBox.
if deltaY < 0 and QueryBoundingBox.minY >= NearbyBoundingBox.maxY then
{
let difference := NearbyBoundingBox.maxY - QueryBoundingBox.minY
if difference > deltaY then
deltaY := difference
}
return deltaY
}