image

Blueprints — очень популярный способ создания геймплея в Unreal Engine 4. Однако если вы уже давно программируете и предпочитаете код, то вам идеально подойдёт C++. С помощью C++ можно даже вносить изменения в движок и создавать собственные плагины.

В этом туториале вы научитесь следующему:

  • Создавать классы C++
  • Добавлять компоненты и делать их видимыми для Blueprints
  • Создавать класс Blueprint на основе класса C++
  • Добавлять переменные и делать их изменяемыми из Blueprints
  • Связывать привязки осей и действий с функциями
  • Переопределять функции C++ в Blueprints
  • Связывать событие коллизии с функцией

Стоит учесть, что это не туториал по изучению C++. Мы сосредоточимся на работе с C++ в контексте Unreal Engine.

Примечание: в этом туториале подразумевается, что вы уже знакомы с основами Unreal Engine. Если вы новичок в Unreal Engine, то сначала изучите состоящий из десяти частей туториал по Unreal Engine для начинающих.

Приступаем к работе


Если вы ещё этого не сделали, то вам понадобится установить Visual Studio. Выполните инструкции из официального руководства Epic по настройке Visual Studio для Unreal Engine 4. (Вы можете использовать альтернативные IDE, но в этом туториале применяется Visual Studio, потому что Unreal рассчитан на работу с ним.)

Затем скачайте заготовку проекта и распакуйте её. Перейдите в папку проекта и откройте CoinCollector.uproject. Если приложение попросить пересобрать модули, то нажмите Yes.


Закончив с этим, вы увидите следующую сцену:


В этом туториале мы создадим шар, который будет перемещать игрок, чтобы собрать монеты. В предыдущих туториалах мы использовали управляемых игроком персонажей с помощью Blueprints. В этом туториале мы создадим его с помощью C++.

Создание класса C++


Для создания класса C++ перейдите в Content Browser и выберите Add New\New C++ Class.


После этого откроется C++ Class Wizard. Во-первых, нужно будет выбрать, от какого класса мы будем наследовать. Поскольку класс должен быть управляемым игроком, нам понадобится Pawn. Выберите Pawn и нажмите Next.


На следующем экране можно указать имя и путь к файлам .h и .cpp. Замените Name на BasePlayer и нажмите на Create Class.


При этом будут созданы файлы и скомпилирован проект. После компиляции Unreal откроет Visual Studio. Если BasePlayer.cpp и BasePlayer.h не будут открыты, то перейдите в Solution Explorer и откройте их. Они находятся в папке Games\CoinCollector\Source\CoinCollector.


Прежде чем двигаться дальше, вам нужно узнать о системе рефлексии Unreal. Эта система управляет различными частями движка, такими как панель Details и сборка мусора. При создании класса с помощью C++ Class Wizard движок Unreal добавляет в заголовок три строки:

  1. #include "TypeName.generated.h"
  2. UCLASS()
  3. GENERATED_BODY()

Движку Unreal нужны эти строки, чтобы класс был видим системе рефлексии. Если вам это не понятно, то не волнуйтесь. Вам достаточно только знать, что системе рефлексии позволяет делать такие вещи, как раскрытие функций и переменных Blueprints и редактору.

Вы можете также заметить, что класс называется ABasePlayer, а не BasePlayer. При создании класса типа actor Unreal ставит перед названием класса префикс A (от слова actor). Чтобы система рефлексии могла работать, ей нужно, чтобы классы имели соответствующие префиксы. Подробнее прочитать о префиксах можно в Стандарте оформления кода Epic.

Примечание: префиксы не отображаются в редакторе. Например, если вам нужно создать переменную типа ABasePlayer, то нужно искать BasePlayer.

Это всё, что вам пока нужно знать о системе рефлексии. Теперь нам нужно добавить модель игрока и камеру. Для этого нужно использовать компоненты.

Добавление компонентов


Для Pawn игрока нам нужно добавить три компонента:

  1. Static Mesh: он позволит выбрать меш, являющийся моделью игрока
  2. Spring Arm: этот компонент используется в качестве штатива камеры. Один конец будет прикреплён к мешу, а к другому будет прикреплена камера.
  3. Camera: Unreal показывает игроку всё, что видит камера.

Во-первых, нам нужно добавить заголовки для каждого типа компонента. Откройте BasePlayer.h и добавьте над #include "BasePlayer.generated.h" следующие строки:

