Files
rltn/test-collision.html

406 lines
17 KiB
HTML
Raw Permalink Normal View History

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Тест системы коллизий интерьеров</title>
<style>
body {
margin: 0;
padding: 0;
background: #000;
color: #fff;
font-family: Arial, sans-serif;
overflow: hidden;
}
#gameContainer {
width: 100vw;
height: 100vh;
position: relative;
}
#debugInfo {
position: absolute;
top: 10px;
left: 10px;
background: rgba(0, 0, 0, 0.7);
padding: 10px;
border-radius: 5px;
font-size: 12px;
z-index: 1000;
}
#instructions {
position: absolute;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.7);
padding: 10px;
border-radius: 5px;
font-size: 12px;
z-index: 1000;
max-width: 300px;
}
</style>
</head>
<body>
<div id="gameContainer"></div>
<div id="debugInfo">
<div>Статус: <span id="status">Загрузка...</span></div>
<div>Коллайдеры: <span id="colliders">0</span></div>
<div>Позиция игрока: <span id="position">0, 0, 0</span></div>
<div>В интерьере: <span id="inInterior">false</span></div>
</div>
<div id="instructions">
<h3>Управление:</h3>
<p><strong>WASD</strong> - движение</p>
<p><strong>Мышь</strong> - поворот камеры (в интерьере)</p>
<p><strong>Клик по объекту</strong> - вход в интерьер</p>
<p><strong>Escape</strong> - выход из интерьера</p>
<br>
<p><strong>Тест коллизий:</strong></p>
<p>В интерьере игрок не должен проходить сквозь стены и объекты</p>
</div>
<script type="module">
import * as THREE from 'https://unpkg.com/three@0.158.0/build/three.module.js';
// Простая система коллизий для тестирования
class SimpleCollisionTest {
constructor() {
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.player = null;
this.isInInterior = false;
this.interiorColliders = [];
this.moveInput = { forward: false, backward: false, left: false, right: false };
this.init();
}
init() {
// Настройка рендерера
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setClearColor(0x87CEEB);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.getElementById('gameContainer').appendChild(this.renderer.domElement);
// Создаем простую сцену
this.createScene();
// Создаем игрока
this.createPlayer();
// Настраиваем обработчики событий
this.setupEventListeners();
// Запускаем игровой цикл
this.animate();
this.updateDebugInfo();
}
createScene() {
// Освещение
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 10, 5);
directionalLight.castShadow = true;
this.scene.add(directionalLight);
// Создаем простой интерьер для тестирования
this.createTestInterior();
}
createTestInterior() {
const interiorGroup = new THREE.Group();
// Стены
const wallGeometry = new THREE.BoxGeometry(0.2, 3, 10);
const wallMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
// Левая стена
const leftWall = new THREE.Mesh(wallGeometry, wallMaterial);
leftWall.position.set(-5, 1.5, 0);
leftWall.castShadow = true;
interiorGroup.add(leftWall);
// Правая стена
const rightWall = new THREE.Mesh(wallGeometry, wallMaterial);
rightWall.position.set(5, 1.5, 0);
rightWall.castShadow = true;
interiorGroup.add(rightWall);
// Задняя стена
const backWallGeometry = new THREE.BoxGeometry(10, 3, 0.2);
const backWall = new THREE.Mesh(backWallGeometry, wallMaterial);
backWall.position.set(0, 1.5, -5);
backWall.castShadow = true;
interiorGroup.add(backWall);
// Передняя стена (с проходом)
const frontWall1 = new THREE.Mesh(new THREE.BoxGeometry(4, 3, 0.2), wallMaterial);
frontWall1.position.set(-3, 1.5, 5);
frontWall1.castShadow = true;
interiorGroup.add(frontWall1);
const frontWall2 = new THREE.Mesh(new THREE.BoxGeometry(4, 3, 0.2), wallMaterial);
frontWall2.position.set(3, 1.5, 5);
frontWall2.castShadow = true;
interiorGroup.add(frontWall2);
// Пол
const floorGeometry = new THREE.BoxGeometry(10, 0.1, 10);
const floorMaterial = new THREE.MeshLambertMaterial({ color: 0x654321 });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.set(0, 0, 0);
floor.receiveShadow = true;
interiorGroup.add(floor);
// Потолок
const ceiling = new THREE.Mesh(floorGeometry, new THREE.MeshLambertMaterial({ color: 0xFFFFFF }));
ceiling.position.set(0, 3, 0);
interiorGroup.add(ceiling);
// Объекты в интерьере
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const boxMaterial = new THREE.MeshLambertMaterial({ color: 0xFF0000 });
const box1 = new THREE.Mesh(boxGeometry, boxMaterial);
box1.position.set(-2, 0.5, -2);
box1.castShadow = true;
interiorGroup.add(box1);
const box2 = new THREE.Mesh(boxGeometry, boxMaterial);
box2.position.set(2, 0.5, 2);
box2.castShadow = true;
interiorGroup.add(box2);
// Собираем коллайдеры
this.interiorColliders = [];
interiorGroup.traverse((child) => {
if (child.isMesh && child.geometry) {
this.interiorColliders.push(child);
}
});
this.scene.add(interiorGroup);
this.interiorGroup = interiorGroup;
console.log('Создано коллайдеров:', this.interiorColliders.length);
}
createPlayer() {
const playerGeometry = new THREE.BoxGeometry(0.6, 1.6, 0.6);
const playerMaterial = new THREE.MeshLambertMaterial({ color: 0x0000FF });
this.player = new THREE.Mesh(playerGeometry, playerMaterial);
this.player.position.set(0, 0.8, 0);
this.scene.add(this.player);
// Устанавливаем камеру на уровне глаз игрока
this.camera.position.set(0, 1.6, 0);
this.camera.lookAt(0, 1.6, -1);
}
setupEventListeners() {
// Клавиатура
document.addEventListener('keydown', (event) => {
switch(event.code) {
case 'KeyW':
this.moveInput.forward = true;
break;
case 'KeyS':
this.moveInput.backward = true;
break;
case 'KeyA':
this.moveInput.left = true;
break;
case 'KeyD':
this.moveInput.right = true;
break;
case 'Escape':
this.exitInterior();
break;
}
});
document.addEventListener('keyup', (event) => {
switch(event.code) {
case 'KeyW':
this.moveInput.forward = false;
break;
case 'KeyS':
this.moveInput.backward = false;
break;
case 'KeyA':
this.moveInput.left = false;
break;
case 'KeyD':
this.moveInput.right = false;
break;
}
});
// Мышь для поворота камеры в интерьере
document.addEventListener('mousemove', (event) => {
if (this.isInInterior) {
this.camera.rotation.y -= event.movementX * 0.002;
this.camera.rotation.x -= event.movementY * 0.002;
this.camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, this.camera.rotation.x));
}
});
// Клик для входа в интерьер
this.renderer.domElement.addEventListener('click', () => {
if (!this.isInInterior) {
this.enterInterior();
}
});
// Изменение размера окна
window.addEventListener('resize', () => {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
});
}
enterInterior() {
console.log('Вход в интерьер');
this.isInInterior = true;
this.player.visible = false;
// Запрашиваем pointer lock
this.renderer.domElement.requestPointerLock();
this.updateDebugInfo();
}
exitInterior() {
console.log('Выход из интерьера');
this.isInInterior = false;
this.player.visible = true;
// Выходим из pointer lock
document.exitPointerLock();
this.updateDebugInfo();
}
checkCollision(testPosition) {
const playerRadius = 0.3;
const playerHeight = 1.6;
// Создаем AABB для игрока
const playerBox = new THREE.Box3();
const playerMin = new THREE.Vector3(
testPosition.x - playerRadius,
testPosition.y,
testPosition.z - playerRadius
);
const playerMax = new THREE.Vector3(
testPosition.x + playerRadius,
testPosition.y + playerHeight,
testPosition.z + playerRadius
);
playerBox.setFromPoints([playerMin, playerMax]);
// Проверяем столкновения с коллайдерами
for (const collider of this.interiorColliders) {
if (!collider.geometry || !collider.visible) continue;
collider.updateMatrixWorld(true);
const colliderBox = new THREE.Box3();
colliderBox.setFromObject(collider);
if (playerBox.intersectsBox(colliderBox)) {
return true;
}
}
return false;
}
updatePlayer(deltaTime) {
if (!this.isInInterior) return;
const speed = 3.0;
const moveDistance = speed * deltaTime;
// Используем простые направления вместо кватернионов
const forward = new THREE.Vector3(0, 0, -1);
const right = new THREE.Vector3(1, 0, 0);
// Поворачиваем направления в соответствии с поворотом камеры
forward.applyEuler(new THREE.Euler(0, this.camera.rotation.y, 0));
right.applyEuler(new THREE.Euler(0, this.camera.rotation.y, 0));
let moveVector = new THREE.Vector3();
if (this.moveInput.forward) moveVector.add(forward);
if (this.moveInput.backward) moveVector.add(forward.clone().multiplyScalar(-1));
if (this.moveInput.left) moveVector.add(right.clone().multiplyScalar(-1));
if (this.moveInput.right) moveVector.add(right);
if (moveVector.length() > 0) {
moveVector.normalize().multiplyScalar(moveDistance);
// Проверяем коллизии по осям отдельно
let safePosition = this.camera.position.clone();
// Проверяем движение по X
if (Math.abs(moveVector.x) > 0.001) {
const xTestPosition = safePosition.clone();
xTestPosition.x += moveVector.x;
if (!this.checkCollision(xTestPosition)) {
safePosition.x = xTestPosition.x;
}
}
// Проверяем движение по Z
if (Math.abs(moveVector.z) > 0.001) {
const zTestPosition = safePosition.clone();
zTestPosition.z += moveVector.z;
if (!this.checkCollision(zTestPosition)) {
safePosition.z = zTestPosition.z;
}
}
this.camera.position.copy(safePosition);
}
}
updateDebugInfo() {
document.getElementById('status').textContent = this.isInInterior ? 'В интерьере' : 'Вне интерьера';
document.getElementById('colliders').textContent = this.interiorColliders.length;
document.getElementById('position').textContent =
`${this.camera.position.x.toFixed(2)}, ${this.camera.position.y.toFixed(2)}, ${this.camera.position.z.toFixed(2)}`;
document.getElementById('inInterior').textContent = this.isInInterior;
}
animate() {
requestAnimationFrame(() => this.animate());
const deltaTime = 0.016; // Примерно 60 FPS
this.updatePlayer(deltaTime);
this.updateDebugInfo();
this.renderer.render(this.scene, this.camera);
}
}
// Запускаем тест
const test = new SimpleCollisionTest();
</script>
</body>
</html>