Hello, I’m making a 3d version of Binding of Isaac for uni, I’m using Box3 for hitboxes and at first I was using the entire player model for the hitbox, passing it into setFromObject, and that worked because the player model measured the same on the x and z axes, but I plan on adding things to the model so it won’t work as intended, plus the hitbox is too big so instead I’m adding a BoxGeometry to the isaac model on creation that I will use as a hitbox, like this:
function genIsaac() {
let isaac_skin_material = new THREE.MeshPhysicalMaterial( { color: 0xffcccc } );
let isaac_head_geometry = new THREE.SphereGeometry( 0.4, 16, 8 );
let isaac = new THREE.Mesh( isaac_head_geometry, isaac_skin_material );
scene.add( isaac );
let isaac_core_geometry = new THREE.BoxGeometry( 0.5, 1.275, 0.5 );
let isaac_core_material = new THREE.MeshBasicMaterial( { color: 0x000000, visible: false } );
let isaac_core = new THREE.Mesh( isaac_core_geometry, isaac_core_material );
isaac_core.layers.toggle( BLOOM_SCENE );
isaac.add( isaac_core );
isaac_core.position.y = -0.2375;
and then I am checking for collisions in the function that updates the player’s position, like this:
isaac_pos.lerp( isaac_speed, 0.15 );
isaac.position.x += isaac_pos.x / 10 * isaac.userData.speed;
if ( collidesWithWall( isaac ) || ( collidesWithPedestal( isaac.children[0] ) && !isaac.userData.flying ) || ( collidesWithObstacle( isaac.children[0] ) && !isaac.userData.flying ) ) {
isaac.position.x -= isaac_pos.x / 10 * isaac.userData.speed;
while ( true ) {
isaac.position.x += isaac_pos.x / 1000;
if ( collidesWithWall( isaac ) || ( collidesWithPedestal( isaac.children[0] ) && !isaac.userData.flying ) || ( collidesWithObstacle( isaac.children[0] ) && !isaac.userData.flying ) ) {
isaac.position.x -= isaac_pos.x / 1000;
break;
}
}
}
isaac.position.z += isaac_pos.y / 10 * isaac.userData.speed;
if ( collidesWithWall( isaac ) || ( collidesWithPedestal( isaac.children[0] ) && !isaac.userData.flying ) || ( collidesWithObstacle( isaac.children[0] ) && !isaac.userData.flying ) ) {
isaac.position.z -= isaac_pos.y / 10 * isaac.userData.speed;
while ( true ) {
isaac.position.z += isaac_pos.y / 1000;
if ( collidesWithWall( isaac ) || ( collidesWithPedestal( isaac.children[0] ) && !isaac.userData.flying ) || ( collidesWithObstacle( isaac.children[0] ) && !isaac.userData.flying ) ) {
isaac.position.z -= isaac_pos.y / 1000;
break;
}
}
}
now, you may notice how I check for collisions with pedestals and obstacles passing isaac.children[0], which is the BoxGeometry mentioned earlier, but for walls I’m using the full model, and that is because the moment I pass the hitbox, instead of colliding with walls and justnot moving further but still being able to move sideways or backwards, it gets stuck in the wall, and what bothers me is that it also breaks the other two functions, meaning it starts behaving the wrong way when colliding with pedestals and obstacles as well, and I don’t understand why, especially since collidesWithWall and collidesWithObstacle are two practically identical functions, save for the lists used which store the objects that I want to check the collisions for:
function collidesWithWall( object ) {
let wall_hitbox = new THREE.Box3();
let object_hitbox = new THREE.Box3();
if ( object.isObject3D ) {
object_hitbox.setFromObject( object );
} else {
object_hitbox = object;
}
for ( let i = 0; i < outer_bounds.length; i++ ) {
wall_hitbox.setFromObject( outer_bounds[i] );
if ( wall_hitbox.intersectsBox( object_hitbox ) ) {
return true;
}
}
return false;
}
function collidesWithObstacle( object ) {
let obstacle_hitbox = new THREE.Box3();
let object_hitbox = new THREE.Box3();
if ( object.isObject3D ) {
object_hitbox.setFromObject( object );
} else {
object_hitbox = object;
}
for ( let i = 0; i < obstacle_hitboxes.length; i++ ) {
obstacle_hitbox.setFromObject( obstacle_hitboxes[i] );
if ( obstacle_hitbox.intersectsBox( object_hitbox ) ) {
return true;
}
}
return false;
}
function collidesWithPedestal( object ) {
let pedestal_hitbox = new THREE.Box3();
let object_hitbox = new THREE.Box3();
object_hitbox.setFromObject( object );
for ( let i = 0; i < pedestals.length; i++ ) {
pedestal_hitbox.setFromObject( pedestals[i] );
if ( pedestal_hitbox.intersectsBox( object_hitbox ) ) {
if ( object.parent.userData.player && !pedestals[i].userData.taken ) {
pedestals[i].userData.taken = true;
pedestals[i].clear();
addItem( pedestals[i].userData.id );
}
return true;
}
}
return false;
}
Using isaac.children[0] for the other two functions works just fine, but everything breaks the moment I put it in collidesWithWall.
I can put up some footage of the issue if I haven’t made myself clear enough.
EDIT:
Video footage of the collision system when I pass isaac as argument for collidesWithWall, I made the hitbox visible so it’s more visually clear, the black box deals with the collisions with the rocks and the pedestals, and the full model deals with the collisions with the walls, but it should be all the black box:
2023-12-07 01-04-47.mkv (3.9 MB)
Video footage of the collision system when I pass isaac.children[0] as argument for collidesWithWall, as soon as I collide with a rock, a pedestal, or a wall, I get stuck:
2023-12-07 01-07-25.mkv (4.4 MB)
EDIT 2:
the full project on codepen, the code provided works as intended save for the fact that the full model is being used for wall collisions instead of the black box, to reproduce the issue replace “isaac” with “isaac.children[0]” for the collidesWithWall function at lines 1688, 1692, 1699 and 1703, you’ll find the collidesWith functions at lines 1324, 1399 and 1416: