Aleksmorshen commited on
Commit
b6a8119
·
verified ·
1 Parent(s): a030a20

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +351 -269
index.html CHANGED
@@ -3,18 +3,14 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
- <title>Metaverse Prototype - Babylon.js Demo</title>
7
- <meta name="description" content="Прототип метавселенной, созданный с помощью Babylon.js. Демонстрация 3D-мира для презентации.">
8
-
9
  <!-- Подключение Babylon.js -->
10
  <script src="https://cdn.babylonjs.com/babylon.js"></script>
11
- <!-- Опционально: загрузчики для моделей (например, glTF) -->
12
  <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
13
- <!-- Опционально: библиотека для GUI Babylon.js (здесь используется HTML/CSS) -->
14
- <!-- <script src="https://cdn.babylonjs.com/gui/babylon.gui.min.js"></script> -->
15
- <!-- Опционально: Post-processing эффекты -->
16
- <script src="https://cdn.babylonjs.com/postProcessesLibrary/babylon.postProcessLibrary.min.js"></script>
17
-
18
 
19
  <style>
20
  html, body {
@@ -24,17 +20,18 @@
24
  margin: 0;
25
  padding: 0;
26
  font-family: Arial, sans-serif;
27
- color: #fff;
28
- background-color: #000; /* Фон на время загрузки */
29
  }
30
 
31
  #renderCanvas {
32
  width: 100%;
33
  height: 100%;
34
- touch-action: none; /* Отключает стандартные действия браузера при касании (важно для управления) */
 
35
  }
36
 
37
- /* --- Экран загрузки --- */
38
  #loadingScreen {
39
  position: absolute;
40
  top: 0;
@@ -42,342 +39,427 @@
42
  width: 100%;
43
  height: 100%;
44
  background-color: #1a1a1a;
 
45
  display: flex;
 
46
  justify-content: center;
47
  align-items: center;
48
- font-size: 2em;
49
- color: #ccc;
50
  z-index: 100;
51
  transition: opacity 0.5s ease-out;
52
  }
53
 
54
  #loadingScreen.hidden {
55
  opacity: 0;
56
- pointer-events: none;
57
- }
58
-
59
- /* --- UI Оверлей (Пример) --- */
60
- #ui-overlay {
61
- position: absolute;
62
- top: 15px;
63
- left: 15px;
64
- background-color: rgba(0, 0, 0, 0.5);
65
- padding: 10px;
66
- border-radius: 5px;
67
- z-index: 10;
68
  }
69
 
70
- #ui-overlay h1 {
71
- margin: 0 0 5px 0;
72
- font-size: 1.2em;
 
 
 
 
 
73
  }
74
 
75
- #ui-overlay p {
76
- margin: 0;
77
- font-size: 0.9em;
78
  }
79
 
80
- /* --- Мобильные элементы управления (визуальные) --- */
81
- .mobile-controls {
82
  position: absolute;
83
  bottom: 20px;
84
- width: 100%;
85
- display: none; /* По умолчанию скрыты */
86
- justify-content: space-between;
87
- align-items: flex-end;
88
- padding: 0 20px;
89
- box-sizing: border-box;
90
  z-index: 10;
91
- pointer-events: none; /* Сами контейнеры не должны перехватывать события */
92
- }
93
-
94
- .joystick-area {
95
- width: 120px;
96
- height: 120px;
97
- background-color: rgba(80, 80, 80, 0.4);
98
- border-radius: 50%;
99
- position: relative;
100
- pointer-events: auto; /* Область джойстика должна быть интерактивна */
101
- display: flex;
102
- justify-content: center;
103
- align-items: center;
104
  }
105
 
106
- .joystick-thumb { /* Просто визуальный индикатор центра */
107
- width: 50px;
108
- height: 50px;
109
- background-color: rgba(200, 200, 200, 0.6);
110
- border-radius: 50%;
111
- }
112
-
113
- .action-button {
114
- width: 80px;
115
- height: 80px;
116
- background-color: rgba(0, 150, 255, 0.6);
117
- border: 2px solid rgba(255, 255, 255, 0.7);
118
- border-radius: 50%;
119
- display: flex;
120
- justify-content: center;
121
- align-items: center;
122
- font-size: 1.1em;
123
- font-weight: bold;
124
- color: #fff;
125
- cursor: pointer;
126
- pointer-events: auto; /* Кнопка должна быть интерактивна */
127
- user-select: none; /* Предотвратить выделение текста на кнопке */
128
- -webkit-user-select: none;
129
- -moz-user-select: none;
130
- -ms-user-select: none;
131
- }
132
-
133
- /* Показываем мобильные контролы только на тач-устройствах (простая проверка) */
134
- body.is-mobile .mobile-controls {
135
- display: flex;
136
  }
137
 
138
  </style>
139
  </head>
140
  <body>
141
-
142
- <canvas id="renderCanvas"></canvas>
143
-
144
  <div id="loadingScreen">
145
- <div>Загрузка метавселенной...</div>
 
