# Voxel terrain vs AABB collision detection

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?

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

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.

• Do what you are trying to collide with the voxels have a shape and size? Dec 23, 2021 at 17:35
• @Theraot The AABB I am trying to collide with the voxels is the green box. Dec 23, 2021 at 17:47
• 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. Dec 29, 2021 at 14:27

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


I implemented this and it works.

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