Геймплейный фреймворк
Классы геймплейного фреймворка Unreal — Actor, Pawn, Character, контроллеры, GameMode, GameState, PlayerState, компоненты и подсистемы.
23 вопросов
JuniorТеорияОчень частоВ чём разница между AActor и APawn в Unreal Engine?
В чём разница между AActor и APawn в Unreal Engine?
Каждый APawn — это AActor, но Pawn добавляет возможность быть захваченным AController. Pawn представляет агента — игрока или ИИ — который получает ввод или управление ИИ. Обычный Actor — это любой объект мира без понятия захвата.
Типичные ошибки
- ✗Думать, что Pawn не связан с Actor —
APawnнаследуется отAActor - ✗Считать, что только Pawn может иметь меш или компоненты — это может любой Actor
- ✗Полагать, что Pawn получает ввод сам по себе, не будучи захваченным
Уточняющие вопросы
- →Что происходит с вводом Pawn, когда контроллер освобождает его?
- →Могут ли два контроллера захватить один Pawn одновременно?
JuniorТеорияОчень частоЧто такое AActor в Unreal Engine и чем он особенный?
Что такое AActor в Unreal Engine и чем он особенный?
AActor — это любой UObject, который можно разместить или заспавнить на уровне. Он содержит компоненты, имеет трансформацию через свой корневой компонент, тикает, реплицируется и проходит жизненный цикл BeginPlay/EndPlay. Это единица геймплейного содержимого в мире.
Типичные ошибки
- ✗Думать, что у Actor обязательно есть видимый меш — многие Actor'ы это невидимые держатели логики
- ✗Путать сам Actor с его корневым компонентом — трансформация Actor берётся из корневого компонента
- ✗Считать, что Actor'ы существуют только на сервере; они создаются и на клиентах
Уточняющие вопросы
- →Как Actor получает мировую трансформацию, если трансформации хранят компоненты, а не сам Actor?
- →В чём разница между
Destroy()и выходом Actor из области видимости?
JuniorТеорияОчень частоВ чём разница между APawn и ACharacter в Unreal Engine?
В чём разница между APawn и ACharacter в Unreal Engine?
ACharacter — это APawn, специализированный для гуманоидных двуногих. Он поставляется с UCapsuleComponent, скелетным мешем и UCharacterMovementComponent, дающим реплицируемые ходьбу, прыжки и падение. У голого Pawn ничего этого нет, и ему нужно своё движение.
Типичные ошибки
- ✗Переворачивать иерархию —
ACharacterнаследуется отAPawn, а не наоборот - ✗Думать, что любой Pawn получает ходьбу/прыжки бесплатно — это только у
ACharacter - ✗Использовать
ACharacterдля негуманоидных агентов вроде машин или турелей
Уточняющие вопросы
- →Когда стоит использовать голый
APawnвместоACharacter? - →Что и как реплицирует
UCharacterMovementComponent?
JuniorТеорияЧастоЧто такое UActorComponent в Unreal Engine и для чего он используется?
Что такое UActorComponent в Unreal Engine и для чего он используется?
UActorComponent — переиспользуемая часть поведения или данных, принадлежащая AActor. Он инкапсулирует одну ответственность — движение, здоровье, звук — чтобы функциональность собиралась из частей на Actor'ах. Подкласс USceneComponent добавляет трансформацию и привязку.
Типичные ошибки
- ✗Думать, что у каждого компонента есть трансформация — она есть только у
USceneComponentи его подклассов - ✗Считать, что компонент может существовать на уровне без владеющего Actor
- ✗Полагать, что компоненты одного класса разделяют состояние между экземплярами Actor
Уточняющие вопросы
- →В чём разница между
UActorComponentиUSceneComponent? - →Чем корневой компонент особенный по сравнению с другими scene-компонентами?
JuniorТеорияЧастоAPlayerController vs APlayerState — какие данные где в Unreal Engine?
APlayerController vs APlayerState — какие данные где в Unreal Engine?
APlayerController обрабатывает ввод, камеру и HUD игрока и в основном локален для сервера и владельца. APlayerState хранит реплицируемые пользовательские данные — счёт, имя, команду — видимые всем клиентам. Контроллер — для управления, PlayerState — для общей статистики.
Типичные ошибки
- ✗Помещать реплицируемый счёт на контроллер — другие клиенты его там не видят
- ✗Помещать логику ввода или HUD на PlayerState вместо контроллера
- ✗Думать, что контроллер свободно реплицируется всем клиентам, как PlayerState
Уточняющие вопросы
- →Почему удалённый клиент не может прочитать
APlayerControllerдругого игрока? - →Какой объект вы бы опросили для построения таблицы счёта и почему?
JuniorКодЧастоРеализуйте базовый компонент здоровья UActorComponent на C++ для Unreal Engine.
Реализуйте базовый компонент здоровья UActorComponent на C++ для Unreal Engine.
Наследуйтесь от UActorComponent, храните MaxHealth и CurrentHealth, предоставьте ApplyDamage/Heal, которые ограничивают значение, и шлите событие смерти при достижении нуля. Динамический multicast-делегат позволяет владеющему Actor реагировать без жёсткой связанности.
Типичные ошибки
- ✗Не ограничивать здоровье, позволяя ему уйти ниже нуля или выше
MaxHealth - ✗Наследовать держатель здоровья от
AActorвместоUActorComponent - ✗Уничтожать владельца прямо внутри компонента вместо рассылки события
Уточняющие вопросы
- →Как бы вы реплицировали
CurrentHealth, чтобы все клиенты видели полоску здоровья? - →Почему рассылать делегат, а не вызывать функцию смерти владельца напрямую?
JuniorТеорияЧастоЗа что отвечает APlayerController в Unreal Engine?
За что отвечает APlayerController в Unreal Engine?
APlayerController представляет намерение человека-игрока. Он получает ввод, захватывает Pawn, владеет камерой и HUD и является сетевой точкой подключения игрока. Он сохраняется между смертями Pawn, поэтому логика респавна и пользовательский UI живут там.
Типичные ошибки
- ✗Путать контроллер с Pawn — контроллер не является видимым телом
- ✗Помещать матчевое состояние на контроллер вместо GameState
- ✗Считать, что контроллер умирает вместе с Pawn — он переживает респавны
Уточняющие вопросы
- →Почему
APlayerController— лучшее место для HUD, чем Pawn? - →Чем
AAIControllerотличается отAPlayerController?
MiddleТеорияЧастоКогда композиция компонентов лучше наследования в Unreal Engine?
Когда композиция компонентов лучше наследования в Unreal Engine?
Предпочитайте композицию, когда поведение — здоровье, инвентарь, прицеливание — нужно несвязанным типам Actor. UActorComponent переиспользуется на любом Actor, избегая глубоких хрупких иерархий. Наследование подходит лишь для настоящих отношений «является».
Типичные ошибки
- ✗Строить глубокую иерархию Actor для общего поведения вместо вынесения компонентов
- ✗Думать, что композиция только про производительность, а не про переиспользование и развязку
- ✗Использовать наследование для ортогональных поведений, не являющихся настоящим «является»
Уточняющие вопросы
- →Как компонент может предоставлять события владеющему Actor без жёсткой связанности?
- →Какие проблемы возникают, когда много типов Actor наследуются от одного огромного базового класса?
MiddleКодЧастоРеализуйте нанесение урона при столкновении двух Actor'ов в Unreal Engine.
Реализуйте нанесение урона при столкновении двух Actor'ов в Unreal Engine.
Привяжите обработчик к делегату OnComponentHit или OnComponentBeginOverlap компонента столкновений, затем направьте урон через UGameplayStatics::ApplyDamage. Это вызовет TakeDamage у цели, оставляя логику урона на получателе, а не на снаряде.
Типичные ошибки
- ✗Опрашивать перекрытия в
Tickвместо привязки делегата столкновений - ✗Вычитать здоровье напрямую вместо маршрутизации через
ApplyDamage/TakeDamage - ✗Забывать проверить, что задетый Actor не является владельцем снаряда
Уточняющие вопросы
- →В чём разница между событием Hit и событием Overlap?
- →Почему направлять урон через
ApplyDamage, а не через вызов своего интерфейса?
MiddleДебаггингЧастоНайдите и исправьте баг в этом коде ввода движения ACharacter в Unreal Engine.
Найдите и исправьте баг в этом коде ввода движения ACharacter в Unreal Engine.
Обработчик вызывает AddMovementInput с вектором мировой оси, игнорирующим yaw контроллера, поэтому персонаж всегда движется вдоль мировой X независимо от направления взгляда. Исправление — выводить направление из forward-вектора управляющего поворота.
Типичные ошибки
- ✗Использовать фиксированный вектор мировой оси вместо направления относительно поворота управления
- ✗Забывать обнулять pitch/roll, чтобы персонажа не вдавливало в землю
- ✗Путать поворот Actor с управляющим поворотом контроллера
Уточняющие вопросы
- →Почему для движения использовать управляющий поворот, а не поворот Actor?
- →Как бы вы корректно добавили движение вбок (вправо/влево)?
MiddleТеорияЧастоКакая логика должна находиться в AGameModeBase в Unreal Engine?
Какая логика должна находиться в AGameModeBase в Unreal Engine?
GameMode владеет правилами игры: какие классы спавнить, где спавнятся игроки, условия победы и поражения, авторитет над счётом и поток матча. Он существует только на сервере — это авторитетное место для решений по правилам, но не для реплицируемого состояния UI.
Типичные ошибки
- ✗Хранить реплицируемое или пользовательское состояние в GameMode — клиенты его не видят
- ✗Помещать логику ввода/камеры в GameMode вместо PlayerController
- ✗Ожидать, что GameMode существует на клиентах для чтения правил
Уточняющие вопросы
- →Где вместо этого должно жить матчевое состояние, видимое клиентам?
- →В чём разница между
AGameModeBaseиAGameMode?
MiddleТеорияЧастоПочему AGameModeBase существует только на сервере в Unreal Engine?
Почему AGameModeBase существует только на сервере в Unreal Engine?
GameMode — авторитетный арбитр правил; если бы у клиентов была своя копия, они могли бы решать исходы локально и читерить. Хранение его только на сервере гарантирует один источник истины для правил. Общее состояние, которое видят клиенты, живёт в реплицируемом AGameStateBase.
Типичные ошибки
- ✗Пытаться обратиться к указателю GameMode на клиенте — там он всегда null
- ✗Думать, что GameMode реплицирует клиентам копию только для чтения
- ✗Путать серверный GameMode с реплицируемым GameState
Уточняющие вопросы
- →Как клиент может инициировать решение по правилу, если у него нет GameMode?
- →Что произойдёт при вызове
GetGameMode()на клиенте?
MiddleТеорияЧастоКакие данные должны находиться в AGameStateBase в Unreal Engine?
Какие данные должны находиться в AGameStateBase в Unreal Engine?
GameState хранит матчевое состояние, которое нужно видеть каждому клиенту: таймер раунда, счёт команд, текущую фазу и список подключённых APlayerState. Он реплицируется всем клиентам, что делает его общим наблюдаемым аналогом серверного GameMode.
Типичные ошибки
- ✗Помещать правила игры в GameState — правила принадлежат серверному GameMode
- ✗Хранить пользовательские данные в GameState вместо PlayerState
- ✗Думать, что GameState только на сервере, как GameMode
Уточняющие вопросы
- →Как GameState связан с GameMode в рантайме?
- →Как бы вы реплицировали меняющийся таймер раунда через GameState?
MiddleТеорияЧастоКакие данные должны находиться в APlayerState в Unreal Engine?
Какие данные должны находиться в APlayerState в Unreal Engine?
PlayerState хранит пользовательские данные, которые должны пережить Pawn и быть видны всем клиентам: имя, счёт, убийства, команда, пинг. На каждого игрока существует один PlayerState, и он реплицируется, поэтому другие клиенты могут читать статистику каждого игрока.
Типичные ошибки
- ✗Хранить матчевое состояние в PlayerState вместо GameState
- ✗Помещать постоянную статистику игрока на Pawn, который умирает при респавне
- ✗Думать, что PlayerState только локальный и не реплицируется другим клиентам
Уточняющие вопросы
- →Почему PlayerState — лучшее место для счёта, чем Pawn?
- →Как найти PlayerState конкретного игрока через GameState?
MiddleКодЧастоНапишите функцию на C++, которая спавнит AActor в заданной позиции в Unreal Engine.
Напишите функцию на C++, которая спавнит AActor в заданной позиции в Unreal Engine.
Вызовите GetWorld()->SpawnActor<T>(Class, Transform, Params). Постройте FTransform из позиции, передайте FActorSpawnParameters и всегда проверяйте возвращённый указатель на null — спавн может не удаться, если тест столкновений отвергнет позицию.
Типичные ошибки
- ✗Использовать
NewObjectилиnewдля Actor — Actor создаётся только черезSpawnActor - ✗Не проверять результат на null — спавн не удаётся, когда обработка столкновений отвергает место
- ✗Забывать, что
BeginPlayвыполняется как часть спавна, а не до него
Уточняющие вопросы
- →Что контролирует
ESpawnActorCollisionHandlingMethodво время спавна? - →Как отложенный спавн позволяет задать свойства до
BeginPlay?
SeniorТеорияЧастоBeginPlay срабатывает поздно, дважды или вовсе не вызывается в editor, PIE и при реплицированном спавне?
BeginPlay срабатывает поздно, дважды или вовсе не вызывается в editor, PIE и при реплицированном спавне?
BeginPlay выполняется лишь после того, как актор и его компоненты полностью созданы и зарегистрированы, а уровень начал игру. Рантайм-вызов SpawnActor запускает его внутри самого спавна; акторы уровня получают его, когда мир начинает игру; на клиенте он ждёт канал актора и начальное реплицированное состояние. Конструктор и PostInitializeComponents выполняются раньше — не читайте там реплицированные данные.
Типичные ошибки
- ✗Читать реплицированные свойства в конструкторе или
PostInitializeComponentsвместоBeginPlay - ✗Считать, что в
BeginPlayклиента уже доступно всё начальное реплицированное состояние - ✗Ожидать одинаковый порядок
BeginPlayдля акторов с уровня и созданных в рантайме
Уточняющие вопросы
- →Куда поместить логику, которая должна выполниться после прихода начального реплицированного состояния на клиент?
- →Как отложенный спавн через
SpawnActorDeferredменяет момент вызоваBeginPlay?
MiddleКодИногдаРеализуйте универсальную систему перезарядки способностей на C++ для Unreal Engine.
Реализуйте универсальную систему перезарядки способностей на C++ для Unreal Engine.
Запишите время мира при срабатывании способности, затем гейтите следующее использование, сравнивая прошедшее время с длительностью перезарядки. Используйте GetWorld()->GetTimeSeconds() для метки — сравнение времён избегает счётчика в Tick и устойчиво к переменному FPS.
Типичные ошибки
- ✗Отсчитывать в
Tickвместо сравнения меток — дрейфует с частотой кадров - ✗Использовать кадры вместо секунд — ломается при переменном FPS
- ✗Забывать, что первое использование должно проходить, пока метка ещё не записана
Уточняющие вопросы
- →Как бы вы предоставили оставшееся время перезарядки для UI с радиальной заливкой?
- →Как бы вы приостанавливали перезарядки, когда игра на паузе?
MiddleТеорияИногдаЧто такое Subsystems в Unreal Engine и какие их виды существуют?
Что такое Subsystems в Unreal Engine и какие их виды существуют?
Subsystems — это управляемые движком синглтоны с автоматическим жизненным циклом. Виды привязаны к области: UEngineSubsystem, UGameInstanceSubsystem, UWorldSubsystem, ULocalPlayerSubsystem. Каждый создаётся и уничтожается вместе со своей областью.
Типичные ошибки
- ✗Писать вручную статический синглтон вместо subsystem с управляемым жизненным циклом
- ✗Выбирать неверную область — например,
UEngineSubsystemдля состояния конкретного мира - ✗Думать, что subsystems — это компоненты Actor, прикреплённые на уровне
Уточняющие вопросы
- →Когда вы выберете
UWorldSubsystemвместоUGameInstanceSubsystem? - →Как получить экземпляр subsystem из C++ или Blueprint?
SeniorДизайнИногдаВы создаёте переиспользуемый компонент перезарядки для способностей игрока в соревновательной многопользовательской игре. При активации способности она должна уходить в перезарядку и отказывать в повторной активации, пока та не истечёт; каждый подключённый клиент должен показывать точную полоску остатка времени, а перезарядка должна переживать переменную сетевую задержку. Читающие клиенты не должны иметь возможности активировать способность чаще, чем позволяет перезарядка. Опишите устройство компонента и стратегию репликации. Требования: авторитет над тем, можно ли активировать способность, однозначен; клиенты показывают верный остаток времени, не доверяя дрейфу собственных часов; UI остаётся отзывчивым несмотря на задержку; а трафик на перезарядку не растёт с частотой кадров.
Вы создаёте переиспользуемый компонент перезарядки для способностей игрока в соревновательной многопользовательской игре. При активации способности она должна уходить в перезарядку и отказывать в повторной активации, пока та не истечёт; каждый подключённый клиент должен показывать точную полоску остатка времени, а перезарядка должна переживать переменную сетевую задержку. Читающие клиенты не должны иметь возможности активировать способность чаще, чем позволяет перезарядка. Опишите устройство компонента и стратегию репликации. Требования: авторитет над тем, можно ли активировать способность, однозначен; клиенты показывают верный остаток времени, не доверяя дрейфу собственных часов; UI остаётся отзывчивым несмотря на задержку; а трафик на перезарядку не растёт с частотой кадров.
Сервер — единственная сторона с авторитетом: он хранит время окончания перезарядки относительно серверных часов реплицируемого GameState, проверяет каждый запрос активации по нему и отклоняет слишком ранние вызовы. Реплицируйте метку времени окончания, а не убывающий счётчик — клиенты сами выводят остаток по синхронизированным часам. Клиент может предсказывать перезарядку ради отзывчивости UI, но значение сервера остаётся авторитетным и исправляет любое расхождение.
Типичные ошибки
- ✗Доверять присланному клиентом флагу «готово» вместо проверки по серверному времени
- ✗Реплицировать убывающий счётчик каждый кадр вместо одной метки времени окончания
- ✗Хранить перезарядку на GameMode, который никогда не реплицируется клиентам
Уточняющие вопросы
- →Как клиентское предсказание уживается с авторитетным значением перезарядки на сервере?
- →Почему реплицировать метку времени окончания, а не само значение остатка в секундах?
SeniorДизайнИногдаВы проектируете конвейер урона для соревновательного шутера, где игроки запускают модифицируемый клиент. Когда игрок стреляет, урон должен применяться к цели, но подменённый клиент не должен иметь возможности завысить наносимый урон, поражать цели, которых не видит, или стрелять чаще, чем позволяет оружие. При этом конвейер должен оставаться отзывчивым для честных игроков на соединении с высокой задержкой. Опишите, как вы выстроите поток между клиентом и сервером. Требования: клиент никогда не задаёт применяемую величину урона; сервер независимо перепроверяет, что выстрел легитимен, прежде чем что-либо применять; здоровье цели хранится там, куда клиент не может писать напрямую; а честные клиенты всё равно получают отзывчивую обратную связь.
Вы проектируете конвейер урона для соревновательного шутера, где игроки запускают модифицируемый клиент. Когда игрок стреляет, урон должен применяться к цели, но подменённый клиент не должен иметь возможности завысить наносимый урон, поражать цели, которых не видит, или стрелять чаще, чем позволяет оружие. При этом конвейер должен оставаться отзывчивым для честных игроков на соединении с высокой задержкой. Опишите, как вы выстроите поток между клиентом и сервером. Требования: клиент никогда не задаёт применяемую величину урона; сервер независимо перепроверяет, что выстрел легитимен, прежде чем что-либо применять; здоровье цели хранится там, куда клиент не может писать напрямую; а честные клиенты всё равно получают отзывчивую обратную связь.
Никогда не позволяйте клиенту присылать урон числом — он просто отправит огромное значение. Клиент шлёт только намерение («я выстрелил по этой цели») через серверный RPC; сервер валидирует всё — оружие принадлежит ему и не на перезарядке, цель в радиусе и в прямой видимости по серверному миру — затем вычисляет и применяет урон. Здоровье живёт в серверном реплицируемом свойстве; клиенты получают результат, но никогда его не задают.
Типичные ошибки
- ✗Принимать величину урона от клиента вместо только намерения действовать
- ✗Считать, что
Reliable-RPC также является доверенным или защищённым от подмены - ✗Позволять клиентам писать собственное здоровье вместо получения реплицированного результата
Уточняющие вопросы
- →Как честно валидировать попадание, если клиент и сервер расходятся в позиции цели?
- →Какие серверные проверки ловят клиента, стреляющего быстрее, чем позволяет оружие?
SeniorТеорияИногдаСчёт игрока пропадает при респавне или seamless travel — как сохранить данные APlayerState?
Счёт игрока пропадает при респавне или seamless travel — как сохранить данные APlayerState?
APlayerState живёт дольше Pawn — Pawn уничтожается и пересоздаётся при смерти, но тот же PlayerState сохраняется, принадлежа APlayerController. Поэтому пользовательская статистика вроде счёта должна лежать на PlayerState, а не на Pawn. При seamless travel движок создаёт новые PlayerState; данные, которые нужно сохранить, переносятся переопределением CopyProperties, который движок вызывает для миграции значений на новый экземпляр.
Типичные ошибки
- ✗Хранить счёт или иную постоянную статистику на Pawn, который умирает при респавне
- ✗Считать, что объекты PlayerState переживают seamless travel без
CopyProperties - ✗Путать время жизни Pawn с привязанным к контроллеру временем жизни PlayerState
Уточняющие вопросы
- →Почему PlayerState переживает респавн Pawn, но не всегда seamless travel?
- →Что при travel должно лежать на PlayerController, а не на PlayerState?
SeniorДебаггингИногдаЗначение, выставленное на GameMode, не доходит до клиентов — как проследить поток авторитета?
Значение, выставленное на GameMode, не доходит до клиентов — как проследить поток авторитета?
AGameModeBase существует только на сервере, поэтому любое выставленное на нём свойство по замыслу невидимо клиентам — оно никогда не реплицируется. Решение — перенести общие данные в AGameStateBase, который реплицируется всем, пометив свойство Replicated с записью в GetLifetimeReplicatedProps. Трассируйте так: убедитесь, что значение пишется на стороне с авторитетом и что клиенты читают его через GetGameState(), а не GetGameMode().
Типичные ошибки
- ✗Класть видимое клиенту состояние на GameMode вместо реплицируемого GameState
- ✗Считать, что GameMode реплицирует копию клиентам, если его свойство помечено
Replicated - ✗Забывать зарегистрировать свойство GameState в
GetLifetimeReplicatedProps
Уточняющие вопросы
- →Как отправить разовое событие с GameMode всем клиентам без постоянного свойства?
- →Почему
GetGameMode()возвращает null на клиенте и что вызывать вместо него?
SeniorПроизводительностьИногда5000 компонентов тикают каждый кадр — как диагностировать и срезать стоимость?
5000 компонентов тикают каждый кадр — как диагностировать и срезать стоимость?
Сначала профилируйте через stat game и Unreal Insights, чтобы подтвердить, что тик — горячий путь. Затем срезайте: отключайте тик там, где он не нужен (bCanEverTick = false), и повышайте TickInterval для медленной логики. Структурное решение — вовсе убрать потиковые вызовы у компонентов: перенести общую логику в один subsystem или менеджер, который раз за кадр обходит весь набор, превращая тысячи виртуальных вызовов в один плотный цикл.
Типичные ошибки
- ✗Оптимизировать тик до профилирования, не подтвердив, что тик действительно узкое место
- ✗Оставлять
bCanEverTickравным true у компонентов без покадровой работы - ✗Держать тысячи потиковых вызовов вместо одного цикла, управляемого subsystem
Уточняющие вопросы
- →Как tick-группа или зависимость тика меняет момент выполнения вашей логики в кадре?
- →Какие накладные расходы несёт потиковый вызов компонента помимо работы в теле тика?