#include "Components/StaticMeshComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"

Примечание: Важно добавлять файл .generated.h последним. В нашем случае директивы include должны выглядеть следующим образом:

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "BasePlayer.generated.h"

Если он будет не последним include, то при компиляции мы получим ошибку.

Теперь нам нужно объявить переменные для каждого компонента. Добавьте после SetupPlayerInputComponent() следующие строки:

UStaticMeshComponent* Mesh;
USpringArmComponent* SpringArm;
UCameraComponent* Camera;

Использованное здесь имя будет именем компонента в редакторе. В нашем случае компоненты будут отображаться как Mesh, SpringArm и Camera.

Далее нам нужно сделать каждую переменную видимой для системы рефлексии. Для этого добавим над каждой переменной UPROPERTY(). Теперь код должен выглядеть вот так:

UPROPERTY()
UStaticMeshComponent* Mesh;

UPROPERTY()
USpringArmComponent* SpringArm;

UPROPERTY()
UCameraComponent* Camera;

Также можно добавить к UPROPERTY() описатели (specifiers). Они будут управлять поведением переменной в различных аспектах движка.

Добавьте VisibleAnywhere и BlueprintReadOnly внутри скобок каждого UPROPERTY(). Отделите каждый описатель запятой.

UPROPERTY(VisibleAnywhere, BlueprintReadOnly)

VisibleAnywhere позволит каждому компоненту быть видимым в редакторе (в том числе и в Blueprints).

BlueprintReadOnly позволит получать ссылку на компонент с помощью нодов Blueprint. Однако он не позволит нам задавать компонент. Для компонентов важно быть read-only, потому что их переменные являются указателями. Мы не хотим, чтобы пользователи задавали их, иначе они могут указать на случайное место в памяти. Стоит заметить, что BlueprintReadOnly всё-таки позволяет задавать переменные внутри компонента, и именно к такому поведению мы стремимся.

Примечание: Для переменных, не являющихся указателями (int, float, boolean и т.д.) используйте EditAnywhere и BlueprintReadWrite.

Теперь, когда у нас есть переменные для каждого компонента, нам нужно их инициализировать. Для этого необходимо создать их внутри конструктора.

Инициализация компонентов


Для создания компонентов можно использовать CreateDefaultSubobject<Type>("InternalName"). Откройте BasePlayer.cpp и добавьте в ABasePlayer() следующие строки:

Mesh = CreateDefaultSubobject<UStaticMeshComponent>("Mesh");
SpringArm = CreateDefaultSubobject<USpringArmComponent>("SpringArm");
Camera = CreateDefaultSubobject<UCameraComponent>("Camera");

Это создаст компонент каждого типа, а затем назначит их адрес в памяти переданной переменной. Аргумент-строка будет внутренним именем компонента, используемым движком (а не отображаемым именем, несмотря на то, что в нашем случае они одинаковы).

Затем нам нужно настроить иерархию (выбрать корневой компонент и так далее). Добавьте после предыдущего кода следующее:

RootComponent = Mesh;
SpringArm->SetupAttachment(Mesh);
Camera->SetupAttachment(SpringArm);

Первая строка сделает Mesh корневым компонентом. Вторая строка прикрепит SpringArm к Mesh. Наконец, третья строка прикрепит Camera к SpringArm.

После завершения кода компонентов нам нужно выполнить компиляцию. Выберите один из следующих способов компиляции:

  1. В Visual Studio выберите Build\Build Solution
  2. В Unreal Engine нажмите на Compile в Toolbar

Затем нам нужно указать, какой меш использовать и поворот пружинного рычага. Рекомендуется делать это в Blueprints, потому что нежелательно жёстко указывать пути к ресурсам в C++. Например, в C++ для задания статичного меша нужно сделать нечто подобное:

