1
\$\begingroup\$

I am trying to implement collision detection in a voxel game. Currently I check the side of the collision by finding the closest side and checking whether there isn't a solid block next to that side. This doesn't work for the example shown below because the algorithm doesn't take the block in the bottom into account when checking collision with the block on the top. I could fix this for this example by checking if there are any blocks left of the top block that are solid and also collide with the player on the same side. Is there a better way to do this? enter image description here

In the image the player is sliding right along the wall and gets stuck colliding with the left side of the top block.

enter image description here

A simpler example of the same problem is how to handle the collsion like in the image above. If the player is moving right and there is gravity then if the collision with the left side of the right block gets resolved first the player gets stuck.

\$\endgroup\$
3
  • \$\begingroup\$ Do what you are trying to collide with the voxels have a shape and size? \$\endgroup\$
    – Theraot
    Commented Dec 23, 2021 at 17:35
  • \$\begingroup\$ @Theraot The AABB I am trying to collide with the voxels is the green box. \$\endgroup\$
    – Wojak2121
    Commented Dec 23, 2021 at 17:47
  • \$\begingroup\$ Here are some hints: seperate your horizontal movement from your vertical movement. After resolving a collision, the edge of the moving objects AABB should not be equal to the edge it collided with. It should be a very tiny distance away. \$\endgroup\$ Commented Dec 29, 2021 at 14:27

2 Answers 2

1
\$\begingroup\$

collision on voxel world is very simple. First you have to calculate the player position and size divide by the block size Example: player.position.x + player.size / block.size Then if you're using any sort of grid then check that position/grid if it has a solid block type in it.

Here is the sample code from my voxel game:

void Camera::collision(float dx, float dy, float dz)
{
    /* AABB collision checker */
    
    float x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, y5;
    x1 = floor((m_position.x - m_width) / m_block.getBlockSize());
    y1 = floor((m_position.y - m_bottom) / m_block.getBlockSize());
    z1 = floor((m_position.z - m_width) / m_block.getBlockSize());
    x2 = floor((m_position.x + m_width) / m_block.getBlockSize());
    y2 = floor((m_position.y + (m_top+2)) / m_block.getBlockSize());
    z2 = floor((m_position.z + m_depth) / m_block.getBlockSize());
    
    // stuck? go up
    if(floor(m_position.y / m_block.getBlockSize()) < m_worldSize.y) {
        while(Types::getSolidBlocks( m_map.getInPosition(floor(m_position.x / m_block.getBlockSize()), floor(m_position.y / m_block.getBlockSize()), floor(m_position.z / m_block.getBlockSize())) )) {
            m_position.y += m_block.getBlockSize();
            y1 = floor((m_position.y - m_bottom) / m_block.getBlockSize());
            y2 = floor((m_position.y + (m_top+2)) / m_block.getBlockSize());
            if(floor(m_position.y / m_block.getBlockSize()) > m_worldSize.y) break;
        }
    }
    
    // vertical collision
    
    if(Types::getSolidBlocks(m_map.getInPosition(x1, y1, z1)) || Types::getSolidBlocks(m_map.getInPosition(x2, y1, z1)) ||
        Types::getSolidBlocks(m_map.getInPosition(x2, y1, z2)) || Types::getSolidBlocks(m_map.getInPosition(x1, y1, z2))) {
        
        if(dy < 0) {
            m_position.y = y1 * m_block.getBlockSize() + m_block.getBlockSize() + m_bottom;
            m_direction.y = 0;
            m_onGround = true;
        }
        m_collision[1] = true;
    }
    
    if(Types::getSolidBlocks(m_map.getInPosition(x1, y2, z1)) || Types::getSolidBlocks(m_map.getInPosition(x2, y2, z1)) ||
        Types::getSolidBlocks(m_map.getInPosition(x2, y2, z2)) || Types::getSolidBlocks(m_map.getInPosition(x1, y2, z2))) {
        
        if(dy > 0) {
            m_position.y = y2 * m_block.getBlockSize() - (m_top+2) - .01;
            m_direction.y = 0;
        }
        m_collision[1] = true;
    }
    
    // horizontal collision
    
    x3 = floor((m_position.x - dx - m_width) / m_block.getBlockSize());
    y3 = floor((m_position.y - dy - m_bottom) / m_block.getBlockSize());
    z3 = floor((m_position.z - dz - m_width) / m_block.getBlockSize());
    x4 = floor((m_position.x - dx + m_width) / m_block.getBlockSize());
    y4 = floor((m_position.y - dy + (m_top+2)) / m_block.getBlockSize());
    z4 = floor((m_position.z - dz + m_depth) / m_block.getBlockSize());
    y5 = round(y3+(y4-y3)/2); // previous y position
    
    if(Types::getSolidBlocks(m_map.getInPosition(x1, y3, z3)) || Types::getSolidBlocks(m_map.getInPosition(x1, y3, z4)) ||
        Types::getSolidBlocks(m_map.getInPosition(x1, y4, z4)) || Types::getSolidBlocks(m_map.getInPosition(x1, y4, z3)) ||
        Types::getSolidBlocks(m_map.getInPosition(x1, y5, z3)) || Types::getSolidBlocks(m_map.getInPosition(x1, y5, z4))) {
        
        if(dx < 0) {
            m_position.x = (x3-1) * m_block.getBlockSize() + m_block.getBlockSize() + m_width;
            m_direction.x = 0;
        }
        m_collision[0] = true;
    }
    
    if(Types::getSolidBlocks(m_map.getInPosition(x2, y3, z3)) || Types::getSolidBlocks(m_map.getInPosition(x2, y3, z4)) ||
        Types::getSolidBlocks(m_map.getInPosition(x2, y4, z4)) || Types::getSolidBlocks(m_map.getInPosition(x2, y4, z3)) ||
        Types::getSolidBlocks(m_map.getInPosition(x2, y5, z3)) || Types::getSolidBlocks(m_map.getInPosition(x2, y5, z4))) {
        
        if(dx > 0) {
            m_position.x = (x4+1) * m_block.getBlockSize() - m_width - .01;
            m_direction.x = 0;
        }
        m_collision[0] = true;
    }
    
    if(Types::getSolidBlocks(m_map.getInPosition(x3, y3, z1)) || Types::getSolidBlocks(m_map.getInPosition(x4, y3, z1)) ||
        Types::getSolidBlocks(m_map.getInPosition(x4, y4, z1)) || Types::getSolidBlocks(m_map.getInPosition(x3, y4, z1)) ||
        Types::getSolidBlocks(m_map.getInPosition(x3, y5, z1)) || Types::getSolidBlocks(m_map.getInPosition(x4, y5, z1))) {
        
        if(dz < 0) {
            m_position.z = (z3-1) * m_block.getBlockSize() + m_block.getBlockSize() + m_depth;
            m_direction.z = 0;
        }
        m_collision[2] = true;
    }
    
    if(Types::getSolidBlocks(m_map.getInPosition(x3, y3, z2)) || Types::getSolidBlocks(m_map.getInPosition(x4, y3, z2)) ||
        Types::getSolidBlocks(m_map.getInPosition(x4, y4, z2)) || Types::getSolidBlocks(m_map.getInPosition(x3, y4, z2)) ||
        Types::getSolidBlocks(m_map.getInPosition(x3, y5, z2)) || Types::getSolidBlocks(m_map.getInPosition(x4, y5, z2))) {
        
        if(dz > 0) {
            m_position.z = (z4+1) * m_block.getBlockSize() - m_depth - .01;
            m_direction.z = 0;
        }
        m_collision[2] = true;
    }
    
}

Btw. The m_map is a grid that checks the block type in the world and dx, dy, dz are the direction of the player

and here's how to use the collider:

// player movement
m_position.x += m_direction.x * dt;
collision(m_direction.x, 0, 0); // collision detector
m_position.y += m_direction.y * dt;
collision(0, m_direction.y, 0); // collision detector
m_position.z += m_direction.z * dt;
collision(0, 0, m_direction.z); // collision detector
\$\endgroup\$
-1
\$\begingroup\$

I implemented this and it works.

\$\endgroup\$
3
  • \$\begingroup\$ This is currently a link only answer, could you describe what it is in the core of the answer? \$\endgroup\$
    – Vaillancourt
    Commented Dec 30, 2021 at 18:29
  • \$\begingroup\$ @Vaillancourt Should I post the full source code? It is around 250 lines long. \$\endgroup\$
    – Wojak2121
    Commented Dec 31, 2021 at 1:29
  • 1
    \$\begingroup\$ Write only what is needed to answer the question asked. We like full answer instead of links in case the target page disappears. \$\endgroup\$
    – Vaillancourt
    Commented Dec 31, 2021 at 3:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .