Шутер от первого лица (first-person shooter, FPS) — это жанр, в котором игрок использует оружие и смотрит на мир глазами персонажа. FPS-игры чрезвычайно популярны, что видно по успеху таких франшиз, какCall of DutyиBattlefield.
Unreal Engine изначально был создан для разработки FPS, поэтому вполне логично использовать его для создания такой игры. В этом туториале вы научитесь следующему:
- Создавать Pawn с видом от первого лица, который сможет двигаться и осматриваться вокруг
- Создавать оружие и привязывать его к Pawn игрока
- Стрелять пулями с помощью трассировки прямых (также известной как трассировка лучей)
- Наносить урон акторам
Примечание:эта статья является десятой частью серии туториалов, посвящённых движку Unreal Engine:
- Часть 1: Знакомство с движком
- Часть 2: Blueprints
- Часть 3: Материалы
- Часть 4: UI
- Часть 5: Как создать простую игру
- Часть 6: Анимация
- Часть 7: Звук
- Часть 8: Системы частиц
- Часть 9: Искусственный интеллект
- Часть 10: Как создать простой FPS
Приступаем к работе
Скачайтезаготовку проектаи распакуйте её. Перейдите в папку проекта и откройтеBlockBreaker.uproject. Вы увидите следующую сцену:
Зелёная стена состоит из множества целей. Когда им наносят урон, они становятся красными. Когда их здоровье достигает нуля, они исчезают. Красная кнопка заново устанавливает все цели.
Для начала нужно создать Pawn игрока.
Создание Pawn игрока
Перейдите в папкуBlueprintsи создайте новыйBlueprint Class. Выберите в качестве родительского классаCharacterи назовите егоBP_Player.
Character— это разновидность Pawn, но с дополнительным функционалом, например, с компонентомCharacterMovement.
Этот компонент автоматически обрабатывает движение, например, ходьбу и прыжки. Мы просто вызываем соответствующую функцию, благодаря чему Pawn перемещается. В этом компоненте также можно задать переменные, такие как скорость ходьбы и прыжков.
Чтобы заставить Pawn двигаться, нам нужно знать, когда игрок нажимает клавишу движения. Для этого мы привяжем движение к клавишамW,A,SиD.
Примечание:если вы незнакомы с привязками, то можете прочитать о них в части туториала, посвящённойBlueprints. Привязкой клавиш мы определяем, какие клавиши будут выполнять действие.
Создание привязок движения
ВыберитеEdit\Project Settingsи откройте настройкиInput.
Создайте дваAxis Mappingsпод названиемMoveForwardиMoveRight. MoveForward будет управлять движением вперёд и назад. MoveRight — движением влево и вправо.
ДляMoveForwardзамените клавишу наW. После этого создайте ещё одну клавишу и выберитеS. ИзменитеScaleдляSна-1.0.
Примечание:если вы хотите подробнее узнать о полеScale, прочитайте часть туториала, посвящённую Blueprints. В разделе «Значение оси и масштаб ввода» говорится, что это и как этим пользоваться.
Позже мы будем умножать значение масштаба на вектор forward Pawn. Это даст нам вектор, направленныйвперёдприположительноммасштабе. Если масштаботрицателен, то вектор будет направленназад. С помощью получившегося вектора можно будет двигать Pawn вперёд и назад.
Теперь нам нужно сделать то же для движения влево и вправо. Измените клавишу дляMoveRightнаD. Затем создайте новую клавишу и выберите для неёA. ИзменитеScaleдляAна-1.0.
Теперь, когда мы настроили привязки, нам нужно использовать их для движения.
Реализация движения
ОткройтеBP_Player, а затем откройте Event Graph. Добавьте событиеMoveForward(то, которое указано в спискеAxis Events). Это событие будет выполняться в каждом кадре, даже если вы ничего не нажимаете.
Также оно будет подавать на выход значениеAxis Value, которое равно заданным ранее значениямScale. Оно будет подавать на выход1при нажатии наWи-1при нажатии наS. Если не нажимать клавиши, то на выходе будет0.
Далее нужно приказать Pawn двигаться. ДобавьтеAdd Movement Inputи соедините его следующим образом:
Add Movement Inputбудет получать вектор и умножать его наScale Value. Это преобразует его в соответствующем направлении. Поскольку мы используемCharacter, то компонентCharacterMovementбудет перемещать Pawn в этом направлении.
Теперь нам нужно указать направление движения. Так как мы хотим двигаться вперёд, то можем использоватьGet Actor Forward Vector. При этом будет возвращаться вектор, направленный вперёд. Создайте его и соедините следующим образом:
Подведём итог:
- MoveForwardвыполняется каждый кадр и передаёт на выходAxis Value. Это значение будет равно1при нажатии наWи-1при нажатии наS. Если не нажимать ни одну из этих клавиш, то на выходе будет0.
- Add Movement Inputумножаетвектор forwardPawn наScale Value. Благодаря этому в зависимости от нажатой клавиши вектор будет направлен вперёд или назад. Если не нажимать клавиши, то вектор не будет иметь направления, то есть Pawn не будет двигаться.
- КомпонентCharacterMovementполучает результат изAdd Movement Input, после чего двигает Pawn в этом направлении.
Повторим процесс дляMoveRight, но заменимGet Actor Forward VectorнаGet Actor Right Vector.
Для проверки движения нужно задать Pawn по умолчанию в игровом режиме.
Задание Pawn по умолчанию
Нажмите наCompileи вернитесь в основной редактор. Откройте панельWorld Settingsи найдите разделGame Mode. Измените значениеDefault Pawn ClassнаBP_Player.
Примечание:если у вас нет панели World Settings, то перейдите в Toolbar и выберитеSettings\World Settings.
Теперь при запуске игры вы автоматически будете использоватьBP_Player. Нажмите наPlayи воспользуйтесь клавишамиW,A,SиDдля движения по уровню.
Создание привязок обзора
Теперь мы создадим привязки для поворота головы.
ОткройтеProject Settings. Создайте ещё дваAxis Mappingsпод названиемLookHorizontalиLookVertical.
Замените клавишу дляLookHorizontalнаMouse X.
Эта привязка будет выдавать положительное значение при перемещении мышивправо, и наоборот.
Теперь изменим клавишу дляLookVerticalнаMouse Y.
Эта привязка будет выдавать положительное значение при перемещении мышивверх, и наоборот.
Теперь нам нужно создать логику, чтобы смотреть вокруг.
Реализация обзора
Если у Pawn нет компонентаCamera, то Unreal автоматически создаёт камеру за вас. По умолчанию, камера будет использовать поворотконтроллера.
Примечание:если вы хотите узнать больше о контроллерах, то изучите наш туториал «Искусственный интеллект».
Несмотря на то, что контроллеры не являются физическими, у них всё равно есть свой поворот. Это значит, что можно направить взгляд Pawn и камеры в разных направлениях. Например, в игре от третьего лица персонаж и камера не всегда смотрят в одном направлении.
Для поворота камеры в игре от первого лица нам достаточно изменить поворот контроллерая.
ОткройтеBP_Playerи создайте событиеLookHorizontal.
Чтобы заставить камеру поворачиваться влево или вправо, нам нужно регулироватьрыскание (yaw). СоздайтеAdd Controller Yaw Inputи соедините его следующим образом:
Теперь при горизонтальном движении мыши контроллер будет поворачиваться влево или вправо. Поскольку камера использует поворот контроллера, она тоже будет поворачиваться.
Повторите процесс дляLookVertical, но заменитеAdd Controller Yaw InputнаAdd Controller Pitch Input.
Если запустите игру сейчас, то заметите, что вертикальное движение камерыинвертировано. Это значит, что при движении мышивверхкамера будет смотретьвниз.
Если вы предпочитаете неинвертированное управление, то умножьтеAxis Valueна-1. Это инвертируетAxis Value, что инвертирует наклон контроллера.
Нажмите наCompileи нажмитеPlay. Оглянитесь вокруг с помощью мыши.
Теперь, когда всё движение и обзор готовы, пришла пора создавать оружие!
Создание оружия
Помните, что при создании Blueprint Class можно выбрать родительский класс? Можно так же выбирать в качестве родительских собственные Blueprints. Это полезно, когда имеются разные типы объектов, обладающие общим функционалом или атрибутами.
Допустим, нам нужно создать разные типы автомобилей. Можно создать базовый класс автомобиля, содержащий такие переменные, как скорость и цвет. Затем можно создать классы (дочерние), которые будут использовать в качестве родительского базовый класс автомобиля. Каждый дочерний класс будет содержать те же переменные. Теперь у вас есть простой способ для создания машин с разными значениями скорости и цвета.
Этим же способом можно создавать оружие. Для этого необходимо для начала создать базовый класс.
Создание базового класса оружия
Вернитесь в основной редактор и создайтеBlueprint ClassтипаActor. Назовите егоBP_BaseGunи откройте.
Теперь мы создадим несколько переменных, задающих свойства оружия. Создайте следующие переменные типаfloat:
- MaxBulletDistance:максимальная дальность полёта каждой пули
- Damage:количество урона, наносимого пулей при попадании в актора
- FireRate:промежуток (в секундах) между выстрелами пуль оружием
Примечание:по умолчанию значения всех переменных равны нулю, что вполне подходит для туториала. Однако если вы хотите, чтобы у новых классов оружия было другое значение по умолчанию, то его нужно задать вBP_BaseGun.
Теперь нам нужно физическое представление этого оружия. Добавьте компонентStatic Meshи назовите егоGunMesh.
Пока не волнуйтесь о выборе статического меша. Мы займёмся этим в следующем разделе, где будем создавать дочерний класс оружия.
Создание дочернего класса оружия
Нажмите наCompileи вернитесь в основной редактор. Для создания дочернего классанажмите правой клавишей мышинаBP_BaseGunи выберитеCreate Child Blueprint Class.
Назовите егоBP_Rifleи откройте. ОткройтеClass Defaultsи задайте переменным следующие значения:
- MaxBulletDistance:5000
- Damage:2
- FireRate:0.1
Это значит, что максимальный путь каждой пули будет равен5000. Если она попадёт в актора, то нанесёт2единицы урона. При стрельбе очередями интервал перед каждым выстрелом будет равен не менее чем0.1секунды.
Теперь нам нужно указать меш, который будет использоваться оружием. Выберите компонентGunMeshи выберите дляStatic MeshзначениеSM_Rifle.
Теперь оружие готово. Нажмите наCompileи закройтеBP_Rifle.
Теперь мы создадим собственный компонент камеры. Он даст нам более удобное управление расположением камеры. Также он позволит нам присоединить оружие к камере, чтобы оно всегда находилось перед камерой.
Создание камеры
ОткройтеBP_Playerи создайте компонентCamera. Назовите егоFpsCamera.
Позиция по умолчанию находится довольно низко, из-за чего игрок может почувствовать себя маленьким. ИзменитеlocationкамерыFpsCameraна(0, 0, 90).
По умолчанию, компоненты Camera не используют поворот контроллера. Чтобы исправить это, перейдите в панель Details и включитеCamera Settings\Use Pawn Control Rotation.
Теперь нам нужно задать место, в котором должно находиться оружие.
Задание местоположения оружия
Для создания местоположения оружия мы можем использовать компонентScene. Эти компоненты идеально подходят для задания местоположений, потому что содержат только Transform. ВыберитеFpsCameraи создайте компонентScene. Таким образом он прикрепится к камере. Назовите егоGunLocation.
Благодаря тому, что мы прикрепилиGunLocationкFpsCamera, оружие постоянно будет сохранять своё положение относительно камеры. Таким образом, мы всегда будем видеть оружие перед камерой.
Теперь присвоимlocationкомпонентаGunLocationзначения(30, 14, -12). Так мы расположим оружие впереди и слегка сбоку от камеры.
Затем зададимrotationзначения(0, 0, -95). Когда мы прикрепим оружие, то будет казаться, что оно всегда направлено в центр экрана.
Теперь нам нужно создать оружие и прикрепить его кGunLocation.
Создание и прикрепление оружия
НайдитеEvent BeginPlayи создайтеSpawn Actor From Class. Выберите дляClassзначениеBP_Rifle.
Поскольку нам нужно будет использовать оружие позже, мы сохраним его в переменной. Создайте переменную типаBP_BaseGunи назовите еёEquippedGun.
Важно то, что переменнаянеимеет типBP_Rifle, потому что игрок может иметь разные типы оружия, а не только одно. Если создать другой тип оружия, то мы не сможем хранить его в переменной типаBP_Rifle. Это будет похоже на то, что мы пытаемся засунуть круг в прямоугольное отверстие.
Выбрав для переменной типBP_BaseGun, мы создали большое «отверстие», подходящее под многие «фигуры».
Теперь присвоимEquippedGunзначениеReturn ValueSpawn Actor From Class.
Чтобы прикрепить оружие, мы можем использоватьAttachToComponent. Создайте его и задайте дляLocation RuleиRotation RuleзначениеSnap to Target. Благодаря этому оружие будет иметь то же местоположение и поворот, что и родитель.
Теперь создадим ссылку наGunLocationи соединим всё следующим образом:
Подведём итог:
- При созданииBP_Playerон будет создавать экземплярBP_Rifle
- EquippedGunбудет хранить ссылку на созданныйBP_Rifleдля дальнейшего использования
- AttachToComponentприсоединяет оружие кGunLocation
Нажмите наCompileи нажмитеPlay. Теперь при создании игрока будет создаваться и оружие! При поворотах оружие будет всегда находиться перед камерой.
Теперь начинается интересное: мы будем стрелять! Чтобы проверить, попала ли куда-нибудь пуля, мы можем использоватьтрассировку прямых.
Стрельба пулями
Трассировка прямых — это функция, получающая начальную и конечную точки (образующие прямую). Она проверяет каждую точку на прямой (от начала до конца), пока на что-нибудь не наткнётся. В играх для проверки попадания пули чаще всего используется такой способ.
Поскольку стрельба — это функция оружия, она должна выполняться в классе оружия, а не игрока. ОткройтеBP_BaseGunи создайте функциюShoot.
Затем создайте два входаVectorи назовите ихStartLocationandEndLocation. Они будут начальной и конечной точками трассировки прямых (которые мы будем передавать изBP_Player).
Трассировку прямых можно выполнять с помощьюLineTraceByChannel. Этот нод проверяет попадания с помощью канала коллизийVisibilityилиCamera. Создайте его и соедините следующим образом:
Теперь нам нужно проверить, попадает ли во что-нибудь трассировка прямых. СоздайтеBranchи соедините его так:
При попаданииReturn Valueбудет выдавать на выходtrue, и наоборот.
Чтобы дать игроку наглядную обратную связь о том, куда попала пуля, можно использовать эффект частиц.
Создание частиц попадания пули
Сначала нам нужно получить местоположение попадания трассировки.Перетащите левой клавишей мышиOut Hitна граф. В меню выберитеBreak Hit Result.
Так мы создадим нод с разными контактами, относящимися к результатам трассировки прямой.
СоздайтеSpawn Emitter at Locationи задайте дляEmitter TemplateзначениеPS_BulletImpact. Затем соедините егоLocationсLocationнодаBreak Hit Result.
Вот как это будет выглядеть:
Подведём итог:
- При выполненииShootона выполняет трассировку прямой с переданными начальной и конечной точкой
- Если попадание зафиксировано, тоSpawn Emitter at LocationсоздастPS_BulletImpactв точке попадания
Теперь, когда логика стрельбы завершена, нам нужно воспользоваться ею.
Вызов функции Shoot
Для начала нам нужно создать привязку клавиш для стрельбы. Нажмите наCompileи откройтеProject Settings. Создайте новуюAxis Mappingпод названиемShoot. Выберите для неё клавишуLeft Mouse Buttonи закройтеProject Settings.
Затем откройтеBP_Playerи создайте событиеShoot.
Чтобы проверить, нажимает ли игрок клавишуShoot, нам достаточно проверить, равно ли значениеAxis Valueединице (1). Создайте выделенные ноды:
Затем создайте ссылку наEquippedGunи вызовите его функциюShoot.
Теперь нам нужно вычислить начальную и конечную точки для трассировки прямой.
Вычисление точек трассировки прямой
Во многих FPS пуля летит из камеры, а не из оружия. Так делают потому, что камеру уже и так идеально выровнена с прицелом. Поэтому если мы будем стрелять из камеры, то пуля гарантированно полетит туда, где находится курсор.
Примечание:некоторые игры всё-таки реализуют стрельбу из оружия. Однако для стрельбы ровно в прицел требуются дополнительные вычисления.
Создайте ссылку наFpsCameraи соедините её сGetWorldLocation.
Теперь нам нужна конечная точка. Не забывайте, что у оружия есть переменнаяMaxBulletDistance. Это значит, что конечная точка должна находится на расстоянииMaxBulletDistanceединиц от камеры. Чтобы реализовать это, создайте выделенные ноды:
Затем соедините всё следующим образом:
Подведём итог:
- Когда игрок нажимает или удерживаетлевую клавишу мыши, оружие будет выпускать пулю с начальной точкой вкамере
- Пуля пролетит расстояние, указанное вMaxBulletDistance
Нажмите наCompile, а затем наPlay. Удерживайтелевую клавишу мыши, чтобы начать стрельбу.
Пока оружие стреляет в каждом кадре. Это слишком быстро, поэтому на следующем этапе мы уменьшим частоту стрельбы оружия.
Уменьшение частоты стрельбы
Во-первых, нам нужна переменная, чтобы определить, может ли игрок стрелять. ОткройтеBP_Playerи создайте переменную типаbooleanс названиемCanShoot. Задайте ей значениеtrueпо умолчанию. ЕслиCanShootравнаtrue, то игрок может стрелять, и наоборот.
Замените разделBranchна следующее:
Теперь игрок может стрелять только если нажата клавишаShootи переменнаяCanShootравнаtrue.
Теперь добавим выделенные ноды:
- Игрок может стрелять, только когда удерживаетлевую клавишу мыши, и когдаCanShootравнаtrue
- Когда игрок стреляет пулей, переменнойCanShootприсваивается значениеfalse. Это не позволит игроку выстрелить снова.
- CanShootбудет снова присвоено значениеtrueчерез промежуток времени, указанный вFireRate
Нажмите наCompileи закройтеBP_Player. НажмитеPlayи проверьте новую частоты стрельбы.
Теперь мы научим цели и кнопку реагировать на пули. Это можно сделать, добавив имурон.
Применение урона
Каждый актор в Unreal имеет возможность получения урона. Однако вы можете выбирать сами,какактор будет на него реагировать.
Например, персонаж файтинга при получении урона будет терять здоровье. Однако некоторые объекты, например, воздушный шарик, могут не иметь здоровья. Тогда можно запрограммировать шарик, чтобы он взрывался при получении урона.
Для того, чтобы можно было управлять получением актором урона, нам сначала нужно применить урон. ОткройтеBP_BaseGunи добавьтеApply Damageв конце функцииShoot.
Теперь нам нужно указать, какому актору мы хотим наносить урон. В нашем случае это актор, в которого попадает трассировка прямой. СоединитеDamaged ActorсHit ActorнодаBreak Hit Result.
Наконец, нам нужно указать величину нанесённого урона. Получите ссылку наDamageи соедините её сBase Damage.
Теперь при вызовеShootона будет наносить урон акторам, попавшим в трассировку прямой. Нажмите наCompileи закройтеBP_BaseGun.
Теперь нам нужно обработать то, как актор получает урон.
Обработка урона
Сначала мы обработаем то, как урон получают цели. ОткройтеBP_Targetи создайтеEvent AnyDamage. Это событие выполняется, когда актор получает урон,не равный нулю.
Теперь вызовите функциюTakeDamageи соедините контактыDamage. Это вычтет здоровье из переменнойHealthцели и обновит цвет цели.
Теперь, когда цель получает урон, она теряет здоровье. Нажмите наCompileи закройтеBP_Target.
Теперь нам нужно обработать то, как получает урон кнопка. ОткройтеBP_ResetButtonи создайтеEvent AnyDamage. Затем вызовите функциюResetTargets.
Это будет восстанавливать все цели при получении урона кнопкой. Нажмите наCompileи закройтеBP_ResetButton.
Нажмите наPlayи начните стрелять по целям. Если вы хотите восстановить цели, то выстрелите по кнопке.
Куда двигаться дальше?
Готовый проект можно скачатьотсюда.
Хотя созданный в этом туториале FPS очень прост, его можно запросто расширить. Попробуйте создать больше оружия с разными типами частоты стрельбы и урона. Можете даже попробовать добавить функцию перезарядки!