static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshToUse(TEXT("StaticMesh'/Game/MyMesh.MyMesh");
MeshComponent->SetStaticMesh(MeshToUse.Object);

Однако в Blueprints достаточно будет просто выбрать меш из раскрывающегося списка.


Если вы переместите ресурс в другую папку, в Blueprints ничего не испортится. Однако в C++ придётся менять каждую ссылку на этот ресурс.

Чтобы задать поворот меша и пружинного рычага в Blueprints, нужно будет создать Blueprint на основании BasePlayer.

Примечание: Обычно практикуется создание базовых классов в C++ с последующим созданием подкласса Blueprint. Это упрощает изменение классов для художников и дизайнеров.

Выделение подклассов классов C++


В Unreal Engine перейдите в папку Blueprints и создайте Blueprint Class. Разверните раздел All Classes и найдите BasePlayer. Выберите BasePlayer, а затем нажмите на Select.


Переименуйте его в BP_Player, а затем откройте.

Сначала мы зададим меш. Выберите компонент Mesh и задайте для его Static Mesh значение SM_Sphere.


Затем нам нужно задать поворот и длину пружинного рычага. Наша игра будет с видом сверху, поэтому камера должна быть над игроком.

Выберите компонент SpringArm и задайте для Rotation значение (0, -50, 0). Это повернёт пружинный рычаг так, что камера будет смотреть на меш сверху вниз.


Поскольку пружинный рычаг является дочерним элементом меша, он начинает вращаться, когда начинает вращаться шар.

GIF

Чтобы исправить это, нам нужно сделать так, чтобы поворот рычага был абсолютным. Нажмите на стрелку рядом с Rotation и выберите World.

GIF

Затем задайте для Target Arm Length значение 1000. Так мы отдалим камеру на 1000 единиц от меша.


Затем нужно задать Default Pawn Class, чтобы использовать наш Pawn. Нажмите на Compile и вернитесь в редактор. Откройте World Settings и задайте для Default Pawn значение BP_Player.


Нажмите на Play, чтобы увидеть Pawn в игре.


Следующим шагом будет добавление функций игроку, чтобы он мог перемещаться.

Реализация движения


Вместо того, чтобы добавлять для движения смещение, мы будем двигаться с помощью физики! Сначала нам нужна переменная, указывающая величину прикладываемой к шару силы.

Вернитесь в Visual Studio и откройте BasePlayer.h. Добавьте после переменных компонентов следующее:

UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MovementForce;

EditAnywhere позволяет изменять MovementForce в панели Details. BlueprintReadWrite позволит задавать и считывать MovementForce с помощью нодов Blueprint.

Далее нам нужно создать две функции. Одну для движения вверх-вниз, другую — для движения влево-вправо.

Создание функций движения


Добавьте под MovementForce следующие объявления функций:

void MoveUp(float Value);
void MoveRight(float Value);

Позже мы свяжем с этими функциями привязки осей. Благодаря этому привязки осей смогут передавать свой scale (поэтому функциям нужен параметр float Value).

Примечание: Если вы незнакомы с привязками осей и scale, изучите туториал про Blueprints.

Теперь нам нужно создать реализацию для каждой функции. Откройте BasePlayer.cpp добавьте в конец файла следующее:

void ABasePlayer::MoveUp(float Value)
{
	FVector ForceToAdd = FVector(1, 0, 0) * MovementForce * Value;
	Mesh->AddForce(ForceToAdd);
}

void ABasePlayer::MoveRight(float Value)
{
	FVector ForceToAdd = FVector(0, 1, 0) * MovementForce * Value;
	Mesh->AddForce(ForceToAdd);
}

MoveUp() добавляет физическую силу для Mesh по оси X. Величина силы задаётся MovementForce. Благодаря умножению результата на Value (масштаб привязки оси), меш может перемещаться в положительном или отрицательном направлениях.

MoveRight() делает то же самое, что и MoveUp(), но по оси Y.

Закончив создание функций движения, мы должны связать с ними привязки осей.

Связывание привязок осей с функциями


Ради упрощения я уже заранее создал привязки осей. Они находятся в Project Settings, в разделе Input.


Примечание: Привязки осей не обязаны иметь то же название, что и функции, с которыми мы их связываем.

Добавьте внутрь SetupPlayerInputComponent() следующий код:

InputComponent->BindAxis("MoveUp", this, &ABasePlayer::MoveUp);
InputComponent->BindAxis("MoveRight", this, &ABasePlayer::MoveRight);

Так мы свяжем привязки осей MoveUp и MoveRight с MoveUp() и MoveRight().

На этом мы закончили с функциями движения. Теперь нам нужно включить физику для компонента Mesh.

Включение физики


Добавьте внутрь ABasePlayer() следующие строки:

Mesh->SetSimulatePhysics(true);
MovementForce = 100000;

Первая строка позволит воздействовать на Mesh физическим силам. Вторая строка присваивает MovementForce значение 100000. Это значит, что при движении шару будет прибавлено 100 000 силы. По умолчанию физические объекты весят примерно 110 килограмм, так что для их перемещения потребуется много силы!

Если мы создали подкласс, некоторые свойства не изменятся, даже если мы изменим их в базовом классе. В нашем случае у BP_Player не будет включено Simulate Physics. Однако теперь во всех создаваемых подклассах оно будет включено по умолчанию.

Выполните компиляцию и вернитесь в Unreal Engine. Откройте BP_Player и выберите компонент Mesh. Затем включите Simulate Physics.


Нажмите Compile, а затем на Play. Нажимайте W, A, S и D, чтобы передвигать шар.

GIF

Далее мы объявим функцию C++, которую можно реализовать с помощью Blueprints. Это позволит дизайнерам создавать функционал без использования C++. Чтобы научиться этому, мы создадим функцию прыжка.

Создание функции прыжка


Сначала нам нужно связать привязку прыжка к функции. В этом туториале мы назначим прыжок на клавишу пробела.


Вернитесь в Visual Studio и откройте BasePlayer.h. Добавьте под MoveRight() следующие строки:

UPROPERTY(EditAnywhere, BlueprintReadWrite)
float JumpImpulse;

UFUNCTION(BlueprintImplementableEvent)
void Jump();

Первое — это переменная float с именем JumpImpulse. Мы можем использовать её при реализации прыжка. Она использует EditAnywhere, чтобы её можно было изменять в редакторе. Также в ней используется BlueprintReadWrite, чтобы мы могли считывать и записывать её с помощью нодов Blueprint.

Далее идёт функция прыжка. UFUNCTION() делает Jump() видимой для системы рефлексии. BlueprintImplementableEvent позволяет Blueprints реализовать Jump(). Если реализация отсутствует, то вызовы Jump() ни к чему не приведут.

Примечание: Если вы хотите создать в C++ реализацию по умолчанию, то используйте BlueprintNativeEvent. Ниже мы расскажем о том, как это сделать.

Так как Jump — это привязка действия, способ связывания немного отличается. Закройте BasePlayer.h и откройте BasePlayer.cpp. Добавьте внутрь SetupPlayerInputComponent() следующее:

InputComponent->BindAction("Jump", IE_Pressed, this, &ABasePlayer::Jump);

Так мы свяжем привязку Jump с Jump(). Она будет выполняться только при нажатии клавиши прыжка. Если вы хотите выполнять её при отпускании клавиши, то используйте IE_Released.

Дальше мы переопределим Jump() в Blueprints.

Переопределение функций в Blueprints


Выполните компиляцию и закройте BasePlayer.cpp. Затем вернитесь к Unreal Engine и откройте BP_Player. Перейдите в панель My Blueprints и наведите мышь на Functions, чтобы появился раскрывающийся список Override. Нажмите на него и выберите Jump. Так мы создадим Event Jump.

GIF

Примечание: Переопределение будет событием, если отсутствует возвращаемый тип. Если возвращаемый тип существует, то это будет функция.

Далее мы создадим следующую схему:


Так мы добавим Mesh импульс (JumpImpulse) по оси Z. Учтите, что в этой реализации игрок может прыгать бесконечно.

Далее нам нужно задать значение JumpImpulse. Нажмите на Class Defaults в Toolbar, а затем перейдите к панели Details. Задайте JumpImpulse значение 100000.


Нажмите на Compile, а затем закройте BP_Player. Нажмите на Play и попробуйте попрыгать с помощью клавиши пробела.

GIF

В следующем разделе мы заставим монеты исчезать при контакте с игроком.

Собирание монет


Для обработки коллизий нам нужно связать функцию с событием наложения. Для этого функция должна удовлетворять двум требованиям. Первое — функция должна иметь макрос UFUNCTION(). Второе требование — функция должна иметь правильную сигнатуру. В этом туториале мы будем использовать событие OnActorBeginOverlap. Это событие требует, чтобы у функции была следующая сигнатура:

FunctionName(AActor* OverlappedActor, AActor* OtherActor)

Вернитесь в Visual Studio и откройте BaseCoin.h. Добавьте под PlayCustomDeath() следующие строки:

UFUNCTION()
void OnOverlap(AActor* OverlappedActor, AActor* OtherActor);

После связывания OnOverlap() будет исполнятся при наложении монеты и другого актора. OverlappedActor будет монетой, а OtherActor — другой актор.

Далее нам нужно реализовать OnOverlap().

Реализация наложений


Откройте BaseCoin.cpp и добавьте в конец файла следующее:

void ABaseCoin::OnOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
}

