codehearts/pickles-fetch-quest

View on GitHub
engine/collision/collision_resolution_physical.py

Summary

Maintainability
A
0 mins
Test Coverage
from .collision_resolution_1d import get_nonoverlapping_coordinate_1d
from engine import geometry


def resolve_physical_collision(first, second):
    """Resolves a collision between two physical objects by repositioning one.

    Objects with the lowest velocity on a given axis will be repositioned
    along that axis to resolve the collision.

    Args:
        first (:obj:`engine.game_object.PhysicalGameObject`):
            The first physical game object in the collision.
        second (:obj:`engine.game_object.PhysicalGameObject`):
            The second physical game object in the collision.

    Returns:
        A tuple of ints for the change in velocity along each axis.
    """
    # Objects are not overlapping
    if not geometry.detect_overlap_2d(first, second):
        return (0, 0)

    # Move the lighter object, leave the heavier object resting
    first_is_heavier = first.mass > second.mass
    moving, resting = (second, first) if first_is_heavier else (first, second)

    x_delta = resolve_game_object_x_collision(moving, resting)
    y_delta = resolve_game_object_y_collision(moving, resting)

    return (x_delta, y_delta)


def resolve_game_object_x_collision(moving, static):
    """Resolves a collision by moving an object along the x axis.

    Args:
        moving (:obj:`engine.game_object.PhysicalGameObject`):
            The object to move along the x axis.
        static (:obj:`engine.game_object.PhysicalGameObject`):
            The object to leave as-is.

    Returns:
        The change in the velocity of the object along the x axis.
    """
    # Detect overlap before applying velocity along y axis, but after x axis
    previous_y = moving.y - moving.velocity.y
    has_overlap = geometry.detect_overlap_1d(
        previous_y, moving.height, static.y, static.height)

    if has_overlap:
        # Overlap detected along y-axis, resolve collision on x-axis
        return _resolve_game_object_axis_collision(moving, static, 'x')
    return 0


def resolve_game_object_y_collision(moving, static):
    """Resolves a collision by moving an object along the y axis.

    Args:
        moving (:obj:`engine.game_object.PhysicalGameObject`):
            The object to move along the y axis.
        static (:obj:`engine.game_object.PhysicalGameObject`):
            The object to leave as-is.

    Returns:
        The change in the velocity of the object along the y axis.
    """
    has_overlap = geometry.detect_overlap_1d(
        moving.x, moving.width, static.x, static.width)

    if has_overlap:
        # Overlap detected along x-axis, resolve collision on y-axis
        return _resolve_game_object_axis_collision(moving, static, 'y')
    return 0


def _resolve_game_object_axis_collision(moving, static, axis):
    """Resolves a collision by moving an object along the specified axis.

    Args:
        moving (:obj:`engine.game_object.PhysicalGameObject`):
            The object to move along the axis.
        static (:obj:`engine.game_object.PhysicalGameObject`):
            The object to leave as-is.
        axis (str): Either 'x' or 'y'.

    Returns:
        The delta between the original velocity and the resolved velocity.
    """
    moving_length = moving.width if axis == 'x' else moving.height
    static_length = static.width if axis == 'x' else static.height

    original_moving_position = getattr(moving, axis)
    original_velocity = getattr(moving.velocity, axis)

    # Get a new coordinate for the moving object to resolve the collision
    resolved_coordinate = get_nonoverlapping_coordinate_1d(
        original_moving_position,
        moving_length,
        original_velocity,
        getattr(static, axis),
        static_length)

    # Cancel velocity/acceleration and update coordinate of moving object
    if abs(resolved_coordinate - original_moving_position) != 0:
        setattr(moving.acceleration, axis, 0)
        setattr(moving.velocity, axis, 0)
        setattr(moving, axis, resolved_coordinate)

    return abs(original_velocity - getattr(moving.velocity, axis))