Создание UI
Проблема
Очень часто может потребоваться создание какого-либо интерфейса для прямого взаимодействия с пользователем. Обычно, в игровых движках, таких как Unity, Unreal Engine и так далее, интерфейс создается в пару кнопок.

Раньше же, интерфейс создавали другим способом — создавали текстурированную плоскость (UI) и выносили её куда-то вдаль. Далее, создавали еще одну камеру, которая проецировала этот интерфейс уже на экран пользователя.
К сожалению, данный способ не может быть воссоздан в продвинутом редакторе. Про ограничения о работе с камерами было описано в статье Работа с камерой.
На данный момент, единственным способом добавления UI, является работа с HTML-кодом страницы. Но этот вариант не будет являться правильным, так как это может плохо сказаться на работе сцены в дополненной реальности.
Рабочий способ
В данном случае я создам 2D-кнопку для воспроизведения анимации 3D-модели. Вы можете посмотреть результат, отсканировав QR-код ниже.

Для создания интерфейса первым делом необходимо создать пустой проект.

Далее необходимо добавить 3D-объект, нажав на кнопку File → Import.

Этот объект должен содержать как минимум одну анимацию.

Теперь выберите объект Scene в дереве объектов справа.

Чтобы создать сценарий, нажмите на кнопку NEW.

Далее необходимо перейти в редактор кода, нажав на кнопку Edit.