146
  </div>
147
 
148
- <div id="ui-overlay">
149
- <h1>Metaverse Demo</h1>
150
- <p>Используйте WASD/Стрелки + Мышь или Тач</p>
151
- </div>
152
 
153
- <!-- Визуальные элементы для мобильного управления -->
154
- <div class="mobile-controls">
155
- <div id="joystickArea" class="joystick-area">
156
- <div class="joystick-thumb"></div>
157
- </div>
158
- <div id="actionButton" class="action-button">
159
- Действие
160
- </div>
161
  </div>
162
 
163
  <script>
164
- const canvas = document.getElementById('renderCanvas');
165
- const loadingScreen = document.getElementById('loadingScreen');
166
- const mobileControls = document.querySelector('.mobile-controls');
167
- const actionButton = document.getElementById('actionButton');
168
-
169
- // Простая проверка на тач-устройство
170
- const isMobile = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
171
- if (isMobile) {
172
- document.body.classList.add('is-mobile');
173
- }
174
 
175
- // --- Настройка Babylon.js ---
176
  const engine = new BABYLON.Engine(canvas, true, {
177
  preserveDrawingBuffer: true,
178
  stencil: true,
179
- antialias: true // Включаем сглаживание
180
  });
181
- engine.displayLoadingUI = function() { /* Отключаем стандартный загрузчик Babylon */ };
182
 
 
183
  let scene;
 
 
 
 
 
184
 
185
- // --- Создание сцены ---
186
  const createScene = async function () {
187
  scene = new BABYLON.Scene(engine);
188
- scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.2, 1.0); // Цвет фона, если скайбокс не загрузится
189
 
190
  // --- Камера ---
191
- // UniversalCamera подходит для всех типов ввода (клавиатура, мышь, тач, геймпад)
192
- const camera = new BABYLON.UniversalCamera("UniversalCamera", new BABYLON.Vector3(0, 1.8, -10), scene);
193
- camera.setTarget(new BABYLON.Vector3(0, 1, 0));
194
- camera.attachControl(canvas, true);
195
-
196
- // Параметры движения камеры
197
- camera.speed = 0.2; // Скорость движения
198
- camera.angularSensibility = 4000; // Чувствительность мыши/тачпада
199
- camera.touchAngularSensibility = 4000; // Чувствительность тач-поворота
200
- camera.touchMoveSensibility = 250.0; // Чувствительность тач-перемещения (для джойстика лучше другая логика)
201
-
202
- // Включаем гравитацию и коллизии для камеры
203
- camera.applyGravity = true;
204
- camera.ellipsoid = new BABYLON.Vector3(0.5, 0.9, 0.5); // Размеры "капсулы" игрока для коллизий
205
- scene.gravity = new BABYLON.Vector3(0, -0.981, 0); // Стандартная гравитация
206
- scene.collisionsEnabled = true; // Включаем систему коллизий в сцене
207
- camera.checkCollisions = true; // Камера будет проверять коллизии
208
 
209
  // --- Освещение ---
210
- // HemisphericLight - простое общее освещение (сверху/снизу)
211
  const light = new BABYLON.HemisphericLight("hemiLight", new BABYLON.Vector3(0, 1, 0), scene);
212
- light.intensity = 0.8;
213
- light.diffuse = new BABYLON.Color3(1, 1, 1); // Белый свет сверху
214
- light.groundColor = new BABYLON.Color3(0.4, 0.4, 0.4); // Серый свет снизу
 
215
 
216
- // DirectionalLight - имитация солнца (дает тени)
217
  const dirLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(-0.5, -1, -0.5), scene);
218
- dirLight.intensity = 0.6;
219
  dirLight.position = new BABYLON.Vector3(20, 40, 20);
220
-
221
- // --- Тени (могут быть ресурсоемкими, особенно на мобильных) ---
222
- const shadowGenerator = new BABYLON.ShadowGenerator(1024, dirLight); // Размер карты теней
223
- shadowGenerator.useBlurExponentialShadowMap = true; // Более мягкие тени
224
- shadowGenerator.blurKernel = 32;
225
- shadowGenerator.setDarkness(0.5); // Насколько темные тени
226
-
227
- // --- Окружение: Скайбокс (Небо) ---
228
- const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: 1000.0 }, scene);
229
- const skyboxMaterial = new BABYLON.StandardMaterial("skyBoxMat", scene);
230
- skyboxMaterial.backFaceCulling = false; // Видеть текстуру изнутри куба
231
- // Используем процедурную текстуру неба для простоты (не нужны внешние файлы)
232
- // Либо можно использовать CubeTexture с внешними изображениями:
233
- // skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("URL_TO_TEXTURES/skybox", scene);
234
- // skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
235
- const skyMaterial = new BABYLON.SkyMaterial("skyMaterial", scene);
236
- skyMaterial.backFaceCulling = false;
237
- skyMaterial.turbidity = 10; // облачность
238
- skyMaterial.luminance = 1; // яркость
239
- skyMaterial.useSunPosition = true; // Использовать позицию "солнца"
240
- skyMaterial.sunPosition = dirLight.position; // Установить позицию солнца как у направленного света
241
- skybox.material = skyMaterial;
242
-
243
-
244
- // --- Окружение: Земля ---
245
- const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 100, height: 100, subdivisions: 2 }, scene);
246
  const groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
