Взаимодействие пользователя с 3D-сценой в Three.js реализуется через события клика и наведения мыши (hover). Для обработки таких событий необходимо определить, на какие объекты пользователь указывает, и связать эти объекты с соответствующими реакциями. Основной инструмент для этого — Raycaster.
Raycaster позволяет создавать «луч» из точки камеры через позицию курсора на экране и определять, какие объекты пересекает этот луч.
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
mouse.x и mouse.y должны быть
нормализованы в диапазон от -1 до 1.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
Параметр true позволяет проверять вложенные объекты
(дети).
Клик по объекту реализуется через событие мыши click или
mousedown:
window.addEventListener('click', (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
console.log('Объект кликнут:', clickedObject.name);
clickedObject.material.color.set(0xff0000);
}
});
Особенности:
intersects[0] содержит ближайший к камере объект.name или userData
для идентификации объекта.Hover требует постоянного отслеживания позиции мыши и проверки пересечений на каждом кадре или при движении курсора.
window.addEventListener('mousemove', (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});
function animate() {
requestAnimationFrame(animate);
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
scene.children.forEach(obj => obj.material.emissive?.set(0x000000));
if (intersects.length > 0) {
const hoveredObject = intersects[0].object;
if (hoveredObject.material.emissive) {
hoveredObject.material.emissive.set(0x555555);
}
}
renderer.render(scene, camera);
}
animate();
Особенности:
material.emissive
для визуального подсвечивания.material.emissive предотвращает ошибки на
объектах без свойства emissive.Если сцена содержит множество объектов, проверка всех элементов на каждом кадре может быть дорогой. Решения:
raycaster.intersectObjects(group.children)).THREE.Mesh).Для удобства взаимодействия объекты могут содержать дополнительную информацию:
mesh.userData = {
id: 1,
type: 'enemy',
onClick: () => { console.log('Враг атакован'); }
};
Использование userData позволяет:
if (intersects.length > 0) {
intersects[0].object.userData.onClick?.();
}
Обычно к объектам привязывают разные реакции для клика и hover:
if (intersects.length > 0) {
const obj = intersects[0].object;
obj.userData.hovered = true;
} else {
scene.children.forEach(o => o.userData.hovered = false);
}
hovered, selected) для корректного
отображения.Эти механизмы являются базой для создания интерактивных 3D-приложений на Three.js и позволяют реализовать богатые интерфейсы без обращения к сторонним библиотекам.