Так как мы хотим распознавать только наложения игрока, то нужно привести OtherActor к ABasePlayer. Прежде чем выполнить приведение, нам нужно добавить заголовок для ABasePlayer. Добавьте под #include "BaseCoin.h" следующее:

#include "BasePlayer.h"

Теперь нам нужно выполнить приведение. В Unreal Engine приведение можно выполнить так:

Cast<TypeToCastTo>(ObjectToCast);

Если приведение выполнено успешно, то оно вернёт указатель на ObjectToCast. Если неудачно, то оно вернёт nullptr. Проверяя результат на nullptr, мы можем определить, имел ли объект нужный тип.

Добавьте внутрь OnOverlap() следующее:

if (Cast<ABasePlayer>(OtherActor) != nullptr)
{
	Destroy();
}

Теперь, когда OnOverlap() выполняется, она будет проверять, имеет ли OtherActor тип ABasePlayer. Если это так, то она будет уничтожать монету.

Далее нам нужно привязать OnOverlap().

Связывание функции наложения


Чтобы связать функцию с событием наложения, нам нужно использовать с событием AddDynamic(). Добавьте внутрь ABaseCoin() следующее:

OnActorBeginOverlap.AddDynamic(this, &ABaseCoin::OnOverlap);

Так мы свяжем OnOverlap() с событием OnActorBeginOverlap. Это событие происходит всегда, когда актор накладывается на другого актора.