247
- // Простая текстура (можно заменить на URL к изображению)
248
- // groundMaterial.diffuseTexture = new BABYLON.Texture("URL_TO_GROUND_TEXTURE.jpg", scene);
249
- // groundMaterial.diffuseTexture.uScale = 10; // Повторение текстуры
250
- // groundMaterial.diffuseTexture.vScale = 10;
251
- groundMaterial.diffuseColor = new BABYLON.Color3(0.5, 0.7, 0.3); // Зеленоватый цвет
252
- groundMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1); // Небольшой блик
253
  ground.material = groundMaterial;
254
- ground.checkCollisions = true; // Земля участвует в коллизиях
255
- ground.receiveShadows = true; // Земля может принимать тени
256
 
257
- // --- Добавим несколько объектов в мир ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
  // Сфера
260
- const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 2 }, scene);
261
- sphere.position = new BABYLON.Vector3(5, 1, 5);
262
- const sphereMaterial = new BABYLON.StandardMaterial("sphereMat", scene);
263
- sphereMaterial.diffuseColor = new BABYLON.Color3(1, 0.5, 0); // Оранжевый
264
- sphere.material = sphereMaterial;
265
  sphere.checkCollisions = true;
 
 
 
 
266
  shadowGenerator.addShadowCaster(sphere); // Сфера отбрасывает тень
267
 
268
- // Куб
269
- const box = BABYLON.MeshBuilder.CreateBox("box", { size: 2 }, scene);
270
- box.position = new BABYLON.Vector3(-5, 1, -3);
271
- const boxMaterial = new BABYLON.StandardMaterial("boxMat", scene);
272
- boxMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.5, 1); // Синий
273
- box.material = boxMaterial;
274
- box.checkCollisions = true;
275
- shadowGenerator.addShadowCaster(box);
276
-
277
- // "Здание" (просто вытянутый куб)
278
- const building = BABYLON.MeshBuilder.CreateBox("building", { width: 5, height: 10, depth: 5 }, scene);
279
- building.position = new BABYLON.Vector3(10, 5, -10);
280
- const buildingMaterial = new BABYLON.StandardMaterial("buildingMat", scene);
281
- buildingMaterial.diffuseColor = new BABYLON.Color3(0.8, 0.8, 0.8); // Светло-серый
282
- building.material = buildingMaterial;
283
- building.checkCollisions = true;
284
- shadowGenerator.addShadowCaster(building);
285
-
286
-
287
- // --- Интерактивность (Пример: клик по сфере) ---
288
- sphere.actionManager = new BABYLON.ActionManager(scene);
289
- sphere.actionManager.registerAction(
290
- new BABYLON.ExecuteCodeAction(
291
- BABYLON.ActionManager.OnPickTrigger, // Действие при клике/тапе
292
- function (evt) {
293
- console.log("Клик по сфере!");
294
- // Пример действия: изменить цвет
295
- sphereMaterial.diffuseColor = sphereMaterial.diffuseColor.equals(BABYLON.Color3.Red())
296
- ? new BABYLON.Color3(1, 0.5, 0) // Вернуть оранжевый
297
- : BABYLON.Color3.Red(); // Сделать красным
298
- }
299
- )
300
- );
 
 
301
 
302
- // --- Пост-обработка (для "флагманской" графики) ---
303
- // DefaultRenderingPipeline применяет несколько эффектов сразу
304
- const pipeline = new BABYLON.DefaultRenderingPipeline(
305
  "defaultPipeline", // Имя
306
- true, // Использовать HDR текстуры (лучше качество)
307
  scene, // Сцена
308
  [camera] // Камеры, к которым применяется
309
  );
 
 
 
 
 
 
310
 
311
- // Настройки эффектов (можно настроить или отключить)
312
- pipeline.samples = 4; // Сглаживание MSAA (если поддерживается)
 
 
313
 
314
- pipeline.bloomEnabled = true; // Эффект свечения (Bloom)
315
- pipeline.bloomThreshold = 0.7;
316
- pipeline.bloomWeight = 0.5;
317
- pipeline.bloomKernel = 64;
318
- pipeline.bloomScale = 0.5;
319
 
320
- pipeline.fxaaEnabled = true; // Сглаживание FXAA (быстрее MSAA)
 
321
 
322
- // pipeline.imageProcessingEnabled = true; // Общая обработка изображения
323
- // if (pipeline.imageProcessingEnabled) {
324
- // pipeline.imageProcessing.contrast = 1.2;
325
- // pipeline.imageProcessing.exposure = 1.0;
326
- // // Можно добавить виньетку, зернистость и т.д.
327
- // }
328
 
329
  return scene;
330
  };
