User posts -Vampire-
26 November 2015 07:46
Ответ на сообщение пользователя Йеджи Заборовски
Да, у меня Казань спрашивала год назад. Не сошлись в мнениях. Хотели за 5 дней запилить конструктор, чтобы туда модель любую вставлять автоматом.
Ну если просто загрузка модели, то это наверн можно и за день сделать.. Я хочу немного посерьёзней штуку. Штоп можно было например размеры менять, текстуры… лампочки расставлять. Если уж прям грубо, то вынести весь возможный и поддерживаемый движком функционал блендера в браузер. ИМХО кста - отличный способ изучения движка
24 November 2015 17:56
21 November 2015 03:05
Всем привет.
Скажите, а планируете ли ли вы добавить возможность динамически создавать что-нибудь? Например материалы или меши. С мешами вродь понятно, можно скопировать его и изменить геометрию например. А вот с материалами как быть? Ведь если я правильно понимаю, все материалы лежат в виде массива в свойстве объекта и достаточно просто добавить еще элемент(ы) к этому массиву. Или всё сложней и страшней?
Скажите, а планируете ли ли вы добавить возможность динамически создавать что-нибудь? Например материалы или меши. С мешами вродь понятно, можно скопировать его и изменить геометрию например. А вот с материалами как быть? Ведь если я правильно понимаю, все материалы лежат в виде массива в свойстве объекта и достаточно просто добавить еще элемент(ы) к этому массиву. Или всё сложней и страшней?
12 November 2015 12:54
12 November 2015 04:59
Собираю вот такую штуку. http://www.youtube.com/watch?v=Nqk_nWAjBus
Но так как держать весь алгоритм в голове сложно, решил сначала всё это визуализировать. Вот что получилось https://vk.com/video5346043_171348244?list=a1d1799490ae6cba1c
Но так как держать весь алгоритм в голове сложно, решил сначала всё это визуализировать. Вот что получилось https://vk.com/video5346043_171348244?list=a1d1799490ae6cba1c
02 November 2015 03:24
13 August 2015 04:40
Привет-привет! Сегодня речь пойдет о всяких машинках, домиках и прочей ерунде, загружаемой на локацию
Сразу оговорюсь: это, пожалуй, самая объемная и трудоёмкая часть из всего проекта. И не допилено еще очень много. По-этому, расскажу наверное в общих чертах как мне это всё видится сейчас.
Сначала о моделях
Модель какого-либо объекта (а в данном конкретном случае я говорю про модель ЗРК), состоит из каких-то функциональных частей. То есть мой ЗРК это не один меш, а траки отдельно, колёса отдельно, ракеты отдельно и тп. Такой подход одновременно и необходим и очень полезен. Во-первых, при желании мы можем стрельнуть в наш ЗРК из гранатомёта например и заставить его развалиться на эти самые составные части. Во-вторых, каждая из этих составных частей будет являться самостоятельным объектом, что позволит нам раскидать эти части по локациям и заставить игрока их искать чтоб потом из них собрать единое что-то. Это применимо как ко всякого рода машинкам, так и к постройкам, оружию и устройствам: например чтоб построить колодец нам нужно три бревна, цепь и ведро.
Настраиваем необходимую анимацию, физику и экспорт.
После экспорта, нам надо эту модель загрузить в игру. Казалось бы возьми просто и положи её в нужную папку… Ан нет… Дело в том, что нам в последствии нужно будет программно назначить ограничители для всех частей объекта. (Простите, если все настройки constraints из blendera нормально понимаются движком. честно не пробовал). Для этого я написал скрипт, которому скармливается результат экспорта из блендера, а он на выходе выдает нам интерфейс, в котором мы можем указать кто чей родитель и тип ограничителя: на данный момент stiff или semi-stiff (скрин ниже). Так же этот скрипт разберёт объект на детальки и создаст для них в БД экземпляры
Теперь к логике
Задача, не много ни мало, в следующем:
1. после загрузки окружающей среды, загрузить на сцену все объекты, находящиеся на локации.
2. собрать объекты
3. расположить объекты в нужных местах
4. повесить на эти объекты физику
5. научить объекты взаимодействовать с нашим персонажем
6. запустить анимацию
.
Загрузка объектов
После того, как мы вытащили из БД список всех объектов локации, нам надо эти объекты загрузить на сцену. Но перед загрузкой надо этот список немного отсортировать. Дело в том, что на локации может быть 2 и более одинаковых объектов, а смысла в том, чтобы дважды спрашивать у сервера одни и те же данные я не вижу. По-этому объекты мы будем тиражировать обычным objects.copy()
Сборка объектов
Так как мы определились, что все объекты состоят из деталек, каким-то образом нам надо их между собой связать чтоб потом адекватно перемещать\вращать. Для этого в БД для каждой детальки (кроме основной "корневой") указывается имя родительского меша и тип отношения между родителем наследником. Тип отношения задаётся флагом canRotate
Функция getObjectTranslationOffset нужна для того, чтоб определить смещение origin дочерней детальки относительно origin родительской. Это необходимо, потому что мы не можем расположить origin всех деталей в точке начала координат ибо правой двери нужно вращаться вокруг одной точки, левой двери вокруг другой точки, а крышке багажника вокруг сааааавсем другой.
Выглядит она так (знаю, что кривая… самому не нравится, но пока ничо лучше я не придумал)
Перебрав в цикле все детальки, мы можем найти "корневую" детальку (например кузов). У этой детальки свойство parentMeshName будет NULL
Расположение объектов на локации
Так как все детальки у нас теперь будут следовать за корневой, нам достаточно указать только её координаты, которые мы вытаскиваем из БД. Так же из БД мы вытаскиваем угол поворота. По большей части нас волнует поворот только вокруг вертикальной оси, так как все остальные крены должны обуславливаться физикой и действием гравитации (не всегда конечно, но за большим исключением). Для поворота я использую немножко модифицированную функцию, приведённую для меня Романом Семенцовым, за что ему еще раз огромное спасибо.
Но если с перемещением объекта всё хорошо, то вот с вращением есть нюанс. Дело в том, что наложив ограничитель semi-stiff, мы позволили детальке забить на вращение родительского меша и крутиться как ей самой вздумается. И при повороте родительского объекта, такая наша деталька останется в своём первозданном положении. Пока еще не пробовал, но думаю смогу победить это путем смены semi-stiff на stiff непосредственно перед поворотом.
Физика
С физикой всё хорошо, до тех пор пока она Static. Но как только, я делаю тип физики динамическим, детальки начинают расползаться в разные стороны. Видать реагируют на соударение между собой. И на ограничители им пофик. Пока не смог это победить и подобрать (именно подобрать, ибо я вааашпе не понимаю как это работает ) нужную комбинацию галочек в блендере. Как подберу обязательно расскажу как я это сделал.
Интерактив
Тут нас интересует два момента: когда персонаж игрока близко подошел к объекту и когда мы кликнули по объекту.
Первое соответственно через collision_sensor, второе через scene.pick_object(). При обоих этих событиях мы выводим список доступных с объектом действий в виде кнопочек
и показываем информацию об объекте а-ля иконка, название, описание…
Анимация
Ну скучно ведь, когда машинки стоят как на фотографии… Значит надо, чтоб они крутились по-всякому. Для этого в блендере для нужных деталек делаем объектную анимацию и называем её как {meshName}_idleAction и запускаем её программно. Так же, мы хотим, чтобы когда мы кликнули по крышке багажника, у нас (кроме отображения списка действий и информации) эта крышка открылась. Для этого создаём объектную анимацию {meshName}_selectAction. С этим проблем казалось бы нет… Но увы я не нашел способа программно получить список действий, которые повешены на объект. animation.get_anim_names() возвращает список всех экшнов и ни слова не говорит о том, кому какой экшн принадлежит. Закрадывается сомнение, что так и должно быть ибо экшну судя по всему пофик у какого объекта менять значения location/rotation/scale в соответствии со значениями в keyframe. (И это грустно ). Так же я не знаю как в блендере задать список экшнов для конкретного меша. Пока думаю что с этим делать…
Итог
Это, конечно, далеко не всё, что придётся пережить объектам локации. Все объекты разные и ведут себя по-разному, и уложить это всё в один пост было бы кощунством. Так что о специфике работы с разными типами объектов буду рассказывать в следующих постах. А пока, всем кавабанга
Сразу оговорюсь: это, пожалуй, самая объемная и трудоёмкая часть из всего проекта. И не допилено еще очень много. По-этому, расскажу наверное в общих чертах как мне это всё видится сейчас.
Сначала о моделях
Модель какого-либо объекта (а в данном конкретном случае я говорю про модель ЗРК), состоит из каких-то функциональных частей. То есть мой ЗРК это не один меш, а траки отдельно, колёса отдельно, ракеты отдельно и тп. Такой подход одновременно и необходим и очень полезен. Во-первых, при желании мы можем стрельнуть в наш ЗРК из гранатомёта например и заставить его развалиться на эти самые составные части. Во-вторых, каждая из этих составных частей будет являться самостоятельным объектом, что позволит нам раскидать эти части по локациям и заставить игрока их искать чтоб потом из них собрать единое что-то. Это применимо как ко всякого рода машинкам, так и к постройкам, оружию и устройствам: например чтоб построить колодец нам нужно три бревна, цепь и ведро.
Настраиваем необходимую анимацию, физику и экспорт.
После экспорта, нам надо эту модель загрузить в игру. Казалось бы возьми просто и положи её в нужную папку… Ан нет… Дело в том, что нам в последствии нужно будет программно назначить ограничители для всех частей объекта. (Простите, если все настройки constraints из blendera нормально понимаются движком. честно не пробовал). Для этого я написал скрипт, которому скармливается результат экспорта из блендера, а он на выходе выдает нам интерфейс, в котором мы можем указать кто чей родитель и тип ограничителя: на данный момент stiff или semi-stiff (скрин ниже). Так же этот скрипт разберёт объект на детальки и создаст для них в БД экземпляры
Теперь к логике
Задача, не много ни мало, в следующем:
1. после загрузки окружающей среды, загрузить на сцену все объекты, находящиеся на локации.
2. собрать объекты
3. расположить объекты в нужных местах
4. повесить на эти объекты физику
5. научить объекты взаимодействовать с нашим персонажем
6. запустить анимацию
.
Загрузка объектов
После того, как мы вытащили из БД список всех объектов локации, нам надо эти объекты загрузить на сцену. Но перед загрузкой надо этот список немного отсортировать. Дело в том, что на локации может быть 2 и более одинаковых объектов, а смысла в том, чтобы дважды спрашивать у сервера одни и те же данные я не вижу. По-этому объекты мы будем тиражировать обычным objects.copy()
Сборка объектов
Так как мы определились, что все объекты состоят из деталек, каким-то образом нам надо их между собой связать чтоб потом адекватно перемещать\вращать. Для этого в БД для каждой детальки (кроме основной "корневой") указывается имя родительского меша и тип отношения между родителем наследником. Тип отношения задаётся флагом canRotate
var parentName = instance.parts[i].parts.data.parentMeshName + '_' + instance.locationObject.id;
var targetName = instance.parts[i].parts.data.meshName + '_' + instance.locationObject.id;
var parent = scene.get_object_by_name(parentName, sceneId);
var target = scene.get_object_by_name(targetName, sceneId);
if(instance.parts[i].parts.data.canRotate)
constraints.append_semi_stiff(target, parent, getObjectTranslationOffset(target, parent));
else
constraints.append_stiff(target, parent, getObjectTranslationOffset(target, parent));
Функция getObjectTranslationOffset нужна для того, чтоб определить смещение origin дочерней детальки относительно origin родительской. Это необходимо, потому что мы не можем расположить origin всех деталей в точке начала координат ибо правой двери нужно вращаться вокруг одной точки, левой двери вокруг другой точки, а крышке багажника вокруг сааааавсем другой.
Выглядит она так (знаю, что кривая… самому не нравится, но пока ничо лучше я не придумал)
var getObjectTranslationOffset = function(target, parent){
var rt = transform.get_translation(target);
var pt = transform.get_translation(parent);
var ox = Math.abs(rt[0] - pt[0]);
var oy = Math.abs(rt[1] - pt[1]);
var oz = Math.abs(rt[2] - pt[2]);
if(rt[0] < pt[0]) ox *= -1
if(rt[1] < pt[1]) oy *= -1;
if(rt[2] < pt[2]) ox *= -1;
var offset = new Float32Array([ox,oy,oz]);
return offset;
}
Перебрав в цикле все детальки, мы можем найти "корневую" детальку (например кузов). У этой детальки свойство parentMeshName будет NULL
Расположение объектов на локации
Так как все детальки у нас теперь будут следовать за корневой, нам достаточно указать только её координаты, которые мы вытаскиваем из БД. Так же из БД мы вытаскиваем угол поворота. По большей части нас волнует поворот только вокруг вертикальной оси, так как все остальные крены должны обуславливаться физикой и действием гравитации (не всегда конечно, но за большим исключением). Для поворота я использую немножко модифицированную функцию, приведённую для меня Романом Семенцовым, за что ему еще раз огромное спасибо.
var X = 0;
var Y = 1;
var Z = 2;
var rotateObject = function(object, axis, angle){
var angle = Math.PI / (180 / angle);
var oldRotaton = transform.get_rotation(object)
var newRotation = new Float32Array(3);
newRotation[axis] = angle;
var newRotationQuat = util.euler_to_quat(newRotation);
quat.multiply(oldRotaton, newRotationQuat, oldRotaton);
transform.set_rotation(object, oldRotaton[0], oldRotaton[1], oldRotaton[2], oldRotaton[3]);
}
//Вызов rotateObject(obj, Y, 45);
Но если с перемещением объекта всё хорошо, то вот с вращением есть нюанс. Дело в том, что наложив ограничитель semi-stiff, мы позволили детальке забить на вращение родительского меша и крутиться как ей самой вздумается. И при повороте родительского объекта, такая наша деталька останется в своём первозданном положении. Пока еще не пробовал, но думаю смогу победить это путем смены semi-stiff на stiff непосредственно перед поворотом.
Физика
С физикой всё хорошо, до тех пор пока она Static. Но как только, я делаю тип физики динамическим, детальки начинают расползаться в разные стороны. Видать реагируют на соударение между собой. И на ограничители им пофик. Пока не смог это победить и подобрать (именно подобрать, ибо я вааашпе не понимаю как это работает ) нужную комбинацию галочек в блендере. Как подберу обязательно расскажу как я это сделал.
Интерактив
Тут нас интересует два момента: когда персонаж игрока близко подошел к объекту и когда мы кликнули по объекту.
Первое соответственно через collision_sensor, второе через scene.pick_object(). При обоих этих событиях мы выводим список доступных с объектом действий в виде кнопочек
[{socketEventName:'someEvent', title:'Действие 1'}, ...., {socketEventName:'someEvent', title:'Действие N'}]
и показываем информацию об объекте а-ля иконка, название, описание…
Анимация
Ну скучно ведь, когда машинки стоят как на фотографии… Значит надо, чтоб они крутились по-всякому. Для этого в блендере для нужных деталек делаем объектную анимацию и называем её как {meshName}_idleAction и запускаем её программно. Так же, мы хотим, чтобы когда мы кликнули по крышке багажника, у нас (кроме отображения списка действий и информации) эта крышка открылась. Для этого создаём объектную анимацию {meshName}_selectAction. С этим проблем казалось бы нет… Но увы я не нашел способа программно получить список действий, которые повешены на объект. animation.get_anim_names() возвращает список всех экшнов и ни слова не говорит о том, кому какой экшн принадлежит. Закрадывается сомнение, что так и должно быть ибо экшну судя по всему пофик у какого объекта менять значения location/rotation/scale в соответствии со значениями в keyframe. (И это грустно ). Так же я не знаю как в блендере задать список экшнов для конкретного меша. Пока думаю что с этим делать…
Итог
Это, конечно, далеко не всё, что придётся пережить объектам локации. Все объекты разные и ведут себя по-разному, и уложить это всё в один пост было бы кощунством. Так что о специфике работы с разными типами объектов буду рассказывать в следующих постах. А пока, всем кавабанга
03 August 2015 16:17
Ну и приследуем стиль функционального программирования. Т.е., модули выступают только как набор API и сами не выполняют никакой работы при загрузке.
Полностью согласен. Всё должно быть использовано только тогда, когда оно должно быть использовано.
var initLocationObject = function(){
_import('EA_LOCATION_OBJECT', function(module){
LOCATION_OBJECT = module;
LOCATION_OBJECT.init();
SOCKET.bindEvents(LOCATION_OBJECT.getSocketEvents());
HOME.setBuildingOnClickListener(function(id){
SOCKET.trigger('build', {buildingId:id, locationId:PROFILE.getCurrentLocation().id, x:0, y:0});
});
initScene();
});
}
03 August 2015 16:05
Ответ на сообщение пользователя Евгений РодыгинВы про GUI? Не исключаю такой возможности, но пока всё это дело выглядит довольно просто А в коде это всё строится вот так
Мне кажется, вы его потом захотите вынести в отдельный модуль все-таки) Просто для удобства. Вроде бы, не самый тривиальный интерфейс должен быть
var setupUi = function(){
var wrapper = document.createElement('div');
wrapper.style.width = '270px';
wrapper.style.height = '100%';
wrapper.style.cssFloat = 'left';
wrapper.style.overflowY = 'auto';
wrapper.appendChild(PROFILE.getProfileWidget());
wrapper.appendChild(PROFILE.getResourcesWidget());
wrapper.appendChild(PROFILE.getBagWidget());
wrapper.appendChild(TECHNOLOGIES.getMapControl());
wrapper.appendChild(MISSIONS.getMapControl());
wrapper.appendChild(TEAMS.getMapControl());
wrapper.appendChild(HOME.getMapControl());
wrapper.appendChild(SETTINGS.getMapControl());
wrapper.appendChild(SCENE.getSoundControl());
wrapper.appendChild(clock);
MAP.getControlPanel(google.maps.ControlPosition.TOP_LEFT).appendChild(wrapper);
//MAP.getControlPanel(SOME_PANEL).appendChild(SOME_HTML_ELEMENT);
}
Если дело удачно пойдет, я думаю, другим пользователям было бы интересно почитать урок на эту темуОбязательно напишу в ближайшее время