Выполните компиляцию и вернитесь в Unreal Engine. Нажмите Play и начните собирать монеты. При контакте с монетой она будет уничтожаться, что приводит к её исчезновению.

GIF

Примечание: Если монеты не исчезают, попробуйте перезапустить редактор, чтобы выполнить полную рекомпиляцию. Для работы некоторых изменений требуется перезапуск.

В следующем разделе мы создадим ещё одну переопределяемую функцию C++. Однако на этот раз мы также создадим реализацию по умолчанию. Для демонстрации этого мы воспользуемся OnOverlap().

Создание реализации функции по умолчанию


Чтобы создать функцию с реализацией по умолчанию, нужно использовать описатель BlueprintNativeEvent. Вернитесь в Visual Studio и откройте BaseCoin.h. Добавьте для OnOverlap()
в UFUNCTION() BlueprintNativeEvent:

UFUNCTION(BlueprintNativeEvent)
void OnOverlap(AActor* OverlappedActor, AActor* OtherActor);

Чтобы сделать функцию реализацией по умолчанию, нам нужно добавить суффикс _Implementation. Откройте BaseCoin.cpp и замените OnOverlap на OnOverlap_Implementation:

void ABaseCoin::OnOverlap_Implementation(AActor* OverlappedActor, AActor* OtherActor)

Теперь если дочерний Blueprint не реализует OnOverlap(), то будет использована эта реализация.

Следующим этапом будет реализация OnOverlap() в BP_Coin.

Создание реализации в Blueprint


Для реализации в Blueprint мы будем вызывать PlayCustomDeath(). Эта функция C++ увеличит скорость вращения монеты. Через 0,5 секунды монета будет себя уничтожать.

Для вызова функции C++ из Blueprints нам нужно использовать описатель BlueprintCallable. Закройте BaseCoin.cpp и откройте BaseCoin.h. Добавьте над PlayCustomDeath() следующее:

UFUNCTION(BlueprintCallable)

Выполните компиляцию и закройте Visual Studio. Вернитесь к Unreal Engine и откройте BP_Coin. Переопределите On Overlap и создайте следующую схему:


Теперь при наложении игрока на монету будет выполняться Play Custom Death.

Нажмите на Compile и закройте BP_Coin. Нажмите Play и соберите несколько монет, чтобы протестировать новую реализацию.

GIF

Куда двигаться дальше?


Вы можете скачать готовый проект отсюда.

Как вы видите, работать с C++ в Unreal Engine довольно просто. Хотя мы уже добились кое-чего в C++, вам ещё нужно многому научиться! Я рекомендую изучить серию туториалов Epic по созданию с помощью C++ шутера с видом сверху.