Теперь можно приступить непосредственно к написанию кода.
Первое, что нужно сделать, это создать функцию для создания интерфейса, назвав ее makeUI()
.
function makeUI() {
}
После этого необходимо создать новый HTML-элемент <div>
, который будет являться контейнером (строки 2-5).
let container;
function makeUI() {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
document.body.appendChild(container);
}
Чтобы избежать создания нового элемента <div>
при каждом запуске сцены, следует добавить проверку существования контейнера с помощью if
(строки 2-3).
let container;
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
document.body.appendChild(container);
}
}
Далее необходимо добавить элемент <img>
внутри контейнера, который будет 2D-интерфейсом (строки 10-14).
let container;
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
document.body.appendChild(container);
}
const img = document.createElement('img');
img.src = 'https://mywebar-a.akamaihd.net/15103/play-button-arrows-svgrepo-com.png';
img.id='imageButton'
img.style.display = 'block';
container.appendChild(img);
}
Давайте немного изменим строку 9 (создание элемента), добавив проверку на его существование, чтобы элемент не был клонирован.
let container;
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
document.body.appendChild(container);
}
const img = container.querySelector('#imageButton')|| document.createElement('img');
img.src = 'https://mywebar-a.akamaihd.net/15103/play-button-arrows-svgrepo-com.png';
img.id='imageButton'
img.style.display = 'block';
container.appendChild(img);
}
Далее нам нужно добавить стили к этим элементам, чтобы изображение находилось точно в нижней части экрана. Для этого я создам объект styles
вне этой функции (строки 2-17). После этого я привяжу стили к контейнеру и изображению (строки 25 и 32).
let container;
const styles = {
container: {
position: 'absolute',
zIndex: 9999,
left: '0',
right: '0',
bottom: '0',
marginLeft: 'auto',
marginRight: 'auto',
width: '120px',
},
img: {
height: '120px',
width: '120px',
},
};
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
Object.assign(container.style, styles.container);
document.body.appendChild(container);
}
const img = container.querySelector('#imageButton')|| document.createElement('img');
img.src = 'https://mywebar-a.akamaihd.net/15103/play-button-arrows-svgrepo-com.png';
img.id='imageButton'
Object.assign(img.style, styles.img);
img.style.display = 'block';
container.appendChild(img);
}
Далее необходимо создать три функции: init
, start
и stop
. Это три функции js, которые выполняются при инициализации сцены (init
), запуске сцены (start
) и остановке сцены (stop
).
let container;
const styles = {
container: {
position: 'absolute',
zIndex: 9999,
left: '0',
right: '0',
bottom: '0',
marginLeft: 'auto',
marginRight: 'auto',
width: '120px',
},
img: {
height: '120px',
width: '120px',
},
};
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
Object.assign(container.style, styles.container);
document.body.appendChild(container);
}
const img = container.querySelector('#imageButton')|| document.createElement('img');
img.src = 'https://mywebar-a.akamaihd.net/15103/play-button-arrows-svgrepo-com.png';
img.id='imageButton'
Object.assign(img.style, styles.img);
img.style.display = 'block';
container.appendChild(img);
}
function init() {
}
function start() {
}
function stop() {
}
В функциях init
и start
необходимо вызвать makeUI()
для его запуска. В этом случае интерфейс появится сразу при запуске сцены (строки 38 и 43).
let container;
const styles = {
container: {
position: 'absolute',
zIndex: 9999,
left: '0',
right: '0',
bottom: '0',
marginLeft: 'auto',
marginRight: 'auto',
width: '120px',
},
img: {
height: '120px',
width: '120px',
},
};
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
Object.assign(container.style, styles.container);
document.body.appendChild(container);
}
const img = container.querySelector('#imageButton')|| document.createElement('img');
img.src = 'https://mywebar-a.akamaihd.net/15103/play-button-arrows-svgrepo-com.png';
img.id='imageButton'
Object.assign(img.style, styles.img);
img.style.display = 'block';
container.appendChild(img);
}
function init() {
makeUI();
}
function start() {
makeUI();
}
function stop() {
}
Теперь необходимо сделать так, чтобы интерфейс скрывался и появлялся. Для этого измените свойство контейнера container.style.display
(строки 42 и 46) в функциях start
(появляться) и stop
(скрываться).
let container;
const styles = {
container: {
position: 'absolute',
zIndex: 9999,
left: '0',
right: '0',
bottom: '0',
marginLeft: 'auto',
marginRight: 'auto',
width: '120px',
},
img: {
height: '120px',
width: '120px',
},
};
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
Object.assign(container.style, styles.container);
document.body.appendChild(container);
}
const img = container.querySelector('#imageButton')|| document.createElement('img');
img.src = 'https://mywebar-a.akamaihd.net/15103/play-button-arrows-svgrepo-com.png';
img.id='imageButton'
Object.assign(img.style, styles.img);
img.style.display = 'block';
container.appendChild(img);
}
function init() {
makeUI();
}
function start() {
makeUI();
container.style.display = 'block';
}
function stop() {
container.style.display = 'none';
}
Теперь необходимо создать событие щелчка для добавленного интерфейса. Событие создается с помощью чистого JavaScript. Для этого создайте функцию clickFunction
(строки 37-40), которая будет запускаться при нажатии на изображение. Также в функцию запуска нужно добавить слушателя события (строка 48).
let container;
const styles = {
container: {
position: 'absolute',
zIndex: 9999,
left: '0',
right: '0',
bottom: '0',
marginLeft: 'auto',
marginRight: 'auto',
width: '120px',
},
img: {
height: '120px',
width: '120px',
},
};
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
Object.assign(container.style, styles.container);
document.body.appendChild(container);
}
const img = container.querySelector('#imageButton')|| document.createElement('img');
img.src = 'https://mywebar-a.akamaihd.net/15103/play-button-arrows-svgrepo-com.png';
img.id='imageButton'
Object.assign(img.style, styles.img);
img.style.display = 'block';
container.appendChild(img);
}
function clickFunction() {
container.style.display = 'none';
}
function init() {
makeUI();
}
function start() {
makeUI();
container.style.display = 'block';
container.addEventListener("click", clickFunction);
}
function stop() {
container.style.display = 'none';
}
Последний шаг - работа с анимацией. Подробнее о работе с анимацией в PRO EDITOR вы можете прочитать в статьеРабота с анимациями 3D-объектов. В данном случае был добавлен один анимационный ролик (строки 37-46). Сама анимация должна быть запущена внутри функции события clickFunction
(строка 49).
let container;
const styles = {
container: {
position: 'absolute',
zIndex: 9999,
left: '0',
right: '0',
bottom: '0',
marginLeft: 'auto',
marginRight: 'auto',
width: '120px',
},
img: {
height: '120px',
width: '120px',
},
};
function makeUI() {
container = document.getElementById('container');
if (!container) {
container = document.createElement('div');
container.id = 'container';
container.style.display = 'none';
Object.assign(container.style, styles.container);
document.body.appendChild(container);
}
const img = container.querySelector('#imageButton')|| document.createElement('img');
img.src = 'https://mywebar-a.akamaihd.net/15103/play-button-arrows-svgrepo-com.png';
img.id='imageButton'
Object.assign(img.style, styles.img);
img.style.display = 'block';
container.appendChild(img);
}
let clock = new THREE.Clock();
const myObject = scene.getObjectByName('stegosaurus_rigcharacter');
const clip = myObject.animations[0];
const mixer = new THREE.AnimationMixer(myObject);
const action = mixer.clipAction(clip);
function update() {
if (mixer) {
mixer.update(clock.getDelta());
}
}
function clickFunction() {
action.play();
container.style.display = 'none';
}
function init() {
makeUI();
}
function start() {
makeUI();
container.style.display = 'block';
container.addEventListener("click", clickFunction);
}
function stop() {
container.style.display = 'none';
}
Сцена готова. Теперь нужно экспортировать сцену в MyWebAR, нажав File → Publish.

Вернитесь в стандартный редактор, нажмите на тип объекта Json MAE на левой панели и загрузите файл .json, который вы экспортировали из продвинутого редактора.

На сцене появится 3D-объект. Сам интерфейс будет виден только в предварительном просмотре или в дополненной реальности.

Last updated