I have this Three.js
code and like for as basic as it is, is working fine at some degree. Whenever I drag the blue cube with TransformControls
the blue cube detect collision but leaves a big gap in between while moving the cube with arrows is much better and correct. What is causing this behavior on this code?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js Configurator with Collision Detection</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/TransformControls.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
camera.position.z = 5;
window.addEventListener('resize', () => {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
});
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
const geometry1 = new THREE.BoxGeometry(1, 1, 1);
const material1 = new THREE.MeshStandardMaterial({ color: 0x4caf50 });
const cube1 = new THREE.Mesh(geometry1, material1);
scene.add(cube1);
const geometry2 = new THREE.BoxGeometry(1, 1, 1);
const material2 = new THREE.MeshStandardMaterial({ color: 0x4caf50 });
const cube2 = new THREE.Mesh(geometry2, material2);
cube2.position.set(2, 0, 0);
scene.add(cube2);
const boxHelper1 = new THREE.BoxHelper(cube1, 0xff0000);
scene.add(boxHelper1);
const boxHelper2 = new THREE.BoxHelper(cube2, 0x0000ff);
scene.add(boxHelper2);
const transformControls = new THREE.TransformControls(camera, renderer.domElement);
transformControls.attach(cube2);
scene.add(transformControls);
let isDragging = false;
let previousPosition = cube2.position.clone();
transformControls.addEventListener('dragging-changed', (event) => {
controls.enabled = !event.value;
isDragging = event.value;
});
transformControls.addEventListener('objectChange', () => {
if (isDragging) {
if (checkCollision(cube1, cube2)) {
cube2.position.copy(previousPosition);
transformControls.detach();
isDragging = false;
} else {
previousPosition.copy(cube2.position);
}
}
});
function checkCollision(obj1, obj2, tolerance = 0.01) {
const box1 = new THREE.Box3().setFromObject(obj1).expandByScalar(-tolerance);
const box2 = new THREE.Box3().setFromObject(obj2).expandByScalar(-tolerance);
return box1.intersectsBox(box2);
}
function snapToGrid(position, gridSize = 0.1) {
return Math.round(position / gridSize) * gridSize;
}
function animate() {
requestAnimationFrame(animate);
boxHelper1.update();
boxHelper2.update();
if (!isDragging) {
if (checkCollision(cube1, cube2)) {
cube2.material.color.set(0xf44336);
} else {
cube2.material.color.set(0x4caf50);
}
}
renderer.render(scene, camera);
}
animate();
document.addEventListener('keydown', (event) => {
const speed = 0.1;
if (!cube1 || !cube2) return;
const originalPosition = cube2.position.clone();
switch (event.key) {
case 'ArrowUp':
cube2.position.y += speed;
break;
case 'ArrowDown':
cube2.position.y -= speed;
break;
case 'ArrowLeft':
cube2.position.x -= speed;
break;
case 'ArrowRight':
cube2.position.x += speed;
break;
}
cube2.position.x = snapToGrid(cube2.position.x);
cube2.position.y = snapToGrid(cube2.position.y);
if (checkCollision(cube1, cube2)) {
cube2.position.copy(originalPosition);
}
});
</script>
</body>
</html>