labyrinth/source/system/collision.cpp
void
    resolve(Component::Position const& rPosition,
            Component::Velocity& rVelocity,
            Component::Collider const& rCollider,
            Component::Position const& rGridPosition,
            Component::GridCollider const& rGridCollider)
    {
        auto const signZero = [](int x)
        {
            return x == 0 ? 0 : (x < 0 ? -1 : 1);
        };

        int dTile[2];
        dTile[0] = signZero(rVelocity.m_delta[0]);
        dTile[1] = signZero(rVelocity.m_delta[1]);

        // Find a coordinate of the tile the object reaches next (based on a coordinate)
        auto const coordinateToTileCoordinate = [&](int coordinate, size_t index)
        {
            return (coordinate - rGridPosition.m_value[index] + dTile[index] * (rGridCollider.m_cellDim[index] / 2 - 1)) / rGridCollider.m_cellDim[index];
        };

        // Find the coodinate the object is trying to reach (based on a tile coordinate)
        auto const tileCoordinateToTargetCoordinate = [&](int tileCoordinate, size_t index)
        {
            return tileCoordinate * rGridCollider.m_cellDim[index] + rGridCollider.m_cellDim[index] / 2 - dTile[index] * (rCollider.m_dim[index] / 2 - rGridCollider.m_cellDim[index] / 2) + rGridPosition.m_value[index];
        };

        // Find the tile the object reaches next
        int tile[2];
        tile[0] = coordinateToTileCoordinate(rPosition.m_value[0], 0);
        tile[1] = coordinateToTileCoordinate(rPosition.m_value[1], 1);

        // If only moving on x-axis
        if (dTile[1] == 0)
        {
            rVelocity.m_delta[0] = signZero(rVelocity.m_delta[0]) * std::min(std::abs(rVelocity.m_delta[0]),
                                                                             std::abs(tileCoordinateToTargetCoordinate(tile[0], 0) - rPosition.m_value[0]));
            rVelocity.m_lastBlockedIndex = 0;
            return;
        }

        // If only moving on y-axis
        if (dTile[0] == 0)
        {
            rVelocity.m_delta[1] = signZero(rVelocity.m_delta[1]) * std::min(std::abs(rVelocity.m_delta[1]),
                                                                             std::abs(tileCoordinateToTargetCoordinate(tile[1], 1) - rPosition.m_value[1]));
            rVelocity.m_lastBlockedIndex = 1;
            return;
        }

        // First try to move into the direction that was blocked last
        auto changeTileIndex = rVelocity.m_lastBlockedIndex;
        auto remainTileIndex = (rVelocity.m_lastBlockedIndex + 1) & 1;

        int target[2];
        target[changeTileIndex] = tileCoordinateToTargetCoordinate(tile[changeTileIndex] + dTile[changeTileIndex], changeTileIndex);
        target[remainTileIndex] = tileCoordinateToTargetCoordinate(tile[remainTileIndex], remainTileIndex);

        auto const lastBlockedDirectionStillBlocked = detectCollision(rGridCollider,
                                                                      rCollider,
                                                                      rGridPosition.m_value[0],
                                                                      rGridPosition.m_value[1],
                                                                      target[0],
                                                                      target[1]);

        // If still blocked, try to move into the other direction
        if (lastBlockedDirectionStillBlocked)
        {
            changeTileIndex = remainTileIndex;
            remainTileIndex = rVelocity.m_lastBlockedIndex;

            target[changeTileIndex] = tileCoordinateToTargetCoordinate(tile[changeTileIndex] + dTile[changeTileIndex], changeTileIndex);
            target[remainTileIndex] = tileCoordinateToTargetCoordinate(tile[remainTileIndex], remainTileIndex);

            // If that direction is blocked as well, just move up to the wall
            if (detectCollision(rGridCollider,
                                rCollider,
                                rGridPosition.m_value[0],
                                rGridPosition.m_value[1],
                                target[0],
                                target[1]))
            {
                target[0] = tileCoordinateToTargetCoordinate(tile[0], 0);
                target[1] = tileCoordinateToTargetCoordinate(tile[1], 1);
            }
        }

        // The actual movement: first move on the axis where the tile does not change, then move the rest of the way on the other axis
        auto const norm = Plight::sqrt(rVelocity.m_delta[0] * rVelocity.m_delta[0] + rVelocity.m_delta[1] * rVelocity.m_delta[1]);
        auto const remainTileDiff = target[remainTileIndex] - rPosition.m_value[remainTileIndex];
        auto const changeTileDiff = target[changeTileIndex] - rPosition.m_value[changeTileIndex];

        rVelocity.m_delta[remainTileIndex] = signZero(remainTileDiff) * std::min(std::abs(remainTileDiff), norm);
        rVelocity.m_delta[changeTileIndex] = signZero(changeTileDiff) * std::min(std::abs(changeTileDiff), Plight::sqrt(norm * norm - rVelocity.m_delta[remainTileIndex] * rVelocity.m_delta[remainTileIndex]));

        // If the object actually moved in the chosen direction, mark the other direction as last blocked
        if (rVelocity.m_delta[changeTileIndex] != 0)
            rVelocity.m_lastBlockedIndex = remainTileIndex;
    }
