События

Управление от первого лица и физика

2015-02-12

Не секрет, что сегодня большой популярностью пользуются игровые приложения с видом от первого лица. Это вполне объяснимо. Такой подход позволяет игроку лучше влиться в окружающий мир, почувствовать себя частью происходящих событий, и зачастую просто гораздо удобнее с точки зрения игрового процесса. Сегодня мы разберемся как создать каркас для подобного приложения, используя 3D веб движок Blend4Web.

Локация

Для начала создадим основу всей локации, внутри которой будет перемещаться персонаж. Добавим объект с несколькими выступами. Это будет земля и физический "фундамент" всей нашей сцены. Так же попросим художника сделать для нас небольшой домик, на примере которого мы разберемся, как работать со статической физикой.

Физика

В Blend4Web существует два варианта физического поведения объектов: cтатический и динамический. В первом случае предполагается, что с течением времени объект не будет изменять своё положение в пространстве. Во вторую же группу входят все объекты, которые могут быть перемещены под действием физики: все персонажи, различные подбираемые объекты, коробки, бочки и т.д.

Кроме того существует два возможных варианта построения физического объема объекта:

  • по его реальной геометрии;
  • с применением физических примитивов (сфера, куб, капсула и т.д.).

Статические объекты

Земля будет статическим объектом с генерацией физического объема по геометрии. Для этого в настройках физики объекта выбираем Physics Type: Static, а в настройках материала необходимо включить флаг Special: Collision. Теперь динамические объекты будут входить в соударение с землей, а персонаж, соответственно, сможет ходить по ней.

С домом можно было бы сделать всё то же самое, но в нашем случае мы прибегнем к небольшой оптимизации. С целью облегчения физической геометрии, мы создадим новый объект со значительно уменьшенным количеством вершин. На его материале включаем Special: Collision и так же запрещаем рендеринг для него c помощью флага Do not Render. На изображении ниже этот материал имеет зеленый цвет.

Подобная техника широко используется в сценах, где планируются серьезные физические расчеты. В играх это позволяет избежать ненужных вычислений и очень существенно повысить количество кадров на физическом движке.

Динамические объекты

Расположим на сцене какой-нибудь объект, с которым сможет взаимодействовать персонаж. Пусть это будет небольшое ведерко:

Как видим, это уже динамический объект типа Rigid Body. Чтобы физика заработала в Blend4Web, на подобных объектах необходимо включать флаг Detect Collisions. В качестве примитива для расчета физика здесь выступает цилиндр. Это гораздо более оптимизированный вариант и его следует использовать всегда, когда есть возможность.

Персонаж

Создадим объект для будущего персонажа и добавим ему динамическую физику с примитивом типа Capsule. В разделе Blend4Web выберем Detect Collisions и Character. Параметры персонажа можно оставить по-умолчанию.

Последнее, на что стоит обратить внимание при настройке сцены - это тип поведения камеры. В нашем случае следует использовать EYE, и включить флаг Use Vertical Rotation Clamping для того, чтобы камера блокировалась в крайних вертикальных положениях.

Управление

Пришло время оживить созданную сцену. Воспользуемся базовым каркасом для приложения из документации разработчика. В обработчике загрузки поместим следующий код:

function load_cb(data_id) {
    // make camera follow the character
    var camobj = m_scs.get_active_camera();
    var character = m_scs.get_first_character();
    m_cons.append_stiff_trans(camobj, character, [0, 0.7, 0]);

    // enable rotation with mouse
    var canvas_elem = m_main.get_canvas_elem();
    canvas_elem.addEventListener("mouseup", function(e) {
        m_mouse.request_pointerlock(canvas_elem);
    }, false);

    setup_movement()
}

Здесь, во-первых, происходит жёсткая привязка камеры к персонажу с отступом вверх на высоту 0.7. Во-вторых, происходит попытка захвата курсора при щелчке на элемент canvas. Аддон mouse.js автоматически позаботится о том, чтобы при перемещении мыши происходил поворот персонажа.

Теперь напишем логику управления в функции setup_movement. Для начала зададим все переменные, которые будем использовать.

var key_a = m_ctl.create_keyboard_sensor(m_ctl.KEY_A);
var key_s = m_ctl.create_keyboard_sensor(m_ctl.KEY_S);
var key_d = m_ctl.create_keyboard_sensor(m_ctl.KEY_D);
var key_w = m_ctl.create_keyboard_sensor(m_ctl.KEY_W);
var key_space = m_ctl.create_keyboard_sensor(m_ctl.KEY_SPACE);
var key_shift = m_ctl.create_keyboard_sensor(m_ctl.KEY_SHIFT);

