Testreact / index.html
Aleksmorshen's picture
Update index.html
b925469 verified
raw
history blame
13.3 kB
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Простой Minecraft-клон на Babylon.js</title>
<style>
html, body {
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: sans-serif; /* Добавим шрифт для UI */
}
#renderCanvas {
width: 100%;
height: 100%;
touch-action: none; /* Для мобильных устройств */
}
/* Простой интерфейс для прицела */
#crosshair {
position: absolute;
top: 50%;
left: 50%;
width: 10px;
height: 10px;
border: 1px solid white;
background-color: rgba(0, 0, 0, 0.5);
transform: translate(-50%, -50%);
pointer-events: none; /* Чтобы не мешал кликам */
}
/* Инструкция */
#instructions {
position: absolute;
bottom: 10px;
left: 10px;
color: white;
background-color: rgba(0,0,0,0.5);
padding: 5px;
border-radius: 3px;
font-size: 12px;
}
</style>
<!-- Подключение Babylon.js -->
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<!-- Опционально: загрузчики моделей/текстур, если понадобятся -->
<!-- <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script> -->
</head>
<body>
<canvas id="renderCanvas"></canvas>
<div id="crosshair"></div>
<div id="instructions">
WASD/Стрелки: Движение | Пробел: Вверх | Shift: Вниз <br>
Левый клик: Удалить блок | Правый клик: Поставить блок
</div>
<script>
const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });
const BLOCK_SIZE = 1; // Размер одного блока
const WORLD_WIDTH = 16; // Ширина мира в блоках
const WORLD_DEPTH = 16; // Глубина мира в блоках
const WORLD_HEIGHT = 8; // Максимальная высота мира (для генерации)
// --- Создание сцены ---
const createScene = () => {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color3(0.5, 0.8, 1.0); // Цвет неба
scene.gravity = new BABYLON.Vector3(0, -0.9, 0); // Гравитация
scene.collisionsEnabled = true; // Включаем обработку столкновений
// --- Камера ---
const camera = new BABYLON.UniversalCamera("playerCamera", new BABYLON.Vector3(WORLD_WIDTH / 2, WORLD_HEIGHT + 2, WORLD_DEPTH / 2), scene);
camera.setTarget(BABYLON.Vector3.Zero()); // Смотрим в центр
camera.attachControl(canvas, true); // Подключаем управление
camera.speed = 0.2; // Скорость движения
camera.angularSensibility = 4000; // Чувствительность мыши
// Включаем столкновения для камеры
camera.checkCollisions = true;
camera.applyGravity = true;
// Задаем "эллипсоид" - физическую модель игрока для столкновений
camera.ellipsoid = new BABYLON.Vector3(0.5, 0.9, 0.5); // Ширина, Высота, Глубина
// Управление с клавиатуры (WASD + пробел/shift)
camera.keysUp.push(87); // W
camera.keysDown.push(83); // S
camera.keysLeft.push(65); // A
camera.keysRight.push(68); // D
// Добавляем управление вверх/вниз (не стандартное для UniversalCamera)
scene.onBeforeRenderObservable.add(() => {
if (camera.inputs.attached.keyboard) {
const keyboard = camera.inputs.attached.keyboard;
if (keyboard.directInput[' ']) { // Пробел
camera.cameraDirection.y += camera.speed / 10; // Двигаемся вверх
}
if (keyboard.directInput[16]) { // Shift
camera.cameraDirection.y -= camera.speed / 10; // Двигаемся вниз
}
}
});
// --- Освещение ---
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.8;
// --- Материалы блоков ---
const grassMaterial = new BABYLON.StandardMaterial("grassMat", scene);
grassMaterial.diffuseColor = new BABYLON.Color3(0.4, 0.8, 0.4); // Зеленый
grassMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1); // Меньше бликов
const dirtMaterial = new BABYLON.StandardMaterial("dirtMat", scene);
dirtMaterial.diffuseColor = new BABYLON.Color3(0.6, 0.4, 0.2); // Коричневый
dirtMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1);
const stoneMaterial = new BABYLON.StandardMaterial("stoneMat", scene);
stoneMaterial.diffuseColor = new BABYLON.Color3(0.5, 0.5, 0.5); // Серый
stoneMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1);
// --- Мир блоков ---
// Используем Map для хранения блоков { "x,y,z": mesh }
const blocks = new Map();
// Прототип блока (для клонирования)
const blockPrototype = BABYLON.MeshBuilder.CreateBox("blockProto", { size: BLOCK_SIZE }, scene);
blockPrototype.isVisible = false; // Сам прототип не видим
blockPrototype.checkCollisions = true; // Блоки должны иметь коллизию
// Генерация простого ландшафта
for (let x = 0; x < WORLD_WIDTH; x++) {
for (let z = 0; z < WORLD_DEPTH; z++) {
// Простая высота на основе шума (очень примитивно)
const height = Math.floor(WORLD_HEIGHT / 2 + Math.sin(x * 0.3) * 2 + Math.cos(z * 0.2) * 2);
for (let y = 0; y < height; y++) {
const block = blockPrototype.clone(`block_${x}_${y}_${z}`);
block.position = new BABYLON.Vector3(
x * BLOCK_SIZE + BLOCK_SIZE / 2,
y * BLOCK_SIZE + BLOCK_SIZE / 2,
z * BLOCK_SIZE + BLOCK_SIZE / 2
);
block.isVisible = true;
// Назначаем материал в зависимости от высоты
if (y === height - 1) {
block.material = grassMaterial; // Верхний слой - трава
} else if (y > height - 4) {
block.material = dirtMaterial; // Под травой - земля
} else {
block.material = stoneMaterial; // Глубже - камень
}
const blockKey = `${block.position.x - BLOCK_SIZE/2},${block.position.y - BLOCK_SIZE/2},${block.position.z - BLOCK_SIZE/2}`; // Ключ по нижнему углу
blocks.set(blockKey, block);
block.isBlock = true; // Флаг, что это наш блок
}
}
}
// --- Взаимодействие с блоками ---
scene.onPointerDown = (evt, pickResult) => {
// evt.button: 0 = левый клик, 1 = средний, 2 = правый клик
// --- Удаление блока (левый клик) ---
if (evt.button === 0) {
if (pickResult.hit && pickResult.pickedMesh && pickResult.pickedMesh.isBlock) {
const blockToRemove = pickResult.pickedMesh;
const blockKey = `${blockToRemove.position.x - BLOCK_SIZE/2},${blockToRemove.position.y - BLOCK_SIZE/2},${blockToRemove.position.z - BLOCK_SIZE/2}`;
blocks.delete(blockKey);
blockToRemove.dispose();
}
}
// --- Размещение блока (правый клик) ---
if (evt.button === 2) {
if (pickResult.hit && pickResult.pickedMesh) { // Проверяем, что попали во что-то
// Определяем позицию для нового блока
// Она должна быть смещена от точки попадания по нормали к поверхности
const normal = pickResult.getNormal(true); // Получаем нормаль к грани
const hitPoint = pickResult.pickedPoint;
// Вычисляем центр потенциального нового блока
const potentialPos = hitPoint.add(normal.scale(BLOCK_SIZE / 2));
// Округляем (снапим) к сетке блоков
// Важно: Вычитаем/добавляем половину размера блока для центрирования
const newBlockX = Math.floor(potentialPos.x / BLOCK_SIZE) * BLOCK_SIZE + BLOCK_SIZE / 2;
const newBlockY = Math.floor(potentialPos.y / BLOCK_SIZE) * BLOCK_SIZE + BLOCK_SIZE / 2;
const newBlockZ = Math.floor(potentialPos.z / BLOCK_SIZE) * BLOCK_SIZE + BLOCK_SIZE / 2;
const newBlockKey = `${newBlockX - BLOCK_SIZE/2},${newBlockY - BLOCK_SIZE/2},${newBlockZ - BLOCK_SIZE/2}`;
// Проверяем, не занято ли это место
if (!blocks.has(newBlockKey)) {
// Проверяем, не ставим ли блок "внутрь" игрока
const playerBox = new BABYLON.BoundingBox(
camera.position.subtract(camera.ellipsoid),
camera.position.add(camera.ellipsoid)
);
const newBlockBox = new BABYLON.BoundingBox(
new BABYLON.Vector3(newBlockX - BLOCK_SIZE/2, newBlockY - BLOCK_SIZE/2, newBlockZ - BLOCK_SIZE/2),
new BABYLON.Vector3(newBlockX + BLOCK_SIZE/2, newBlockY + BLOCK_SIZE/2, newBlockZ + BLOCK_SIZE/2)
);
if (!playerBox.intersects(newBlockBox)) {
const newBlock = blockPrototype.clone(`block_${newBlockKey.replace(/,/g, '_')}`);
newBlock.position = new BABYLON.Vector3(newBlockX, newBlockY, newBlockZ);
newBlock.material = dirtMaterial; // По умолчанию ставим землю
newBlock.isVisible = true;
newBlock.isBlock = true;
blocks.set(newBlockKey, newBlock);
} else {
console.log("Нельзя ставить блок внутри себя!");
}
} else {
console.log("Место занято!");
}
}
}
};
// Отключаем контекстное меню по правому клику на канвасе
canvas.addEventListener("contextmenu", (evt) => {
evt.preventDefault();
});
// --- Оптимизация (очень базовая) ---
// Можно добавить Octree для ускорения рендеринга и коллизий
// scene.createOrUpdateSelectionOctree();
return scene;
};
// --- Запуск ---
const scene = createScene();
engine.runRenderLoop(() => {
scene.render();
});
window.addEventListener('resize', () => {
engine.resize();
});
</script>
</body>
</html>