331
 
332
- // --- Запуск ---
333
- createScene().then((createdScene) => {
334
- scene = createdScene;
335
- // Убираем экран загрузки после инициализации сцены
336
- engine.hideLoadingUI();
337
- loadingScreen.classList.add('hidden');
338
-
339
- // Основной цикл рендеринга
340
- engine.runRenderLoop(function () {
341
- if (scene) {
342
- scene.render();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  }
 
 
 
 
 
344
  });
345
- }).catch(error => {
346
- console.error("Ошибка создания сцены:", error);
347
- loadingScreen.innerHTML = "<div>Ошибка загрузки :(</div>";
348
- });
349
 
 
 
350
 
351
- // --- Обработка изменения размера окна ---
352
- window.addEventListener('resize', function () {
353
- engine.resize();
354
- });
 
 
 
 
355
 
356
- // --- Обработка мобильной кнопки "Действие" (пример) ---
357
- if (isMobile && actionButton) {
358
- actionButton.addEventListener('pointerdown', function(event) {
359
- event.preventDefault(); // Предотвратить стандартное поведение (например, прокрутку)
360
- console.log("Нажата кнопка Действие!");
361
- // Здесь можно добавить логику действия:
362
- // Например, эмулировать взаимодействие с объектом перед камерой
363
- // или выполнить другое игровое действие.
364
-
365
- // Визуальный фидбек нажатия
366
- actionButton.style.backgroundColor = 'rgba(0, 100, 200, 0.8)';
367
  });
368
- actionButton.addEventListener('pointerup', function(event) {
369
- actionButton.style.backgroundColor = 'rgba(0, 150, 255, 0.6)';
370
- });
371
- actionButton.addEventListener('pointerleave', function(event) { // Если палец ушел с кнопки
372
- actionButton.style.backgroundColor = 'rgba(0, 150, 255, 0.6)';
 
 
 
373
  });
 
 
 
 
 
 
374
  }
375
 
376
- // --- Дополнительно: Логика для виртуального джойстика (Упрощенная) ---
377
- // UniversalCamera уже обрабатывает перетаскивание по экрану для движения/поворота.
378
- // Этот код просто показывает визуальные элементы.
379
- // Для полноценного джойстика потребовалась бы сложная логика отслеживания
380
- // пальца в пределах joystickArea и преобразования смещения в векторы движения камеры.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
  </script>
383
 
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>Babylon.js Metaverse Demo</title>
7
+ <meta name="description" content="Демонстрация базовой метавселенной, созданной с помощью Babylon.js. Включает 3D-мир, управление персонажем и элементы для мобильных устройств.">
 
8
  <!-- Подключение Babylon.js -->
9
  <script src="https://cdn.babylonjs.com/babylon.js"></script>
10
+ <!-- (Опционально) Загрузчики для моделей (например, glTF) -->
11
  <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
12
+ <!-- (Опционально) GUI для элементов интерфейса -->
13
+ <script src="https://cdn.babylonjs.com/gui/babylon.gui.min.js"></script>
 
 
 
14
 
15
  <style>
16
  html, body {
 
20
  margin: 0;
21
  padding: 0;
22
  font-family: Arial, sans-serif;
23
+ color: #eee; /* Светлый текст для темного фона */
24
+ background-color: #111; /* Темный фон по умолчанию */
25
  }
26
 
27
  #renderCanvas {
28
  width: 100%;
29
  height: 100%;
30
+ touch-action: none; /* Отключает стандартные жесты браузера (важно для управления) */
31
+ outline: none; /* Убирает рамку при фокусе */
32
  }
33
 
34
+ /* Стили для экрана загрузки */
35
  #loadingScreen {
36
  position: absolute;
37
  top: 0;
 
39
  width: 100%;
40
  height: 100%;
41
  background-color: #1a1a1a;
42
+ color: #ffffff;
43
  display: flex;
44
+ flex-direction: column;
45
  justify-content: center;
46
  align-items: center;
47
+ font-size: 1.5em;
 
48
  z-index: 100;
49
  transition: opacity 0.5s ease-out;
50
  }
51
 
52
  #loadingScreen.hidden {
53
  opacity: 0;
54
+ pointer-events: none; /* Чтобы не мешал кликам после скрытия */
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
 
57
+ #loadingSpinner {
58
+ border: 4px solid #f3f3f3;
59
+ border-top: 4px solid #3498db;
60
+ border-radius: 50%;
61
+ width: 40px;
62
+ height: 40px;
63
+ animation: spin 1s linear infinite;
64
+ margin-top: 20px;
65
  }
66
 
67
+ @keyframes spin {
68
+ 0% { transform: rotate(0deg); }
69
+ 100% { transform: rotate(360deg); }
70
  }
71
 
72
+ /* Стили для виртуального джойстика (для мобильных) */
73
+ #joystickContainer {
74
  position: absolute;
75
  bottom: 20px;