var move_state = {
    left_right: 0,
    forw_back: 0
}

var move_array = [key_w, key_s, key_a, key_d, key_shift];
var character = m_scs.get_first_character();

Первые 6 строк создают сенсоры клавиш управления. Помимо типового WASD управления так же будет ускорение на SHIFT и прыжок на пробел. Объект move_state отвечает за наличие скорости в прямом и в боковом направлениях. move_array - массив управляющих сенсоров. В качестве объекта управления получаем первого найденного на сцене персонажа и сохраняем его в переменную character. Далее объявим основной обработчик управления move_cb.

var move_cb = function(obj, id, pulse) {
    if (pulse == 1) {
        switch (id) {
        case "FORWARD":
            move_state.forw_back = 1;
            break;
        case "BACKWARD":
            move_state.forw_back = -1;
            break;
        case "LEFT":
            move_state.left_right = 1;
            break;
        case "RIGHT":
            move_state.left_right = -1;
            break;
        case "RUNNING":
            m_phy.set_character_move_type(obj, m_phy.CM_RUN);
            break;
        }
    } else {
        switch (id) {
        case "FORWARD":
        case "BACKWARD":
            move_state.forw_back = 0;
            break;
        case "LEFT":
        case "RIGHT":
            move_state.left_right = 0;
            break;
        case "RUNNING":
            m_phy.set_character_move_type(obj, m_phy.CM_WALK);
            break;
        }
    }

    m_phy.set_character_move_dir(obj, move_state.forw_back,
                                      move_state.left_right);
};

Этот обработчик будет срабатывать при нажатии или отпускании любой из клавиш: W, A, S, D, Shift. Первому случаю (нажатию) соответствует значение pulse равное 1, второму (отпусканию) - значение -1. move_state несёт в себе информацию о текущих нажатых клавишах, так, к примеру, его поле forw_back будет равно:

  • 1, если нажата клавиша W,
  • -1, если нажата S
  • 0, если ни одна из этих клавиш не нажата

За логику вызовов функции move_cb будут отвечать следующие сенсорные множества:

m_ctl.create_sensor_manifold(character, "FORWARD", m_ctl.CT_TRIGGER,
    move_array, function(s) {return s[0]}, move_cb);
m_ctl.create_sensor_manifold(character, "BACKWARD", m_ctl.CT_TRIGGER,
     move_array, function(s) {return s[1]}, move_cb);
m_ctl.create_sensor_manifold(character, "LEFT", m_ctl.CT_TRIGGER,
    move_array, function(s) {return s[2]}, move_cb);
m_ctl.create_sensor_manifold(character, "RIGHT", m_ctl.CT_TRIGGER,
    move_array, function(s) {return s[3]}, move_cb);

var running_logic = function(s) {
   return (s[0] || s[1] || s[2] || s[3]) && s[4];
}
m_ctl.create_sensor_manifold(character, "RUNNING", m_ctl.CT_TRIGGER,
    move_array, running_logic, move_cb);

Все сенсорные множества имеют тип CT_TRIGGER, то есть срабатывают при любом изменении значения логической функции. Логика ускоренного движения персонажа включает в себя все 5 сенсоров. Таким образом увеличение и снижение скорости происходит, если изменяется значение одного из сенсоров движения и сенсора клавиши Shift.

Последнее действие - это прыжок персонажа:

var jump_cb = function(obj, id, pulse) {
    m_phy.character_jump(obj);
}
m_ctl.create_sensor_manifold(character, "JUMP", m_ctl.CT_SHOT,
        [key_space], null, jump_cb);

Здесь всё так же, как при перемещении персонажа, только сенсорное множество изменило свой тип на CT_SHOT. Таким образом, событие вызывается только при нажатии на клавишу пробел, но не при её отпускании.

В результате этих нехитрых операций мы получили отличную заготовку для полноценной игры с видом от первого лица. Можно пойти дальше и добавить возможность подбирать объекты, носить в руках какие-либо орудия, позволить персонажу взаимодействовать с окружающими объектами при нажатии на разные клавиши.

Ссылка на приложение в отдельном окне.

Исходные файлы моделей войдут в состав бесплатного дистрибутива Blend4Web SDK.

Изменения

[2015-02-12] Изначальная публикация.

[2015-02-16] Тип камеры заменен на EYE.

