Эта запись — блокнот для записи небольших заметок о некоторых неочевидных особенностях, всплывающих при разработке игр на Unreal Engine 4.
Все эти особенности приходится гуглить, а потом о них быстро забываешь, поэтому будет полезно хранить для себя и человечества эту информацию.
Речь будет идти в основном о «программировании мышкой» посредством BluePrint’ов.
Как сделать чтобы персонаж (Character) превращался в ragdoll и падал на землю?
1. Для этого нужно, чтобы у Skeletal Mesh’а, используемого в Character’е был привязан Physical Asset. Проверить, привязан ли он, можно открыв Skeletal Mesh в редакторе (дважды кликнув по нему в Content Browser), и посмотрев на параметр Physical Asset.
2. Важно, чтобы Physical Asset был «полноценным», то есть чтобы все части в нём были представлены отдельными элементами, а не одним большим монолитом на всё тело. Чтобы проверить, так ли это, нужно открыть в Content Browser нужное Physical Body и в редакторе сверху нажать кнопку Simulate, которая запускает симуляцию физики и показывает что будет с телом в этой ситуации. Если всё правильно — тело должно повалиться на пол как ragdoll, а не одним куском.
2.1. Если тело (Physical Asset) у вас сделано неправильно, одним куском (что, например, именно так у входящего в стандартный набор персонажа Mannequin), нужно сгенерировать новое тело на базе скелета. Нажимаем на Skeletal Mesh правой кнопкой (в Content Browser) и выбираем Create -> Physical Asset. После чего нужно открыть Skeletal Mesh и в настройках указать в качестве физического тела свежесозданное тело.
2.2. После создания и привязки физического тела нужно открыть его, и опять, нажав Simulate, убедиться в том, что всё работает как следует.
3. Для переведения тела персонажа в режим Ragdoll нужно назначить ему соответсвующий тип столкновений. В коде который у вас отвечает за смерть, нужно вызвать Mesh->setCollisionPreset(«Ragdoll») (Здесь и далее имею ввиду именно Blueprint).
4. И чтобы заставить тело подчиниться гравитации, нужно включить симуляцию физики: Mesh->setSimulatePhysics(true), после этого тело упадёт и начнёт беспорядочно валяться по полу.
Как сделать чтобы тело повалялось и успокоилось?
5. Чтобы тело не валялось слишком долго, крутя своими частями и завязываясь в узлы, его хотелось бы успокоить. Если просто отключить обработку физики — тело снова выпрямится и будет стоять так как ему положено по анимации. Чтобы решить эту проблему, можно создать компонент PoseableMesh. Он, по сути, является аналогом Skeletal Mesh, которого можно поставить в некую «позу» и в ней и оставить. В нашем случае эта поза должна соответствовать положению Skeletal Mesh. Создадим функцию, назвав её, например, FreezeMesh, и нарисуем в ней такую конструкцию:
6. Эту функцию можно будет вызывать когда вам надоело, что тело валяется и дрыгается на земле, скажем через пару секунд. Для этого нужно создать Custom Event в Event Graph’е, и вызывать из него команду Delay(2), то есть, приостановить выполнение на 2 секунды, а из неё уже вызывать FreezeMesh.
Как сделать, чтобы после смерти персонаж перестал подчиняться Controller’у?
Если при смерти у вашего персонажа просто проигрывается анимация смерти, а не включается ragdoll, то персонаж будет продолжать подчиняться Controller’у, например, враг будет даже мёртвым продолжать ползти за героем. Чтобы этого не случилось, достаточно в момент смерти отключить управление персонажа контроллеров. В функции, которая у вас обрабатывает смерть персонажа, достаточно вызвать getAiController->UnPosess.
Внимание! После вызова UnPosess контроллер отстыкуется от pawn’а, и обратиться к нему через getAiController будет уже невозможно!(чтобы осознать это, потребовалось несколько дней отладки)
Сделать чтобы мяч, пинаемый персонажем не крутился задом наперёд
Если вас персонаж создан на базе Character, а мяч (или что-то ещё круглой формы, например голова врага) на основе Actor, в котором вы в качестве корневого элемента используете SphereCollision, то когда ваш персонаж пинает мяч, он может крутиться задом наперёд, не так как будто он катится а так, как будто он «кручёный» и летит над землёй вращаясь в противоположную сторону.
Чтобы решить эту проблему, нужно:
1) Найти внутри вашего персонажа, в дереве компонентов, компонент CharacterMovement
2) в нём найти параметр Push Factor Point ZOffset и установить его = 0
Как сделать простейший объект-пулю
Время от времени возникает потребность создать объект, движущийся как пуля и умеющий обрабатывать столкновение с препятствиями.
Это может быть обычная пуля, ракета, вращающийся кирпич, горящий череп или птица, не принципиально.
Основные важные моменты этого создания:
1) Нужно создать новый Blueprint, наследованный от Actor, назвать его, скажем Bullet.
2) Внутри Bullet, в дереве компонентов, где изначально есть только корневой объект сцены, нужно добавить компоненты: Projectile Movement (чтобы двигался как пуля), Static/Skeletal Mesh (в зависимости от того, статическая у вас модель или скелетная) назовём его body, и Sphere/Capsule/Cube Collision (выбирайте более подходящий) для обработки столкновений, назовём её collider.
3) Настройка body (Static/Skeletal Mesh):
3.1) Сделать базовые настройки — выбрать модель, материал, если нужно — анимацию, выбрать масштаб.
3.2) Повернуть модель носом в сторону оси X, так проще будет позиционировать «пулю» при выстреле, и лететь она будет не боком а передом.
3.3) Все настройки столкновений (Collision) нужно отключить, данная модель вообще не должна ни сталкиваться ни overlap’ить с другими объектами. Эту работу будет делать collider.
3.4) Не включать симуляцию физики, так как тогда Projectile Movement не сможет управлять движением вашей пули.
4) Настройка Projectile Movement
4.1) Найти настройку Velocity (скорость), которая задаётся координатами по X, Y, Z, и указать скорость в поле X, например, 1000. Потом подберёте более подходящую.
4.2) Указать гравитацию, либо вообще отключить её (сделать равной нулю) в настройке gravity
5) Настройка collider
5.1) Первым делом коллайдер нужно сделать корневым элементом дерева компонентов. Для этого в дереве компонентов нужно мышкой перетащить collider поверх корневого элемента — тогда остальные элементы станут находиться внутри него.
5.2) В настройках Collision поставить галки Hit simulation Generation и Overlap simulation Generation, чтобы у вас обрабатывались события от пересечения и столкновения.
5.3) В настройках Collision выбрать правильный тип, например WorldDynamic, или настроить столкновения и пересечения с разными объектами вручную.
6) Обработка столкновений и пересечений:
В EventGraph добавить узлы OnBeginOverlap и OnHit, к ним подключить узлы обработчиков, самое простое — просто PrintString(«Пересечение»), после чего DestroyActor (уничтожить пулю).
7) Выстрел:
В акторе, который отвечает за выстрел (например, персонаж, или некое оружие), в обработчике, который занимается выстрелами данной пулей, достаточно вызвать SpawnActorFromClass, указав ваш класс bullet, и правильно задав положение пули в пространстве и её поворот (направление полёта).
Поворот персонажа, управляемого ИИ в сторону врага
1) Для плавного поворота, особенно, когда есть именно враг, можно использовать Controller->setFocus(targetActor)
2) Для поворота на указанную точку (когда врага нет, а надо просто посмотреть куда-то, можно использовать Controller->setFocalPoint(targetPointVector). Плавность поворота при этом не гарантируется.
Не получается сделать плавное вращение через setFocus, в чем может быть проблема?
Вот например: http://www.dreambotstudios.com/ue4-make-ai-rotate-smoothly-avoid-snapping/
Не получается, все равно рывком поворачивается.
проверяю так: у меня в btree стоит задержка, а потом setFocus на pawn игрока, когда он за спиной ai находится
В идеале я ищу что то типа setControlRotation что бы можно было вручную к нужной точке поворачивать через rinterp, но setControlRotation не работает
Нашел, что надо убрать галку в aicontroller SetControlRotationFromPawnOrientation, тогда setControlRotation работает, но не учитывает pitch? а это проблема. Верх/вниз тоже надо смотреть
С pitch проблема, когда мне нужно было сделать «самодельную» управляемую ракету (как в Half-life, которая летит в направлении точки где лазерный луч падает на поверхность), чтобы она поворачивала с нужной мне скоростью, а не резко и не так как это делает setFocus, пришлось писать свой алгоритм.
1) Определяем вектор направления ракеты
2) Определяем вектор «нужного» направления, куда ракета должна повернуть (в сторону лазера)
3) Находим угол между векторами. Предполагается, что скорость поворота изменяется в градусах в секунду. Поэтому надо определять на какую долю угла нужно повернуть.
4) Делаем оба вектора единичными, проводим между их концами новый вектор, делим его на угол и умножаем на угловую скорость. То что получилось добавляем к вектору скорости ракеты, а после этого вектор скорости ракеты домножаем на некую величину чтобы его длина стала такой же как была до изменения (тогда получится что ракета именно поворачивает, а не ускоряется в направлении цели).
В случае когда надо сделать плавный поворот по pitch без изменения скорости (у ракеты тело было настроено всегда поворачивать в направлении скорости), достаточно просто определить угол межу целью и реальным направлением, и добавить к реальному углу небольшой угол равный скорости * dt, где dt — это длина тика (не помню как в UE переменная называется)