76
+ left: 20px;
77
+ width: 150px; /* Размер области джойстика */
78
+ height: 150px;
79
+ /* background-color: rgba(80, 80, 80, 0.3); */ /* Полупрозрачный фон для отладки */
80
+ /* border-radius: 50%; */
 
81
  z-index: 10;
82
+ display: none; /* По умолчанию скрыт, показываем через JS при необходимости */
83
+ pointer-events: none; /* Контейнер не ловит события, ловит сам джойстик GUI */
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
 
86
+ /* Можно добавить стили для других элементов GUI здесь */
87
+
88
+ /* Простое уведомление */
89
+ #infoBox {
90
+ position: absolute;
91
+ top: 10px;
92
+ left: 10px;
93
+ background-color: rgba(0,0,0,0.5);
94
+ color: white;
95
+ padding: 8px;
96
+ border-radius: 5px;
97
+ font-size: 0.9em;
98
+ z-index: 5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  }
100
 
101
  </style>
102
  </head>
103
  <body>
104
+ <!-- Экран загрузки -->
 
 
105
  <div id="loadingScreen">
106
+ <p>Загрузка метавселенной...</p>
107
+ <div id="loadingSpinner"></div>
108
  </div>
109
 
110
+ <!-- Основной Canvas для рендеринга -->
111
+ <canvas id="renderCanvas"></canvas>
 
 
112
 
113
+ <!-- Контейнер для джойстика (позиционирование) -->
114
+ <div id="joystickContainer"></div>
115
+
116
+ <!-- Информационный блок -->
117
+ <div id="infoBox">
118
+ Управление: WASD/Стрелки + Мышь (ПК) | Джойстик + Касание (Мобильные)
 
 
119
  </div>
120
 
121
  <script>
122
+ // Получаем элементы DOM
123
+ const canvas = document.getElementById("renderCanvas");
124
+ const loadingScreen = document.getElementById("loadingScreen");
125
+ const joystickContainer = document.getElementById("joystickContainer"); // Хотя GUI не использует его напрямую, он полезен для понимания области
 
 
 
 
 
 
126
 
127
+ // Инициализация движка Babylon.js
128
  const engine = new BABYLON.Engine(canvas, true, {
129
  preserveDrawingBuffer: true,
130
  stencil: true,
131
+ antialias: true // Включаем сглаживание для лучшей графики
132
  });
 
133
 
134
+ // Глобальные переменные для сцены, камеры и управления
135
  let scene;
136
+ let camera;
137
+ let inputMap = {}; // Для отслеживания нажатых клавиш
138
+ let moveSpeed = 0.15; // Скорость движения
139
+ let cameraSensitivity = 0.003; // Чувствительность мыши/касания
140
+ let virtualJoystick = null; // Для мобильного управления
141
 
142
+ // --- Функция создания сцены ---
143
  const createScene = async function () {
144
  scene = new BABYLON.Scene(engine);
145
+ scene.clearColor = new BABYLON.Color3.FromHexString("#2c3e50"); // Цвет фона (небо)
146
 
147
  // --- Камера ---
148
+ // Универсальная камера подходит для ПК (WASD/Мышь) и имеет настройки для Touch
149
+ camera = new BABYLON.UniversalCamera("playerCamera", new BABYLON.Vector3(0, 2, -10), scene);
150
+ camera.attachControl(canvas, true); // Прикрепляем управление к canvas
151
+ camera.speed = 0; // Отключаем встроенное движение WASD, будем управлять вручную
152
+ camera.angularSensibility = 8000 * cameraSensitivity; // Настройка чувствительности вращения
153
+
154
+ // Настройки для камеры от первого лица
155
+ camera.ellipsoid = new BABYLON.Vector3(0.5, 0.9, 0.5); // Размер "тела" игрока для коллизий
156
+ camera.checkCollisions = true; // Включаем проверку коллизий для камеры
157
+ camera.applyGravity = true; // Включаем гравитацию
158
+ camera.minZ = 0.1; // Ближняя плоскость отсечения
 
 
 
 
 
 
159
 
160
  // --- Освещение ---
161
+ // Мягкий окружающий свет
162
  const light = new BABYLON.HemisphericLight("hemiLight", new BABYLON.Vector3(0, 1, 0), scene);
163
+ light.intensity = 0.7;
164
+ light.diffuse = new BABYLON.Color3(1, 1, 1);
165
+ light.specular = new BABYLON.Color3(0.5, 0.5, 0.5);
166
+ light.groundColor = new BABYLON.Color3(0.3, 0.3, 0.3);
167
 
168
+ // Направленный свет (сол��це) для теней
169
  const dirLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(-0.5, -1, -0.5), scene);
 
170
  dirLight.position = new BABYLON.Vector3(20, 40, 20);
171
+ dirLight.intensity = 0.8;
172
+
173
+ // --- Тени ---
174
+ // Генератор теней высокого качества (можно настроить для производительности)
175
+ const shadowGenerator = new BABYLON.ShadowGenerator(2048, dirLight); // 2048 - размер карты теней
176
+ shadowGenerator.useExponentialShadowMap = true; // Мягкие тени
177
+ // shadowGenerator.useBlurExponentialShadowMap = true; // Еще мягче, но дороже
178
+ // shadowGenerator.blurKernel = 32;
179
+ shadowGenerator.darkness = 0.5;
180
+
181
+ // --- Мир (Окружение) ---
182
+ // Земля
183
+ const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 100, height: 100 }, scene);
184
+ ground.checkCollisions = true; // Земля участвует в коллизиях
 
 
 
 
 
 
 
 
 
 
 
 
185
  const groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
186
+ // Простая текстура для земли (можно заменить на PBR материал и текстуры для "флагманской" графики)
187
+ const groundTexture = new BABYLON.Texture("https://www.babylonjs-playground.com/textures/grass.png", scene); // Пример текстуры
188
+ groundTexture.uScale = 20;
189
+ groundTexture.vScale = 20;
190
+ groundMaterial.diffuseTexture = groundTexture;
191
+ groundMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1); // Меньше бликов на траве
192
  ground.material = groundMaterial;
193
+ ground.receiveShadows = true; // Земля принимает тени
 
194
 
195
+ // Небо (Skybox) для атмосферы
196
+ const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: 1000.0 }, scene);
197
+ const skyboxMaterial = new BABYLON.StandardMaterial("skyBoxMat", scene);
198
+ skyboxMaterial.backFaceCulling = false; // Рендерим внутренние грани куба
199
+ // Текстура неба (ищите "cube texture" или "skybox texture")
200
+ skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("https://www.babylonjs-playground.com/textures/skybox", scene);
201
+ skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
202
+ skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
203
+ skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
204
+ skybox.material = skyboxMaterial;
205
+
206
+ // --- Добавляем объекты в мир ---
207
+ // Коробка
208
+ const box = BABYLON.MeshBuilder.CreateBox("box", { size: 2 }, scene);
209
+ box.position = new BABYLON.Vector3(5, 1, 5);
210
+ box.checkCollisions = true;
211
+ const boxMat = new BABYLON.StandardMaterial("boxMat", scene);
212
+ boxMat.diffuseColor = new BABYLON.Color3.FromHexString("#e74c3c"); // Красный
213
+ box.material = boxMat;
214
+ shadowGenerator.addShadowCaster(box); // Коробка отбрасывает тень
215
 
216
  // Сфера
217
+ const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 3 }, scene);
218
+ sphere.position = new BABYLON.Vector3(-5, 1.5, -5);
 
 
 
219
  sphere.checkCollisions = true;
220
+ const sphereMat = new BABYLON.StandardMaterial("sphereMat", scene);
221
+ sphereMat.diffuseColor = new BABYLON.Color3.FromHexString("#3498db"); // Синий
222
+ sphereMat.specularPower = 64; // Блик
223
+ sphere.material = sphereMat;
224
  shadowGenerator.addShadowCaster(sphere); // Сфера отбрасывает тень
225
 
226
+ // Колонна (цилиндр)
227
+ const cylinder = BABYLON.MeshBuilder.CreateCylinder("cylinder", {height: 6, diameter: 1}, scene);
228
+ cylinder.position = new BABYLON.Vector3(0, 3, 8);
229
+ cylinder.checkCollisions = true;
230
+ const cylMat = new BABYLON.StandardMaterial("cylMat", scene);
231
+ cylMat.diffuseTexture = new BABYLON.Texture("https://www.babylonjs-playground.com/textures/rock.jpg", scene); // Текстура камня
232
+ cylinder.material = cylMat;
233
+ shadowGenerator.addShadowCaster(cylinder);
234
+
235
+ // Пример использования PBR материала для более реалистичного вида
236
+ const pbrSphere = BABYLON.MeshBuilder.CreateSphere("pbrSphere", {diameter: 2.5}, scene);
237
+ pbrSphere.position = new BABYLON.Vector3(-8, 1.25, 4);
238
+ const pbrMat = new BABYLON.PBRMaterial("pbrMat", scene);
239
+ pbrMat.metallic = 1.0; // Металлический
240
+ pbrMat.roughness = 0.2; // Относительно гладкий (отражающий)
241
+ pbrMat.albedoColor = new BABYLON.Color3(0.8, 0.8, 0.85);
242
+ pbrMat.reflectionTexture = skyboxMaterial.reflectionTexture; // Используем скайбокс для отражений
243
+ pbrMat.enableSpecularAntiAliasing = true; // Улучшение отражений
244
+ pbrSphere.material = pbrMat;
245
+ pbrSphere.checkCollisions = true;
246
+ shadowGenerator.addShadowCaster(pbrSphere);
247
+
248
+
249
+ // --- Физика ---
250
+ scene.gravity = new BABYLON.Vector3(0, -9.81 / 60, 0); // Применяем гравитацию (делим на 60 для согласования с частотой кадров)
251
+ scene.collisionsEnabled = true; // Включаем систему коллизий в сцене
252
+
253
+ // --- Управление ---
254
+ setupInputHandling();
255
+
256
+ // --- Мобильное управление (Виртуальный джойстик) ---
257
+ if (isMobileDevice()) {
258
+ setupVirtualJoystick();
259
+ joystickContainer.style.display = 'block'; // Показываем область джойстика
260
+ }
261
 