Комментарии
12 дек. 2015 17:56
Еще заметил - когда выставил скорость бега и ходьбы на максимум при нажатии на Shift(бег) она начинает подпрыгивать)
18 дек. 2015 12:23
Почему-то не могу найти в закладке "physics" раздел "blend4web", как показано на 3-м и 4-м скриншоте. Blend4Web версии 15.10 SDK, ставил по мануалу.
18 дек. 2015 12:44

Почему-то не могу найти в закладке "physics" раздел "blend4web", как показано на 3-м и 4-м скриншоте. Blend4Web версии 15.10 SDK, ставил по мануалу.

У нас были изменения в интерфейсе, теперь достаточно выбрать вверху Engine -> Blend4web (на скриншоте уже выбрано). После этого в закладках будут присутствовать только настройки, относящиеся к аддону и удалены лишние.

Скриншоты в документации мы обновили, но в ряде уроков ещё могут оставаться старые.
18 дек. 2015 13:37

Ответ на сообщение пользователя Иван Любовников
У нас были изменения в интерфейсе, теперь достаточно выбрать вверху Engine -> Blend4web (на скриншоте уже выбрано). После этого в закладках будут присутствовать только настройки, относящиеся к аддону и удалены лишние.

Скриншоты в документации мы обновили, но в ряде уроков ещё могут оставаться старые.

Тогда у меня вопрос -как назначить тип "Character"? Делаю так: скопировал полностью весь каталог с примером /deploy/tutorials/examples/firstperson/. Создаю в блендере плоскость, масштабирую ее, назначаю материал, в свойствах материала ставлю галочку "Special: Collision", далее - галочку в закладке "Physics" - "Object Physics". Создаю Куб (вернее, не удаляю первоначальный), также назначаю материал, в свойствах материала ставлю галочку "Special: Collision", пытаюсь выбрать "Physics" - "Object Physics", "Physics Type" - "Character" и получаю предупреждение - "unsupported physics type". Но настоящие индейцы не сдаются. Выбираю "Physics Type" - "Dynamic", Ставлю галочку "Character" (самый нижний раздел в Physics). Вроде все ОК, экспортирую как "first-person.json" прямо в копию каталога firstperson (с заменой) и получается мелькающая картинка с кубом, который бегает по всему экрану. Даже если в свойствах куба запретить рендеринг, то все равно плоскость мелькает. Налицо фигня с привязкой камеры к объекту "Character". Что можно сделать?
18 дек. 2015 14:21
Добрый день,

Создаю Куб (вернее, не удаляю первоначальный), также назначаю материал, в свойствах материала ставлю галочку "Special: Collision", пытаюсь выбрать "Physics" - "Object Physics", "Physics Type" - "Character" и получаю предупреждение - "unsupported physics type"
Для динамических физических объектов не нужно добавлять физический материал. Требуется только выбрать в настройках физики соответствующий "Physics Type". Об этом можно прочитать в документации. В вашем случае физический материал все-равно переопределиться физикой объекта, но лучше этот материал убрать с персонажа, чтобы не было путаницы.
Блендеровский Character действительно у нас не поддерживается, и используется свой Character. Возможно, в будущих релизах перейдем на блендеровскую модель.

Вроде все ОК, экспортирую как "first-person.json" прямо в копию каталога firstperson (с заменой) и получается мелькающая картинка с кубом, который бегает по всему экрану
Здесь нужно посмотреть в консоль. Она скажет, что у камеры неверный тип. Приложение firstperson писалось для камеры типа EYE. Именно этот тип вам и нужно выставить.

И ещё один момент. На статической физике (плоскость Plane) следует применить Scale, иначе будет использоваться меш с уменьшенным размером.
29 окт. 2016 01:05
Спасибо за урок! Но я не до конца понял как работать с клавишами, а именно - не могу сделать активацию клавишей (допустим KEY_E) вместо активации кликом мыши(. То есть имеется FPS контроллер, и для него нужна привычная нам активация на клавишу действия,как в большинстве FPS игр, а не клик мышью. Возможно, это очень просто, но я совсем новичок в JS( Не могу допереть. Подскажите, пожалуйста, каким образом можно реализовать это
29 окт. 2016 15:46
Так и не разобрался, как же сэмулировать Select. Кастую луч из камеры, а как выбрать объект, попавший под луч - хз( Именно так выбрать, чтобы Нода swith select поняла в итоге
Пожалуйста, зарегистрируйтесь или войдите под своей учетной записью , чтобы оставлять сообщения.