Title: Three.js Отладка JavaScript Description: Отладка JavaScript с THREE.js TOC: Отладка JavaScript Большая часть этой статьи посвящена не непосредственно THREE.js, а скорее об отладке JavaScript в целом. Мне показалось важным, что многие люди, начинающие с THREE.js, также начинают с JavaScript, поэтому я надеюсь, что это поможет им легче решать любые возникающие проблемы. Отладка - большая тема, и я, вероятно, не смогу охватить все, что нужно знать, но если вы новичок в JavaScript, вот несколько советов. Я настоятельно рекомендую вам потратить некоторое время на их изучение. Они очень помогут вам в вашем обучении. ## Изучите инструменты разработчика вашего браузера Все браузеры имеют инструменты разработчика. [Chrome](https://developers.google.com/web/tools/chrome-devtools/), [Firefox](https://developer.mozilla.org/en-US/docs/Tools), [Safari](https://developer.apple.com/safari/tools/), [Edge](https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide). В Chrome вы можете кликнуть значок `⋮` , выбрать More Tools->Developer Tools чтобы перейти к инструментам разработчика. Сочетание клавиш также показано там.
В Firefox вы кликаете значок `☰` , выбираете "Web Developer", затем выбираете "Toggle Tools"
В Safari сначала необходимо включить меню «Разработка» в разделе «Дополнительные настройки Safari».
Затем в меню «Разработка» вы можете выбрать «Показать / подключить веб-инспектора».
С Chrome вы также можете [использовать Chrome на своем компьютере для отладки веб-страниц, работающих на Chrome, на вашем телефоне или планшете Android](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/). Точно так же с Safari вы можете [использовать свой компьютер для отладки веб-страниц, работающих в Safari на iPhone и iPad](https://www.google.com/search?q=safari+remote+debugging+ios). Я наиболее знаком с Chrome, поэтому в этом руководстве будет использоваться Chrome в качестве примера при обращении к инструментам, но большинство браузеров имеют схожие функции, поэтому здесь должно быть легко применить что-либо для всех браузеров. ## Выключить кеш Браузеры пытаются повторно использовать уже загруженные данные. Это очень удобно для пользователей, поэтому, если вы заходите на веб-сайт во второй раз, многие файлы, используемые для отображения сайта, больше не будут загружаться. С другой стороны, это может быть плохо для веб-разработки. Вы изменяете файл на своем компьютере, перезагружаете страницу и не видите изменений, потому что браузер использует версию, полученную в прошлый раз. С другой стороны, это может быть плохо для веб-разработки. Вы изменяете файл на своем компьютере, перезагружаете страницу и не видите изменений, потому что браузер использует версию, полученную в прошлый раз. Сначала выберите настройки из углового меню
Затем выберите "Отключить кэш (пока открыт DevTools)".
## Используйте консоль JavaScript Внутри всех devtools есть *консоль*. Показывает предупреждения и сообщения об ошибках. ** ЧИТАЙТЕ СООБЩЕНИЯ!! ** Обычно должно быть только 1 или 2 сообщения.
Если вы видите какие-либо другие **ПРОЧИТАЙТЕ ИХ**. Например:
Я неправильно написал «three» как «threee» Вы также можете распечатать свою собственную информацию на консоли с помощью `console.log`, как в ```js console.log(someObject.position.x, someObject.position.y, someObject.position.z); ``` Даже круче, если вы регистрируете объект, вы можете инспектировать его. Например, если мы регистрируем объект корневой сцены из [статьи gLTF](threejs-load-gltf.html) ```js { const gltfLoader = new GLTFLoader(); gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => { const root = gltf.scene; scene.add(root); + console.log(root); ``` Затем мы можем развернуть этот объект в консоли JavaScript
Вы также можете использовать `console.error`, который сообщает, что сообщение красным цветом включает в себя трассировку стека. ## Поместить данные на экран Другой очевидный, но часто упускаемый из виду способ - добавить теги `
` или `
` и поместить в них данные. 

Самый очевидный способ - сделать некоторые элементы HTML 

```html

+
+
x:
+
y:
+
z:
+
``` Разместите стиле вверху холста. (при условии, что ваш холст заполняет страницу) ```html ``` А затем найдите элементы и настройте их содержимое. ```js // at init time const xElem = document.querySelector('#x'); const yElem = document.querySelector('#y'); const zElem = document.querySelector('#z'); // at render or update time xElem.textContent = someObject.position.x.toFixed(3); yElem.textContent = someObject.position.y.toFixed(3); zElem.textContent = someObject.position.z.toFixed(3); ``` Это более полезно для значений в реальном времени {{{example url="../threejs-debug-js-html-elements.html" }}} Еще один способ вывести данные на экран - это очистить регистратор. Я только что придумал этот термин, но многие игры, над которыми я работал, использовали это решение. Идея в том, что у вас есть буфер, который отображает сообщения только для одного кадра. Любая часть вашего кода, которая хочет отображать данные, вызывает некоторую функцию для добавления данных в этот буфер каждый кадр. Это гораздо меньше работы, чем создание элемента на часть данных выше. Например, давайте изменим HTML сверху только на это ```html

``` И давайте создадим простой класс для управления этим *буфером очистки*. ```js class ClearingLogger { constructor(elem) { this.elem = elem; this.lines = []; } log(...args) { this.lines.push([...args].join(' ')); } render() { this.elem.textContent = this.lines.join('\n'); this.lines = []; } } ``` Тогда давайте сделаем простой пример, который каждый раз, когда мы щелкаем мышью, создает сетку, которая движется в произвольном направлении в течение 2 секунд. Мы начнем с одного из примеров из статьи о [том, как сделать вещи отзывчивыми](threejs-responsive.html) Вот код, который добавляет новую сетку каждый раз, когда мы щелкаем мышью ```js const geometry = new THREE.SphereBufferGeometry(); const material = new THREE.MeshBasicMaterial({color: 'red'}); const things = []; function rand(min, max) { if (max === undefined) { max = min; min = 0; } return Math.random() * (max - min) + min; } function createThing() { const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); things.push({ mesh, timer: 2, velocity: new THREE.Vector3(rand(-5, 5), rand(-5, 5), rand(-5, 5)), }); } canvas.addEventListener('click', createThing); ``` А вот код, который перемещает созданные нами meshes, регистрирует их и удаляет их, когда у них заканчивается таймер ```js const logger = new ClearingLogger(document.querySelector('#debug pre')); let then = 0; function render(now) { now *= 0.001; // convert to seconds const deltaTime = now - then; then = now; ... logger.log('fps:', (1 / deltaTime).toFixed(1)); logger.log('num things:', things.length); for (let i = 0; i < things.length;) { const thing = things[i]; const mesh = thing.mesh; const pos = mesh.position; logger.log( 'timer:', thing.timer.toFixed(3), 'pos:', pos.x.toFixed(3), pos.y.toFixed(3), pos.z.toFixed(3)); thing.timer -= deltaTime; if (thing.timer <= 0) { // remove this thing. Note we don't advance `i` things.splice(i, 1); scene.remove(mesh); } else { mesh.position.addScaledVector(thing.velocity, deltaTime); ++i; } } renderer.render(scene, camera); logger.render(); requestAnimationFrame(render); } ``` Теперь кликните мышкой в примере ниже. {{{example url="../threejs-debug-js-clearing-logger.html" }}} ## Параметры запроса Следует также помнить, что на веб-страницах могут передаваться данные либо через параметры запроса, либо через привязку, иногда называемую поиском и хэшем. https://domain/path/?query#anchor Вы можете использовать это, чтобы сделать функции необязательными или передать параметры. Например, давайте возьмем предыдущий пример и сделаем так, чтобы материал отладки отображался только в том случае, если мы добавили `?debug=true` в URL. Сначала нам нужен код для разбора строки запроса ```js /** * Returns the query parameters as a key/value object. * Example: If the query parameters are * * abc=123&def=456&name=gman * * Then `getQuery()` will return an object like * * { * abc: '123', * def: '456', * name: 'gman', * } */ function getQuery() { return Object.fromEntries(new URLSearchParams(window.location.search).entries()); } ``` Тогда мы можем сделать элемент отладки скрытым по умолчанию ```html + ``` Затем в коде мы читаем параметры и выбираем не скрывать отладочную информацию тогда и только тогда, когда передается `?debug=true` ```js const query = getQuery(); const debug = query.debug === 'true'; const logger = debug ? new ClearingLogger(document.querySelector('#debug pre')) : new DummyLogger(); if (debug) { document.querySelector('#debug').style.display = ''; } ``` Мы также создали `DummyLogger`, который ничего не делает, и решили использовать его, если `?debug=true` не был передан. ```js class DummyLogger { log() {} render() {} } ``` Вы можете увидеть, если мы используем этот URL: threejs-debug-js-params.html отладочной информации нет, но если мы используем этот URL: threejs-debug-js-params.html?debug=true есть отладочная информация.. Несколько параметров можно передать, разделив их символом '&' как в `somepage.html?someparam=somevalue&someotherparam=someothervalue`. Используя такие параметры, мы можем передавать все виды опций. Может быть `speed=0.01` чтобы замедлить наше приложение для облегчения понимания чего-либо, или `showHelpers=true` для того, чтобы добавлять или нет помощников, которые показывают свет, тень или усечение камеры, рассматриваемые в других уроках. ## Научитесь пользоваться отладчиком В каждом браузере есть отладчик, в котором вы можете останавливать вашу программу шаг за шагом и проверять все переменные. Обучение тому, как использовать отладчик - слишком большая тема для этой статьи, но вот несколько ссылок * [Get Started with Debugging JavaScript in Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/javascript/) * [Debugging in Chrome](https://javascript.info/debugging-chrome) * [Tips and Tricks for Debugging in Chrome Developer Tools](https://hackernoon.com/tips-and-tricks-for-debugging-in-chrome-developer-tools-458ade27c7ab) ## Проверьте `NaN` в отладчике или в другом месте `NaN` это сокращение от Not A Number. Это то, что JavaScript будет назначать в качестве значения, когда вы делаете что-то, что не имеет смысла математически. В качестве простого примера
Часто, когда я что-то делаю и на экране ничего не появляется, я проверяю некоторые значения, и если я вижу `NaN`, имеет место быть, чтобы начать поиск. В качестве примера, когда я впервые начал создавать путь для [статьи о загрузке файлов gLTF](threejs-load-gltf.html) я сделал кривую, используя класс `SplineCurve`, который создает 2D-кривую. Затем я использовал эту кривую, чтобы двигать автомобили ```js curve.getPointAt(zeroToOnePointOnCurve, car.position); ``` Внутренне `curve.getPointAt` вызывает функцию `set` для объекта, переданного в качестве второго аргумента. В этом случае вторым аргументом является `car.position`, который является `Vector3`. Функция `set` в `Vector3` требует 3 аргумента, x, y и z, но `SplineCurve` является 2D-кривой и поэтому вызывает `car.position.set` только с x и y. В результате `car.position.set` устанавливает x в x, y в y, а z в `undefined`. Беглый взгляд в отладчике `matrixWorld` автомобиля показал множество значений `NaN`.
Видя, что в матрице присутствуют `NaN` можно предположить что-то вроде `position`, `rotation`, `scale` или какой-либо другой функции, которая влияет на то, что в матрице содержались неверные данные. Работая в обратном направлении, было легко отследить проблему. В верхней части `NaN` есть также `Infinity`, что является аналогичным признаком того, что где-то есть математическая ошибка. ## Смотри в коде! THREE.js является открытым исходным кодом. Не бойтесь заглянуть внутрь кода! Вы можете заглянуть внутрь на [github](https://github.com/mrdoob/three.js). Вы также можете заглянуть внутрь, войдя в функции в отладчике. Когда вы сделаете это, рассмотрите возможность использования `three.js` вместо более распространенного `three.min.js`. `three.min.js` - уменьшенная, сжатая и поэтому уменьшенная для загрузки версия. `three.js` - более крупная, но более простая в отладке версия. Я часто переключаю свой код на использование `three.js`, чтобы пройтись по коду и посмотреть, что происходит. ## Поместите `requestAnimationFrame` внизу вашей функции рендеринга. Я часто вижу эту закономерность ```js function render() { requestAnimationFrame(render); // -- do stuff -- renderer.render(scene, camera); } requestAnimationFrame(render); ``` Я бы предложил поместить вызов `requestAnimationFrame` внизу, как в ```js function render() { // -- do stuff -- renderer.render(scene, camera); requestAnimationFrame(render); } requestAnimationFrame(render); ``` Основная причина в том, что ваш код остановится – возникновение ошибки. Помещение `requestAnimationFrame` вверху означает, что ваш код будет продолжать работать, даже если у вас есть ошибка, так как вы уже запросили другой кадр. Лучше найти эти ошибки, чем игнорировать их. Они могут быть причиной того, что что-то не появляется так, как вы ожидаете, но если ваш код не остановится, вы можете даже не заметить. ## Проверьте свои единицы! Это в основном означает понимание, например, когда использовать градусы, а когда использовать радианы. К сожалению, THREE.js не использует везде одинаковые единицы измерения. Первое, что пришло в голову поле зрения камеры - в градусах. Все остальные углы указаны в радианах. Другое место, на которое стоит обратить внимание - ваши единицы измерения. До недавнего времени 3D-приложения могли выбирать любую единицу измерения, который они хотели. Одно приложение может выбрать 1 единицу = 1 см. Другой может выбрать 1 единицу = 1 фут. На самом деле все еще верно, что вы можете выбрать любые единицы измерения для определенных приложений. Тем не менее, THREE.js предполагает 1 единицу = 1 метр. Это важно для таких вещей, как физический рендеринг с использованием метров для вычисления световых эффектов. Это также важно для AR и VR, которые должны иметь дело с реальными устройствами, такими как ваш телефон или как контроллеры VR. ## Создание *Minimal, Complete, Verifiable, Example* для Stack Overflow Если вы решите задать вопрос о THREE.js, вам почти всегда необходимо предоставить MCVE, что означает «Минимальный, Полный, Проверяемый, Пример». **Минимальная** часть важна. Допустим, у вас возникла проблема с перемещением пути в последнем примере [загрузки статьи gLTF](threejs-load-gltf.html). Этот пример имеет много частей. Включите их в список 1. A bunch of HTML 2. Some CSS 3. Lights 4. Shadows 5. DAT.gui code to manipulate shadows 6. Code to load a .GLTF file 7. Code to resize the canvas. 8. Code to move the cars along paths Это довольно много. Если ваш вопрос касается только пути, вы можете удалить большую часть HTML, так как вам нужен только тег `` и `