262
+ // --- Пост-обработка (для "флагманской" графики) ---
263
+ // Свечение (Bloom)
264
+ const pipeline = new BABYLON.DefaultRenderingPipeline(
265
  "defaultPipeline", // Имя
266
+ true, // Использовать HDR текстуры (лучше для bloom)
267
  scene, // Сцена
268
  [camera] // Камеры, к которым применяется
269
  );
270
+ pipeline.samples = 4; // Сглаживание для пост-эффектов
271
+ pipeline.bloomEnabled = true;
272
+ pipeline.bloomThreshold = 0.8; // Порог яркости для свечения
273
+ pipeline.bloomWeight = 0.5; // Интенсивность свечения
274
+ pipeline.bloomKernel = 64; // Размер размытия для свечения
275
+ pipeline.bloomScale = 0.5; // Масштаб текстуры свечения
276
 
277
+ // (Опционально) Ambient Occlusion для лучшего затенения в углах
278
+ // pipeline.ssaoEnabled = true;
279
+ // pipeline.ssaoRatio = 0.5; // Настройки SSAO (могут влиять на производительность)
280
+ // pipeline.ssaoBlurRatio = 0.5;
281
 
 
 
 
 
 
282
 
283
+ // --- Оптимизация ---
284
+ // scene.freezeActiveMeshes(); // Полезно для статичных сцен, но не для динамичных
285
 
286
+ // Действия после полной загрузки сцены
287
+ await scene.whenReadyAsync();
288
+ loadingScreen.classList.add('hidden'); // Скрываем экран загрузки
289
+ console.log("Сцена готова!");
 
 
290
 
291
  return scene;
292
  };
293
 
294
+ // --- Функция настройки обработки ввода (Клавиатура) ---
295
+ function setupInputHandling() {
296
+ scene.actionManager = new BABYLON.ActionManager(scene);
297
+
298
+ // Регистрация нажатия клавиши
299
+ scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyDownTrigger, function (evt) {
300
+ inputMap[evt.sourceEvent.key.toLowerCase()] = true; // Записываем нажатую клавишу
301
+ }));
302
+
303
+ // Регистрация отпускания клавиши
304
+ scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyUpTrigger, function (evt) {
305
+ inputMap[evt.sourceEvent.key.toLowerCase()] = false; // Удаляем клавишу
306
+ }));
307
+
308
+ // Управление вращением камеры мышью/касанием (обрабатывается UniversalCamera)
309
+ // Дополнительно можно настроить чувствительность и т.д.
310
+ }
311
+
312
+ // --- Функция настройки виртуального джойстика ---
313
+ function setupVirtualJoystick() {
314
+ const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
315
+
316
+ virtualJoystick = new BABYLON.GUI.VirtualJoystick(true); // true = left handed joystick
317
+ virtualJoystick.setJoystickSensibility(2.5); // Чувствительность джойстика
318
+
319
+ // Настройка внешнего вида джойстика
320
+ virtualJoystick.joystickArea.width = "150px";
321
+ virtualJoystick.joystickArea.height = "150px";
322
+ virtualJoystick.joystickArea.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
323
+ virtualJoystick.joystickArea.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
324
+ virtualJoystick.joystickArea.left = "20px"; // Отступ слева
325
+ virtualJoystick.joystickArea.top = "-20px"; // Отступ снизу (отрицательный top при bottom-выравнивании)
326
+ virtualJoystick.joystickArea.background = "rgba(100, 100, 100, 0.4)";
327
+ virtualJoystick.joystickArea.alpha = 0.7;
328
+ virtualJoystick.joystickArea.cornerRadius = 75; // Круглый
329
+
330
+ virtualJoystick.joystickThumb.width = "60px";
331
+ virtualJoystick.joystickThumb.height = "60px";
332
+ virtualJoystick.joystickThumb.background = "rgba(200, 200, 200, 0.6)";
333
+ virtualJoystick.joystickThumb.color = "white"; // Цвет иконки (если есть)
334
+ virtualJoystick.joystickThumb.alpha = 0.8;
335
+ virtualJoystick.joystickThumb.cornerRadius = 30; // Круглый
336
+
337
+ // Скрытие стандартного контейнера джойстика (мы используем свой div для позиционирования)
338
+ virtualJoystick.joystickContainer.isVisible = false;
339
+
340
+ advancedTexture.addControl(virtualJoystick);
341
+
342
+ // Обработка касаний для вращения камеры на мобильных
343
+ let isPointerDown = false;
344
+ let startX = 0;
345
+ let startY = 0;
346
+
347
+ canvas.addEventListener("pointerdown", (evt) => {
348
+ // Не обрабатываем клики по джойстику как вращение
349
+ if (virtualJoystick.joystickArea.contains(evt.clientX, evt.clientY)) {
350
+ return;
351
  }
352
+ isPointerDown = true;
353
+ startX = evt.clientX;
354
+ startY = evt.clientY;
355
+ // Предотвращаем выделение текста/элементов при движении пальца
356
+ evt.preventDefault();
357
  });
 
 
 
 
358
 
359
+ canvas.addEventListener("pointermove", (evt) => {
360
+ if (!isPointerDown) return;
361
 
362
+ const deltaX = (evt.clientX - startX) * cameraSensitivity;
363
+ const deltaY = (evt.clientY - startY) * cameraSensitivity;
364
+
365
+ // Вращаем камеру
366
+ // В UniversalCamera нужно использовать setTarget или вращать вручную
367
+ // Простой способ - имитировать движение мыши
368
+ camera.cameraRotation.y += deltaX;
369
+ camera.cameraRotation.x += deltaY;
370
 
371
+ // Ограничиваем вертикальное вращение
372
+ camera.cameraRotation.x = Math.max(-Math.PI / 2.1, Math.min(Math.PI / 2.1, camera.cameraRotation.x));
373
+
374
+
375
+ startX = evt.clientX;
376
+ startY = evt.clientY;
377
+ evt.preventDefault();
 
 
 
 
378
  });
379
+
380
+ canvas.addEventListener("pointerup", (evt) => {
381
+ isPointerDown = false;
382
+ evt.preventDefault();
383
+ });
384
+ canvas.addEventListener("pointerout", (evt) => { // На случай, если палец ушел за пределы canvas
385
+ isPointerDown = false;
386
+ evt.preventDefault();
387
  });
388
+
389
+ }
390
+
391
+ // --- Функция проверки, мобильное ли устройство ---
392
+ function isMobileDevice() {
393
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
394
  }