/*
        Resolve a collision between an moving object with collider and a tile map with grid collider
    */
Resolve a collision between an moving object with collider and a tile map with grid collider
Resolve a collision between an moving object with collider and a tile map with grid collider
void
    resolve(Component::Position const& rPosition,
            Component::Velocity& rVelocity,
            Component::Collider const& rCollider,
            Component::Position const& rGridPosition,
            Component::GridCollider const& rGridCollider)
    {
        auto const signZero = [](int x)
        {
            return x == 0 ? 0 : (x < 0 ? -1 : 1);
        };
        int dTile[2];
        dTile[0] = signZero(rVelocity.m_delta[0]);
        dTile[1] = signZero(rVelocity.m_delta[1]);
        auto const coordinateToTileCoordinate = [&](int coordinate, size_t index)
        {
            return (coordinate - rGridPosition.m_value[index] + dTile[index] * (rGridCollider.m_cellDim[index] / 2 - 1)) / rGridCollider.m_cellDim[index];
        };
        auto const tileCoordinateToTargetCoordinate = [&](int tileCoordinate, size_t index)
        {
            return tileCoordinate * rGridCollider.m_cellDim[index] + rGridCollider.m_cellDim[index] / 2 - dTile[index] * (rCollider.m_dim[index] / 2 - rGridCollider.m_cellDim[index] / 2) + rGridPosition.m_value[index];
        };
        int tile[2];
        tile[0] = coordinateToTileCoordinate(rPosition.m_value[0], 0);
        tile[1] = coordinateToTileCoordinate(rPosition.m_value[1], 1);
        if (dTile[1] == 0)
        {
            rVelocity.m_delta[0] = signZero(rVelocity.m_delta[0]) * std::min(std::abs(rVelocity.m_delta[0]),
                                                                             std::abs(tileCoordinateToTargetCoordinate(tile[0], 0) - rPosition.m_value[0]));
            rVelocity.m_lastBlockedIndex = 0;
            return;
        }
        if (dTile[0] == 0)
        {
            rVelocity.m_delta[1] = signZero(rVelocity.m_delta[1]) * std::min(std::abs(rVelocity.m_delta[1]),
                                                                             std::abs(tileCoordinateToTargetCoordinate(tile[1], 1) - rPosition.m_value[1]));
            rVelocity.m_lastBlockedIndex = 1;
            return;
        }
        auto changeTileIndex = rVelocity.m_lastBlockedIndex;
        auto remainTileIndex = (rVelocity.m_lastBlockedIndex + 1) & 1;
        int target[2];
        target[changeTileIndex] = tileCoordinateToTargetCoordinate(tile[changeTileIndex] + dTile[changeTileIndex], changeTileIndex);
        target[remainTileIndex] = tileCoordinateToTargetCoordinate(tile[remainTileIndex], remainTileIndex);
        auto const lastBlockedDirectionStillBlocked = detectCollision(rGridCollider,
                                                                      rCollider,
                                                                      rGridPosition.m_value[0],
                                                                      rGridPosition.m_value[1],
                                                                      target[0],
                                                                      target[1]);
        if (lastBlockedDirectionStillBlocked)
        {
            changeTileIndex = remainTileIndex;
            remainTileIndex = rVelocity.m_lastBlockedIndex;
            target[changeTileIndex] = tileCoordinateToTargetCoordinate(tile[changeTileIndex] + dTile[changeTileIndex], changeTileIndex);
            target[remainTileIndex] = tileCoordinateToTargetCoordinate(tile[remainTileIndex], remainTileIndex);
            if (detectCollision(rGridCollider,
                                rCollider,
                                rGridPosition.m_value[0],
                                rGridPosition.m_value[1],
                                target[0],
                                target[1]))
            {
                target[0] = tileCoordinateToTargetCoordinate(tile[0], 0);
                target[1] = tileCoordinateToTargetCoordinate(tile[1], 1);
            }
        }
        auto const norm = Plight::sqrt(rVelocity.m_delta[0] * rVelocity.m_delta[0] + rVelocity.m_delta[1] * rVelocity.m_delta[1]);
        auto const remainTileDiff = target[remainTileIndex] - rPosition.m_value[remainTileIndex];
        auto const changeTileDiff = target[changeTileIndex] - rPosition.m_value[changeTileIndex];
        rVelocity.m_delta[remainTileIndex] = signZero(remainTileDiff) * std::min(std::abs(remainTileDiff), norm);
        rVelocity.m_delta[changeTileIndex] = signZero(changeTileDiff) * std::min(std::abs(changeTileDiff), Plight::sqrt(norm * norm - rVelocity.m_delta[remainTileIndex] * rVelocity.m_delta[remainTileIndex]));
        if (rVelocity.m_delta[changeTileIndex] != 0)
            rVelocity.m_lastBlockedIndex = remainTileIndex;
    }
void resolve ( Component :: Position const & rPosition , Component :: Velocity & rVelocity , Component :: Collider const & rCollider , Component :: Position const & rGridPosition , Component :: GridCollider const & rGridCollider ) { auto const signZero = [ ] ( int x ) { return x == 0 ? 0 : ( x < 0 ? -1 : 1 ) ; } ; int dTile [ 2 ] ; dTile [ 0 ] = signZero ( rVelocity . m_delta [ 0 ] ) ; dTile [ 1 ] = signZero ( rVelocity . m_delta [ 1 ] ) ; auto const coordinateToTileCoordinate = [ & ] ( int coordinate , size_t index ) { return ( coordinate - rGridPosition . m_value [ index ] + dTile [ index ] * ( rGridCollider . m_cellDim [ index ] / 2 - 1 ) ) / rGridCollider . m_cellDim [ index ] ; } ; auto const tileCoordinateToTargetCoordinate = [ & ] ( int tileCoordinate , size_t index ) { return tileCoordinate * rGridCollider . m_cellDim [ index ] + rGridCollider . m_cellDim [ index ] / 2 - dTile [ index ] * ( rCollider . m_dim [ index ] / 2 - rGridCollider . m_cellDim [ index ] / 2 ) + rGridPosition . m_value [ index ] ; } ; int tile [ 2 ] ; tile [ 0 ] = coordinateToTileCoordinate ( rPosition . m_value [ 0 ] , 0 ) ; tile [ 1 ] = coordinateToTileCoordinate ( rPosition . m_value [ 1 ] , 1 ) ; if ( dTile [ 1 ] == 0 ) { rVelocity . m_delta [ 0 ] = signZero ( rVelocity . m_delta [ 0 ] ) * std :: min ( std :: abs ( rVelocity . m_delta [ 0 ] ) , std :: abs ( tileCoordinateToTargetCoordinate ( tile [ 0 ] , 0 ) - rPosition . m_value [ 0 ] ) ) ; rVelocity . m_lastBlockedIndex = 0 ; return ; } if ( dTile [ 0 ] == 0 ) { rVelocity . m_delta [ 1 ] = signZero ( rVelocity . m_delta [ 1 ] ) * std :: min ( std :: abs ( rVelocity . m_delta [ 1 ] ) , std :: abs ( tileCoordinateToTargetCoordinate ( tile [ 1 ] , 1 ) - rPosition . m_value [ 1 ] ) ) ; rVelocity . m_lastBlockedIndex = 1 ; return ; } auto changeTileIndex = rVelocity . m_lastBlockedIndex ; auto remainTileIndex = ( rVelocity . m_lastBlockedIndex + 1 ) & 1 ; int target [ 2 ] ; target [ changeTileIndex ] = tileCoordinateToTargetCoordinate ( tile [ changeTileIndex ] + dTile [ changeTileIndex ] , changeTileIndex ) ; target [ remainTileIndex ] = tileCoordinateToTargetCoordinate ( tile [ remainTileIndex ] , remainTileIndex ) ; auto const lastBlockedDirectionStillBlocked = detectCollision ( rGridCollider , rCollider , rGridPosition . m_value [ 0 ] , rGridPosition . m_value [ 1 ] , target [ 0 ] , target [ 1 ] ) ; if ( lastBlockedDirectionStillBlocked ) { changeTileIndex = remainTileIndex ; remainTileIndex = rVelocity . m_lastBlockedIndex ; target [ changeTileIndex ] = tileCoordinateToTargetCoordinate ( tile [ changeTileIndex ] + dTile [ changeTileIndex ] , changeTileIndex ) ; target [ remainTileIndex ] = tileCoordinateToTargetCoordinate ( tile [ remainTileIndex ] , remainTileIndex ) ; if ( detectCollision ( rGridCollider , rCollider , rGridPosition . m_value [ 0 ] , rGridPosition . m_value [ 1 ] , target [ 0 ] , target [ 1 ] ) ) { target [ 0 ] = tileCoordinateToTargetCoordinate ( tile [ 0 ] , 0 ) ; target [ 1 ] = tileCoordinateToTargetCoordinate ( tile [ 1 ] , 1 ) ; } } auto const norm = Plight :: sqrt ( rVelocity . m_delta [ 0 ] * rVelocity . m_delta [ 0 ] + rVelocity . m_delta [ 1 ] * rVelocity . m_delta [ 1 ] ) ; auto const remainTileDiff = target [ remainTileIndex ] - rPosition . m_value [ remainTileIndex ] ; auto const changeTileDiff = target [ changeTileIndex ] - rPosition . m_value [ changeTileIndex ] ; rVelocity . m_delta [ remainTileIndex ] = signZero ( remainTileDiff ) * std :: min ( std :: abs ( remainTileDiff ) , norm ) ; rVelocity . m_delta [ changeTileIndex ] = signZero ( changeTileDiff ) * std :: min ( std :: abs ( changeTileDiff ) , Plight :: sqrt ( norm * norm - rVelocity . m_delta [ remainTileIndex ] * rVelocity . m_delta [ remainTileIndex ] ) ) ; if ( rVelocity . m_delta [ changeTileIndex ] != 0 ) rVelocity . m_lastBlockedIndex = remainTileIndex ; }
Resolve a collision between an moving object with collider and a tile map with grid collider
Resolve a collision between an moving object with collider and a tile map with grid collider
// Find a coordinate of the tile the object reaches next (based on a coordinate) // Find the coodinate the object is trying to reach (based on a tile coordinate) // Find the tile the object reaches next // If only moving on x-axis // If only moving on y-axis // First try to move into the direction that was blocked last // If still blocked, try to move into the other direction // If that direction is blocked as well, just move up to the wall // The actual movement: first move on the axis where the tile does not change, then move the rest of the way on the other axis // If the object actually moved in the chosen direction, mark the other direction as last blocked