Данный перевод является вольным переводом страницы по документации Unreal 4.Я не гарантирую что он на 100% подойдёт или будет корректным. В первую очередь я перевожу для себя с целью изучить данный движек, который я считаю лучшим среди существующих на текущий момент времени.
Спасибо за понимание.
Начало
Руководство по написанию кода на С++ на движке Unreal Engine является увлекательным занятием, и не волнуйтесь если у вас что-то не будет получаться. Программирование на Unreal достаточно не сложно, и не надо волноваться если у вас что-то не получается. Мы думаем о Unreal C++ как о помощнике в С++, который предоставляет большие возможности чтобы сделать проще С++ для всех.
С учётом на писаного ранее,давайте взглянем на рабочий процесс программиста при построении и создании бизнес логики для дизайнера. Сейчас мы постараемся создать класс который позже будет доработан с помощью Blueprints дизайнером или программистом. В этом классе который мы создадим, мы добавим некоторые свойства, которые в последствии может установить или изменить дизайнер. Так же мы будем получать значения этих свойств с помощью визуального редактора в Блюпринт или в коде программы
Class Wizard (Мастер создания классов)
На рисунке ниже, мы создадим новый класс(Actor) помощью визарда, а далее продожим его изменение с помощью Blueprints редактора. Мастер класс предназначен для генерации классов в С++. На самом деле их сам визард можно и не использовать для создания классов(кому как удобнее).
Второй шаг после создания класса, нам необходимо задать имя нового класа(если не задать имя то оно сгенирируется автоматически). От себя могу заметить, папку для создания вашего класса необходимо указать в souce вашего проекта.
После того как будет сгенерирован класс, Unreal откроет среду разработки для дальнейшего редактирования класса. Более подробное расмотрение мастера генерации классов мы рассмотрим попозже.
Содержимое файла который сгенерировал класс
#include "GameFramework/Actor.h" #include "MyActor.generated.h" UCLASS() class AMyActor : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AMyActor(); // Called when the game starts or when spawned virtual void BeginPlay() override; // Called every frame virtual void Tick( float DeltaSeconds ) override; };
Мастер класс генерирует два метода BeginPlay() и Tick() в качестве перегружаемых.
метод BeginPlay() – событие которое позволяет узнать вошел ли в игру Actor. BeginPlay является методом в котором можно реализовать начальную логику геймплея при появлении Actor.
Метод Tick() – Событие которое вызывается каждый кадр и которое принимает как параметр количество затраченного времени с момента последнего кадра. В данном методе можно сделать любую повторяющуюся логику. Если данный метод не нужен, то лучше его удалить, чтобы не перезагружать ресурсы компьютеры при геймплее.
В коде ниже представлена реализация конструктора который вызывается всегда при вызове класса
AMyActor::AMyActor() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you do not need it. PrimaryActorTick.bCanEverTick = true; }
Making a property show up in the editor (Создание отображаемых свойств и редактирование)
Мы создали класс, теперь давайте добавим свойства которые можно будет устанавливать через визуальный редактор Unreal Editor. Связывание своиств производится с помощью специального макроса UPROPERTY() . Для того чтобы разрешить редактирование своиств в визуальном редакторе, необходимо задать UPROPERTY(EditAnywhere), что даст в последующим право видеть и редактировать своиства класса.
UCLASS() class AMyActor : public AActor { GENERATED_BODY() UPROPERTY(EditAnywhere) int32 TotalDamage; ... };
Этого достаточно, чтобы редактировать значение новой переменной. Есть так же другие способы для задания редактирования и контроля редактирования переменных. Один из способов, это передача доп. информации через макрос UPROPERTY(). Добавив переменную TotalDamage вы её увидете в дополнительных своиствах. Так же можно задать своиста категорий к чему будет относиться данная переменная. Задание категории так же можно произвести через передачу параметров в макрос.
UPROPERTY(EditAnywhere, Category="Damage") int32 TotalDamage;
Если пользователь захочет отредактировать данную категорию, то он её увидит в разделе под заголовком Damage на ряду с другими своиствами. Это очень хороший способ группировки своиств на ряду с другими настройками.
Теперь давайте через своиства макроса зададим возможность редактирование наших переменных через Blueprints
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage") int32 TotalDamage;
BlueprintReadWrite – это спец параметр, который даёт возможность читать и редактировать любые переменные заданные после UPROPERTY
Все свойства которые доступны для доступа и работы с блюпринтами через код можно почитать по ссылке, на английском языке
Давайте добавим несколько различных свойств для того чтобы понять как они будут отрабатываться и контролироваться дизайнером.
UCLASS() class AMyActor : public AActor { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage") int32 TotalDamage; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage") float DamageTimeInSeconds; UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Transient, Category="Damage") float DamagePerSecond; ... };
Своиство DamageTimeInSeconds может модифицироваться дизайнером.
Свойство DamagePerSecond является расчётным свойством и оно скрыто за счёт VisibleAnywhere, как изменяется это свойство мы рассмотрим дальше. Флаг Transient означает переменная является не постоянная(не константной).
Установка значений переменной по умолчанию
Установка значений переменных в конструкторе ведётся так же как и простой код в С++, в конструкторе класса. Ниже приведено два примера идентичных по функционалу.
Первый пример:
AMyActor::AMyActor() { TotalDamage = 200; DamageTimeInSeconds = 1.f; }
Второй пример:
AMyActor::AMyActor() : TotalDamage(200), DamageTimeInSeconds(1.f) { }
Вот как будут выглядеть наши переменные при отображении в Blueprint(е)
Горячая перезагрузка(Hot Reloading)
Движек Unreal позваляет компилировать проект в исходный код двумя способами без закрытия проекта, так называемая горячая перезагрузка.
1) Первый способ является простая компиляция проекта через visua studio или XCode. При чём так же будут подцеплены все dll библиотеки которые присутствуют в движке.
2) Второй способ можно реализовать нажав клавишу Complite в Unreal
Вы можете использовать любой из доступных способов.
Расширение классов C++ с помощью Blueprints
До сих пор мы создавали простые классы геймплея с помощью мастера класса С++, добавляя некоторые свойства для редактирования дизайнером. Давайте добавим те же классы со стороны дизайнера.
Для начала необходимо создать BluePrint класс из класса AMyActor. Стоит обратить внимание на название нового создаваемого класса. Имя нового класса буде MyActor, которое будет наследовать класс AMyActor. Это необходимо для того чтобы у дизайнера было понимание, какой класс мы наследуем, для дальнейшей работы.
После нажатия select, в Blueprint Создаст новый класс с Вашим заданным именем. Как видно на скрине классу дали имя CustomActor1
Это наш первый класс который имеет определённые свойства которые можно задать дизайнером. Первоначально установим свойства TotalDamage на 300, а так же установим время доставки домага в 2 секунды, установив значение Damage Time in second. Должно получиться следующее
Те расчёты которые были сделаны программой не соответствуют ожидаемому результату. Должно было 150, а отображается 200. Причина ошибки является то, что наш урон был вычислен в первую секунду, а потом свойства были инициализированны в процессе перезагрузки метода. Приведённый код ниже решает данную проблему неточного вычисления.
void AMyActor::PostInitProperties() { Super::PostInitProperties(); CalculateValues(); } void AMyActor::CalculateValues() { DamagePerSecond = TotalDamage / DamageTimeInSeconds; } #if WITH_EDITOR void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { CalculateValues(); Super::PostEditChangeProperty(PropertyChangedEvent); } #endif
Как можете заметить, метод PostEditChangeProperty() находится внутри инструкции
#if #ifdef
Не смог корректно перевести
This is so that building your game only the code that you need for the game, removing any extra code that might increase the size of your executable unnecessarily.
Теперь у нас есть код который соответствует ожидаемому результату и переменная DamagePerSecond отображает ожидаемое значение, что можно видеть ниже
Вызов функций через C++ и Blueprint
Во время создания геймплея в С++ коде, у дизайнеров должна быть возможность вызывать эти функции в Blueprint. Давайте посмотрим как может обращаться дизайнер к созданным классам через С++.
Вызов функции созданной через С++ достаточно проста и наглядно видно на рисунке ниже. Для того чтобы дизайнер мог обращаться к функциям прописанным в С++ из Blueprint, необходимо прописать функцию после макроса.
UFUNCTION(BlueprintCallable, Category="Damage") void CalculateValues();
После добавления функции в Макрос UFUNCTION() можно наблюдать где данная функция будет доступна в Blueprint. Чтобы отнести функцию к категории, необходимо добавить название категории в передаваемых параметрах макроса. Отношение категории к функции наглядно видно на картинке.
Как мы видим , функция отнесена к соответствующей категории “damage”. В коде Blueprint показано как задать изменение отображаемых свойство TotalDamage с последующим вызовом функции и пересчётом зависимых переменных.
Большая часть движка может быть изменена с помощью blueprint, из ранее написанного кода на C++ использую макрос UFUNCTION(). Но, тем не менее лучшим подходом является программирование логики через С++. В первую очередь это связано с оптимизацией кода и поведения игры.
Давайте расмотрим ещё один способ взаимодействия дизайнера и программиста. Предположим, что нам необходимо уведомлять дизайнера о предстоящем событии на которые должен реагировать код через blueprint.Часто это бывает некоторые эффекты или например восстановление актёра который ране был скрыт. Фрагмент кода ниже показывает как это можно реализовать.
UFUNCTION(BlueprintImplementableEvent, Category="Damage") void CalledFromCpp();
Эта функция вызывается как и любая другая функция из C++, В движке Unreal генерируется база С++ которая реализует функцию вызова Blueprint VM.Если Blueprint заполняет тело текущего метода, то функция ведёт себя как обычный класс в С++
UFUNCTION(BlueprintNativeEvent, Category="Damage") void CalledFromCpp();
По факту будет сгенерирован метод.
void AMyActor::CalledFromCpp_Implementation() { // Do something cool here }
Стоит упомянуть, что метод будет сгенерирован автоматически и в заголовок необходимо будет добавить декларацию _Implementation(). Это касается всех версий выше 4.8UE
ссылка на вторую часть : вторая часть