Skip to content
This repository was archived by the owner on Nov 19, 2018. It is now read-only.

Entity Movement And Physics

NSDex edited this page Jul 9, 2016 · 7 revisions

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. To describe it well, I will need to disclose more of the actual Minecraft code structure than usual. Method and variable names will be changed where practical.

Physics

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

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.

  1. 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;
}
  1. 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.

For a player entity, the only type of entity that will be found by this query are Boats. Although the player can collide with many other entities, a Boat is the only one that inhibits movement. Collision with entities that can be pushed are handled later (after the pushing entity has been moved). However, a Boat or Minecart colliding with another entity will inhibit its movement. Therefore, the query for entities potentially colliding with a Boat or Minecart will return the bounding boxes of all entities within the query bounding box.

  1. 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
}

Clone this wiki locally