Если вы новичок в Unreal Engine, то изучите нашу серию туториалов для начинающих из десяти частей. В этой серии вы познакомитесь с различными системами, такими как Blueprints, материалы и системы частиц.

Комментарии (30)


  1. AxisPod
    10.02.2018 13:13

    Выбирал движок для проекта и вот из коммерческих UE в плане скриптинга текстом на мой взгляд оказался самым худшим. Тут какое-то пародие на C++, скорее это голый C с классами, куча макросов, без которых никуда, но при этом на них документация устарела настолько, что бесполезна. stl нет, есть куча своих велосипедов куда худшего качества. Ну и самое вкусное на последок, код отрабатывает не в песочнице, а в пространстве редактора, любая ошибка доступа к памяти и редактор падает сразу и без вариантов.


    1. ufna
      10.02.2018 15:00
      +1

      Потому что это не скриптинг, это — программирование. Для «скриптов» есть прекрасные Blueprints, где в ногу себе выстрелить даже при желании сложновато ;)


      1. AxisPod
        11.02.2018 07:10

        Я не про это слегка говорил. Тут правильнее было бы писать, что используется C++ подобный язык, ибо от C++ только синтаксис остался, да и корявое его использование в из библиотеках. Многие вещи можно было сделать средствами шаблонов, но нет, понапихали кучу шаблонов их настолько много, что хоть стой, хоть падай. А уж как выматывает их прекомпайлер. Посмотреть на CryEngine к примеру, всё сделано заметно удобнее.


        1. ufna
          11.02.2018 12:06

          И вы знаете, это прекрасно работает. Быстро, понятно и удобно. При правильной архитектуре проекта ещё и собирается шустро. Не так там много правил, которым надо следовать. И никто не мешает вам использовать сипипи на полную :)

          А CE хоть и более «путь сипипи самурая», но архитектурно печален. Это не двигло для игр, а скорее сипипи песочница :(


    1. braza
      10.02.2018 22:31

      А главное у них есть специальная глава для тех, кто пришел из Unity, показывающая как код на Unity транслируется в UE4. И она просто уничтожает желание выбирать Unreal.


      1. maisvendoo
        11.02.2018 01:06

        Но не после того, как один и тот же проект выдает на GTX580 и на GTX1080 Ti одинаковые 30 фпс. После лицезрения такого, желание использовать юнити пропадает напрочь.

        А та же сцена на анреал, с более высоким уровнем детализации (с трехмерной травой и цветочками, ага) выдает 120 фпс на 580-й.

        К тому же анреал — открытый код движка, а юнити извините — за бабки


        1. braza
          11.02.2018 13:30

          Не все игры требуют топовой графики.
          Открытость движка != Бесплатность для коммерческого использования. Но у Unreal чуть приятнее модель платы: только свыше какой-то суммы заработка начинают процент брать.


          1. maisvendoo
            11.02.2018 13:56

            Не все игры требуют топовой графики.

            не все, но те же сквады, купившись на «простоту» юнити завели свой проект KSP в тупик и в итоге продали игру

            Открытость == понимание как оно работает, возможность вмешаться в процесс. Ничего, что крупные конторы лицензируют интересующие их движки с исходниками?

            юнити, в его бесплатной инкарнации — хрень, не подходящая под крупные проекты. Те кто используют его для серьезных работ лицензируют его с исходниками ядра рендера, которое, кстати, написано на C++


          1. AxisPod
            12.02.2018 18:46

            Но у Unreal чуть приятнее модель платы

            Да вы видно шутите?

            После того, как вы заработали $3000, каждый квартал вы обязаны отправлять информацию о доходах с игры и собственно саму денежку и это до тех пор, пока вы не снимите игру с продажи, даже если вы заработаете $1 за это время, вы обязаны будете это делать. Почему-то я не считаю это удобной моделью оплаты.


            1. WNeZRoS
              13.02.2018 11:42
              +1

              Насколько я понимаю, каждый квартал заработок до 3000$ не облагается процентом. Чтобы не отвлекать людей по мелочам.


      1. norlin
        11.02.2018 17:26

        Ради интереса заглянул в эту статью — не увидел там ничего страшного. Но с юнити у меня работать не получилось, поэтому не могу оценить с этой точки зрения...


        1. braza
          12.02.2018 03:53

          Страшного нет, особенно при должном терпении, а с какого-то момента может и стокгольмский синдром уже поможет, но… Почти все примеры кода раза в полтора-два длиннее в UE4, не говоря уже о волшебных макросах итд, которые нужно просто запомнить.


          1. norlin
            12.02.2018 11:06

            Почти все примеры кода раза в полтора-два длиннее

            Так это не UE4 же, а разница между C# и C++. Насчёт макросов — согласен, приходится запоминать. Лично у меня не было опыта работы с C++ до UE4 и, судя по интернетам, эти макросы сильно облегчают жизнь...


  1. Bookvarenko
    10.02.2018 15:27

    Как его под вайном завести?


    1. maisvendoo
      10.02.2018 16:06

      Как его под вайном завести?

      Зачем???


      1. Bookvarenko
        11.02.2018 09:12

        Смущает третий пункт "Сборка движка: забег по граблям"


        1. ufna
          11.02.2018 11:58

          Да там странная статья про извращения в целом. На Ubuntu давно все работает из коробки :)


          1. maisvendoo
            11.02.2018 13:17

            На Ubuntu давно все работает из коробки :)

            никто не запрещает вам использовать убуту


            1. ufna
              11.02.2018 13:34

              Я ни в коем случае не хочу принизить вашу статью, и это здорово, что вы и на арч линуксе запустили движок и работаете с ним, но все-таки тех, кто спрашивает про «завести под вайном», я бы отправлял «на убунту», где весь этот шаманизм не нужен.

              Сам я использую инструменты исходя из соображений их удобства для работы с конкретной технологией, оставляя личные предпочтения для «домашних нужд».


              1. maisvendoo
                11.02.2018 14:07

                я использую инструменты исходя из соображений их удобства для работы с конкретной технологией

                тут я с Вами целиком и полностью согласен


  1. alexoron
    10.02.2018 16:55

    Это шутка???
    Getting up and running

    Windows
    Mac
    Linux
    Android
    iOS
    HTML5
    Playstation 4 or XboxOne


    1. maisvendoo
      10.02.2018 17:58

      Эту ссылку бесполезно постить, для доступа к репозиторию нужен аккаунт EpicGames и аккаунт на GitHib. Тогда эпики дают доступ к просмотру кода движка.


      1. DFrostt
        10.02.2018 21:57

        Аккаунт создается за пару минут. EpicGames быстро дают доступ. Тут нет проблем.


        1. maisvendoo
          11.02.2018 00:00

          Проблем то нет, но люди не в теме видно уже минусуют человека типа за «битую ссылку», хотя он в общем-то не причем


    1. VBKesha
      10.02.2018 18:27

      Оно конечно там всё есть но тот же HTML5 работает не везде не всегда…


  1. AckeardOct
    11.02.2018 20:36

    У меня проблема. Не могу понять. На этапе «Создание реализации функции по умолчанию».

    Если в BaseCoin я добавляю
    UFUNCTION(BlueprintNativeEvent)
    void OnOverlap_Implementation(AActor* OverlappedActor, AActor* OtherActor);

    компиляция в qtcreator проходит, но компиляция в редакторе не проходит.

    Я на арче сижу.

    И вопрос в сторону. Насолько это удобней на винде? И у тех кто использует на линуксе, нет ли косяков с меняшками редактора? У меня awesome wm если что.


    1. norlin
      12.02.2018 11:09

      UFUNCTION(BlueprintNativeEvent)
      void OnOverlap_Implementation(AActor* OverlappedActor, AActor* OtherActor);

      Такого не должно быть.


      Посмотрите в статье: там в хэдере (BaseCoin.h) объявляется метод OnOverlap, а в коде (BaseCoin.cpp) – прописывается имплементация метода OnOverlap_Implementation. В cpp файле не должно быть макроса, а в хэдере не должно быть _Implementation.


  1. Tutanhomon
    12.02.2018 11:48

    класс называется ABasePlayer, а не BasePlayer. При создании класса типа actor Unreal ставит перед названием класса префикс A (от слова actor)

    Мы же вроде от Pawn наследовались…


    1. ufna
      12.02.2018 12:32

      Pawn на самом деле APawn, тк прямой потомок Actor’а


  1. Tutanhomon
    12.02.2018 11:55

    Примечание: Если монеты не исчезают, попробуйте перезапустить редактор, чтобы выполнить полную рекомпиляцию. Для работы некоторых изменений требуется перезапуск.

    А вот здесь я как юнити программист немного расстроился.