События

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

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:12
Еее! Даешь продолжение! С зомби там и двустволкой!
13 окт. 2015 15:38
а почему игра так сильно подглючивает?
13 окт. 2015 16:38
Добрый день,
Что именно у вас "подглючивает"? Вы имеете в виду низкую производительность? Если так, то скажите, пожалуйста, какая у вас конфигурация?
04 дек. 2015 15:25
повторил урок (то есть скопировал код), почему то персонаж начинает двигаться по плоскости после комбинации Alt+Shift+W (совершенно случайно получилось), а так просто летает в пространствеMegaMall.7z….
И Jump не работает
07 дек. 2015 13:21
не поможите разобраться?… вроде все по уроку…
08 дек. 2015 10:02

смело задавайте вопросы и оставляйте свои пожелания в комментариях
а вы не отвечаете


все просмотрел… вроде все так же…. а обезьяна не падает на пол как тут
08 дек. 2015 10:34

все просмотрел… вроде все так же…. а обезьяна не падает на пол как
У вас в функции load_cb() инициализируется режим управления камерой (enable_camera_controls) - он, видимо, конфликтует с режимом управления персонажем, который задаётся в setup_movement().

Закомментируйте строчку:
m_app.enable_camera_controls();

и должно заработать.
08 дек. 2015 10:47
уря, уря, уря) заработало! спасибо!

но не прыгает обезьянка…
08 дек. 2015 14:31

но не прыгает обезьянка…
Хмм, это похоже на баг, вообщем посмотрим. Конкретно с этим мешем сейчас заработает, если в настройках физики объекта выставить Bounds: Cylinder или Box.
08 дек. 2015 15:04
Урррррааааа, заработало!!!

выставил Bounds: Box.
Пожалуйста, зарегистрируйтесь или войдите под своей учетной записью , чтобы оставлять сообщения.