395
 
396
+
397
+ // --- Основной цикл рендеринга ---
398
+ engine.runRenderLoop(function () {
399
+ if (!scene || !scene.isReady()) {
400
+ return; // Если сцена не готова, ничего не делаем
401
+ }
402
+
403
+ // --- Обновление движения игрока ---
404
+ const forward = new BABYLON.Vector3(
405
+ Math.sin(camera.rotation.y) * moveSpeed,
406
+ 0, // Мы не двигаемся вверх/вниз клавишами напрямую (гравитация делает свое дело)
407
+ Math.cos(camera.rotation.y) * moveSpeed
408
+ );
409
+ const right = new BABYLON.Vector3(
410
+ Math.sin(camera.rotation.y + Math.PI / 2) * moveSpeed,
411
+ 0,
412
+ Math.cos(camera.rotation.y + Math.PI / 2) * moveSpeed
413
+ );
414
+ const moveDirection = BABYLON.Vector3.Zero();
415
+
416
+ // Клавиатура
417
+ if (inputMap["w"] || inputMap["arrowup"]) {
418
+ moveDirection.addInPlace(forward);
419
+ }
420
+ if (inputMap["s"] || inputMap["arrowdown"]) {
421
+ moveDirection.addInPlace(forward.negate()); // Движение назад
422
+ }
423
+ if (inputMap["a"] || inputMap["arrowleft"]) {
424
+ moveDirection.addInPlace(right.negate()); // Движение влево
425
+ }
426
+ if (inputMap["d"] || inputMap["arrowright"]) {
427
+ moveDirection.addInPlace(right); // Движение вправо
428
+ }
429
+
430
+ // Джойстик (если есть)
431
+ if (virtualJoystick && virtualJoystick.pressed) {
432
+ // Добавляем вектор от джойстика к общему вектору движения
433
+ const joystickForward = forward.scale(virtualJoystick.deltaPosition.y);
434
+ const joystickRight = right.scale(virtualJoystick.deltaPosition.x);
435
+ moveDirection.addInPlace(joystickForward);
436
+ moveDirection.addInPlace(joystickRight);
437
+ }
438
+
439
+ // Применяем движение к камере
440
+ if (moveDirection.lengthSquared() > 0) {
441
+ // Нормализуем и масштабируем, чтобы диагональное движение не было быстрее
442
+ // moveDirection.normalize().scaleInPlace(moveSpeed); - Не нужно, уже учтено в forward/right
443
+ camera.moveWithCollisions(moveDirection);
444
+ }
445
+
446
+
447
+ // Рендерим сцену
448
+ scene.render();
449
+ });
450
+
451
+ // --- Обработчик изменения размера окна ---
452
+ window.addEventListener("resize", function () {
453
+ engine.resize();
454
+ });
455
+
456
+ // --- Запуск создания сцены ---
457
+ createScene().then(() => {
458
+ // Сцена успешно создана
459
+ }).catch(error => {
460
+ console.error("Ошибка при создании сцены:", error);
461
+ loadingScreen.innerHTML = "<p>Ошибка загрузки. Пожалуйста, обновите страницу.</p>";
462
+ });
463
 
464
  </script>
465