UE4 C++

Home  /  Programming  /  UE4 C++


Эту шпаргалку делаю для себя, чтобы научиться программировать на С++ под UE4, здесь собраны знания из множества видеоуроков. Все классы созданы на основе класса Actor, если не указано другое


Основы

x pitch
y yaw
z roll

Super::BeginPlay(); // вызывает конструктор родительского класса

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "1")
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = 2)

UFUNCTION(BlueprintCallable, Category = "1")
void MyFunc() const; // const говорит, что метод не будет изменять состояние объекта
// output log
UE_LOG(LogTemp, Warning, TEXT("Message"));

// screen log, замененный макросом с упрощенной формой
#define print(text) if(GEngine) GEngine->AddOnScreenDebugMessage(
    -1, 5.f, FColor::Red, TEXT(text));
print("some text"); // вызов

// местоположение игрока
FVector MyCharacter = 
    GetWorld()->GetFirstPlayerController()->GetPawn()->GetActorLocation();
if (GEngine) GEngine->AddOnScreenDebugMessage(
    -1, 2.f, FColor::Blue, FString::Printf(TEXT("Location %s"),
    *MyCharacter.ToString()));

// отключение объекта
SetActorHiddenInGame(true);
SetActorEnableCollision(false);
SetActorTickEnabled(false);

Создание Actor и Mesh component

.h

public: 
    UPROPERTY()
    USceneComponent* Root; // создаем объект

    UPROPERTY(EditAnywhere)
    UStaticMeshComponent* Mesh; // создаем меш для объекта

.cpp

    Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
    RootComponent = Root;

    Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    Mesh->AttachTo(Root); // прикрепляем меш к рутовому объекту


Триггер

Включение/выключение лампочки

.h

#include "Components/BoxComponent.h"
#include "Components/PointLightComponent.h"

public: 
    UFUNCTION()
    void OnOverlapBegin(
        UPrimitiveComponent* OverlappedComp,
        AActor* OtherActor, 
        UPrimitiveComponent* OtherComp, 
        int32 OtherBodyIndex, 
        bool bFromSweep, 
        const FHitResult& SweepResult);

    UFUNCTION(BlueprintCallable, Category = "Trigger")
    void ActiveLight(bool Active);

    UPROPERTY(BlueprintReadOnly, Category = "Trigger")
    bool bActiveLight;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trigger")
    int32 index = 0;
    
private:
    UBoxComponent* Trigger;
    UPointLightComponent* PointLight;

.cpp

AMyActor::AMyActor()
{
    PointLight = CreateDefaultSubobject<UPointLightComponent>(TEXT("PointLight"));
    PointLight->Intensity = 10000.f;
    PointLight->SetVisibility(false);
    RootComponent = PointLight;

    Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("Trigger"));
    Trigger->OnComponentBeginOverlap.AddDynamic(this, &AMyActor::OnOverlapBegin);
    Trigger->AttachTo(RootComponent);
}

void AMyActor::OnOverlapBegin(
    UPrimitiveComponent* OverlappedComp,
    AActor* OtherActor,
    UPrimitiveComponent* OtherComp,
    int32 OtherBodyIndex,
    bool bFromSweep,
    const FHitResult& SweepResult)
{
    if ((OtherActor != nullptr) &&
        (OtherActor != this) && 
        (OtherComp != nullptr) &&
        index > 0)
        bActiveLight = true;
}

void AMyActor::ActiveLight(bool Active)
{
    if(Active)
        PointLight->SetVisibility(true);
}

На основе класса MyActor создаем blueprint BP_MyActor

Показываем сообщение при входе/выходе из триггера

.h

protected:
     
    virtual void BeginPlay() override;

public:

    AMyTriggerBox();

    UFUNCTION()
    void OnOverlapBegin(class AActor* OverlappedActor, class AActor* OtherActor);
    
    UFUNCTION()
    void OnOverlapEnd(class AActor* OverlappedActor, class AActor* OtherActor);

.cpp

#include "DrawDebugHelpers.h"
#include "Engine/Engine.h"

#define print(text) if(GEngine) GEngine->AddOnScreenDebugMessage(
    -1, 1.5, FColor::Green, text)
#define printf(text, fstring) if(GEngine) GEngine->AddOnScreenDebugMessage(
    -1, 1.5, FColor::Green, FString::Printf(TEXT(text), fstring))

AMyTriggerBox::AMyTriggerBox()
{
    OnActorBeginOverlap.AddDynamic(this, &AMyTriggerBox::OnOverlapBegin);
    OnActorEndOverlap.AddDynamic(this, &AMyTriggerBox::OnOverlapEnd);
}

void AMyTriggerBox::BeginPlay()
{
    Super::BeginPlay();

    // делаем компонент Box видимым
    DrawDebugBox(
        GetWorld(), 
        GetActorLocation(), 
        GetComponentsBoundingBox().GetExtent(), 
        FColor::Purple, 
        true, -1, 0, 5);
}

void AMyTriggerBox::OnOverlapBegin(AActor* OverlappedActor, AActor* OtherActor)
{
    if (OtherActor && (OtherActor != this))
    {
        print("Overlap beging");
        printf("Overlapped object name: %s", *OverlappedActor->GetName());
    }
}

void AMyTriggerBox::OnOverlapEnd(AActor* OverlappedActor, AActor* OtherActor)
{
    if (OtherActor && (OtherActor != this))
    {
        print("Overlap end");
        printf("Overlapped object name: %s", *OtherActor->GetName());
    }
}

Включение/выключение лампочки

.h

    UPROPERTY(VisibleAnywhere, Category = "Light Switch")
    class UPointLightComponent* PointLight;

    UPROPERTY(VisibleAnywhere, Category = "Light Switch")
    class USphereComponent* LightSphere;

    UPROPERTY(VisibleAnywhere, Category = "Light Switch")
    float LightIntensity;

    UFUNCTION()
    void ToggleLight();

    UFUNCTION()
    void OnOverlapBegin(
        UPrimitiveComponent* OverlappedComp, 
        AActor* OtherActor, 
        UPrimitiveComponent* OtherComp, 
        int32 OtherBodyIndex, 
        bool bFromSweep, 
        const FHitResult& SweepResult);

    UFUNCTION()
    void OnOverlapEnd(
        UPrimitiveComponent* OverlappedComp, 
        AActor* OtherActor, 
        UPrimitiveComponent* OtherComp, 
        int32 OtherBodyIndex);

.cpp

#include "Components/PointLightComponent.h"
#include "Components/SphereComponent.h"
#include "DrawDebugHelpers.h"

ARotation::ARotation()
{
    LightIntensity = 3000.f;
    PointLight = CreateDefaultSubobject<UPointLightComponent>(TEXT("Point light"));
    PointLight->Intensity = LightIntensity;
    PointLight->bVisible = true;
    RootComponent = PointLight;

    LightSphere = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere"));
    LightSphere->InitSphereRadius(300.f);
    LightSphere->SetCollisionProfileName(TEXT("Trigger"));
    LightSphere->SetupAttachment(RootComponent);

    LightSphere->OnComponentBeginOverlap.AddDynamic(this, &ARotation::OnOverlapBegin);
    LightSphere->OnComponentEndOverlap.AddDynamic(this, &ARotation::OnOverlapEnd);
}

void ARotation::BeginPlay()
{
    DrawDebugSphere(
        GetWorld(), GetActorLocation(), 300.f, 50, FColor::Red, true, -1, 0, 2);
}

void ARotation::ToggleLight()
{
    // если включен - выключить, если выключен - включить
    PointLight->ToggleVisibility();
}

void ARotation::OnOverlapBegin(
    UPrimitiveComponent* OverlappedComp,
    AActor* OtherActor,
    UPrimitiveComponent* OtherComp,
    int32 OtherBodyIndex,
    bool bFromSweep,
    const FHitResult& SweepResult)
{
    if (OtherActor && (OtherActor != this) && OtherComp)
        ToggleLight();
}

void ARotation::OnOverlapEnd(
    UPrimitiveComponent* OverlappedComp,
    AActor* OtherActor,
    UPrimitiveComponent* OtherComp,
    int32 OtherBodyIndex)
{
    if (OtherActor && (OtherActor != this) && OtherComp)
        ToggleLight();
}


Rotation

.h

    UPROPERTY(EditAnywhere, Category = Movement)
    float AngleAxis;

    UPROPERTY(EditAnywhere, Category = Movement)
    FVector Dimensions;

    UPROPERTY(EditAnywhere, Category = Movement)
    FVector AxisVector;

    UPROPERTY(EditAnywhere, Category = Movement)
    float Multiplier;

    UPROPERTY()
    USceneComponent* Root;

    UPROPERTY(EditAnywhere)
    UStaticMeshComponent* Mesh;

.cpp

ARotation::ARotation()
{
    PrimaryActorTick.bCanEverTick = true;

    Dimensions = FVector(300, 0, 0);
    AxisVector = FVector(0, 0, 1);
    Multiplier = 50.f;

    Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
    RootComponent = Root;

    Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    Mesh->AttachTo(Root);
}

void ARotation::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    FVector NewLocation = FVector(0, 0, 400);
    AngleAxis += DeltaTime * Multiplier;
    if (AngleAxis >= 360.0f)
        AngleAxis = 0;
    FVector RotateValue = Dimensions.RotateAngleAxis(AngleAxis, AxisVector);
    NewLocation.X += RotateValue.X;
    NewLocation.Y += RotateValue.Y;
    NewLocation.Z += RotateValue.Z;
    FRotator NewRotation = FRotator(0, AngleAxis, 0);
    FQuat QuatRotation = FQuat(NewRotation);
    SetActorLocationAndRotation(
        NewLocation, QuatRotation, false, 0, ETeleportType::None);
}

.h

    UPROPERTY()
    float AngleAxis;

    UPROPERTY()
    USceneComponent* Root;

    UPROPERTY(EditAnywhere)
    UStaticMeshComponent* Mesh;

.cpp

ARotation::ARotation()
{
    PrimaryActorTick.bCanEverTick = true;

    AngleAxis = 0;

    Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
    RootComponent = Root;

    Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    Mesh->AttachTo(Root);
}

void ARotation::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    FVector NewLocation = FVector(0, 0, 800);
    FVector Radius = FVector(200, 0, 0);
    AngleAxis++;
    if (AngleAxis > 360.0f)
        AngleAxis = 1;
    FVector RotateValue = Radius.RotateAngleAxis(AngleAxis, FVector(0, 0, 1));
    NewLocation.X += RotateValue.X;
    NewLocation.Y += RotateValue.Y;
    NewLocation.Z += RotateValue.Z;
    SetActorLocation(NewLocation);
}

.h

    UPROPERTY(EditAnywhere, Category = "Rotaion")
    float pitchValue;

    UPROPERTY(EditAnywhere, Category = "Rotaion")
    float yawValue;

    UPROPERTY(EditAnywhere, Category = "Rotaion")
    float rollValue;

.cpp

ARotation::ARotation()
{
    pitchValue = 0.f;
    yawValue = 0.f;
    rollValue = 0.f;
}

void ARotation::Tick(float DeltaTime)
{
    FQuat QuatRotation = FQuat(FRotator(pitchValue, yawValue, rollValue));
    AddActorLocalRotation(QuatRotation, false, 0, ETeleportType::None);
}

Вращение вокруг вектора

.h

    float AngleAxis;

.cpp

ARotation::ARotation()
{
    AngleAxis = 0;
}

void ARotation::Tick(float DeltaTime)
{
    FVector NewLocation = FVector(0, 0, 800);
    FVector Radius = FVector(200, 0, 0);
    AngleAxis++;
    if (AngleAxis > 360.f)
        AngleAxis = 1;
    FVector RotateValue = Radius.RotateAngleAxis(AngleAxis, FVector(0, 0, 1));
    NewLocation.X += RotateValue.X;
    NewLocation.Y += RotateValue.Y;
    NewLocation.Z += RotateValue.Z;
    SetActorLocation(NewLocation);
}


Component hit

Если Actor сталкивается с объектом, то на экран выводится имя этого объекта

.h

    UPROPERTY(VisibleAnywhere)
    class UBoxComponent* MyComp;

    UFUNCTION()
    void OnCompHit(
        UPrimitiveComponent* HitComp, 
        AActor* OtherActor, 
        UPrimitiveComponent* OtherComp, 
        FVector NormalImpulse, 
        const FHitResult& Hit);

.cpp

#include "Components/BoxComponent.h"
#include "Kismet/GameplayStatics.h"

ARotation::ARotation()
{
    MyComp = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComp"));
    MyComp->SetSimulatePhysics(true);
    MyComp->SetNotifyRigidBodyCollision(true);
    MyComp->BodyInstance.SetCollisionProfileName("BlockAllDynamic");
    MyComp->OnComponentHit.AddDynamic(this, &ARotation::OnCompHit);
    RootComponent = MyComp;
}

void ARotation::OnCompHit(
    UPrimitiveComponent * HitComp, 
    AActor * OtherActor, 
    UPrimitiveComponent * OtherComp, 
    FVector NormalImpulse, 
    const FHitResult & Hit)
{
    if ((OtherActor != NULL) && (OtherActor != this) && (OtherComp != NULL))
        if (GEngine)
            GEngine->AddOnScreenDebugMessage(
                -1, 
                5.f, 
                FColor::Green, 
                FString::Printf(TEXT("Hit %s"), *OtherActor->GetName()));
}

Перемещение и вращение объекта

.h

    UPROPERTY(EditAnywhere, Category = "Location")
    FVector NewLocation;

    UPROPERTY(EditAnywhere, Category = "Location")
    FQuat NewRotation;

.cpp

void ARotation::BeginPlay()
{
    SetActorLocationAndRotation(NewLocation, NewRotation, false, 0, ETeleportType::None);  
}


Вызов функции по таймеру

.h

    UFUNCTION()
    void RepeatingFunction();

    FTimerHandle MemberTimerHandle;

.cpp

#include "TimerManager.h"

void ARotation::BeginPlay()
{
    Super::BeginPlay();
    
    GetWorldTimerManager().SetTimer(
        MemberTimerHandle, 
        this, 
        &ARotation::RepeatingFunction,
        2.f, 
        true, 
        5.f);   
}

void ARotation::RepeatingFunction()
{
    if (GEngine)
        GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("HELLO"));
}

Перемещение камеры к указанному объекту

.h

    UPROPERTY(EditAnywhere)
    AActor* MyActor;

.cpp

void ARotation::BeginPlay()
{
    Super::BeginPlay();
    
    APlayerController* OurPlayer = UGameplayStatics::GetPlayerController(this, 0);
    OurPlayer->SetViewTargetWithBlend(MyActor, 4.f); // камера будет перемещаться 4 секунды
}

Объект, к которому происходит перемещение


Нажатие кнопки

Если игрок внутри триггера, то нажатие на F включает/выключает свет

.h

    UPROPERTY(VisibleAnywhere, Category = "Light Switch")
    class UPointLightComponent* PointLight;

    UPROPERTY(VisibleAnywhere, Category = "Light Switch")
    class USphereComponent* LightSphere;

    UPROPERTY(VisibleAnywhere, Category = "Light Switch")
    float LightIntensity;

    UFUNCTION(BlueprintCallable, Category = "Light Switch")
    void ToggleLight();

.cpp

#include "Components/PointLightComponent.h"
#include "Components/SphereComponent.h"

ARotation::ARotation()
{
    LightIntensity = 3000.f;
    PointLight = CreateDefaultSubobject<UPointLightComponent>(TEXT("Light"));
    PointLight->Intensity = LightIntensity;
    PointLight->bVisible = true;
    RootComponent = PointLight;

    LightSphere = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere"));
    LightSphere->InitSphereRadius(300.f);
    LightSphere->SetCollisionProfileName(TEXT("Trigger"));
    LightSphere->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore);
    LightSphere->SetupAttachment(RootComponent);
}

Character.h

UCLASS(config=Game)
class AMyProjectCharacter : public ACharacter
{
    GENERATED_BODY()
    UPROPERTY(VisibleAnywhere, Category = "Trigger Capsule")
    class UCapsuleComponent* TriggerCapsule;

public:
    UFUNCTION()
    void OnOverlapBegin(
        UPrimitiveComponent* OverlappedComp,
        AActor* OtherActor, 
        UPrimitiveComponent* OtherComp, 
        int32 OtherBodyIndex, 
        bool bFromSweep, 
        const FHitResult& SweepResult);

    UFUNCTION()
    void OnOverlapEnd(
        UPrimitiveComponent* OverlappedComp, 
        AActor* OtherActor, 
        UPrimitiveComponent* OtherComp, 
        int32 OtherBodyIndex);

    class ARotation* CurrentLightSwitch;

protected:
    // вызывается при нажатии на кнопку F
    void OnAction();
}

Character.cpp

#include "Rotation.h" // так называется мой класс

AMyProjectCharacter::AMyProjectCharacter()
{
    TriggerCapsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Trigger"));
    // размер должен совпадать с размером коллайдера игрока
    TriggerCapsule->InitCapsuleSize(55.f, 96.f);
    TriggerCapsule->SetCollisionProfileName(TEXT("Trigger"));
    TriggerCapsule->SetupAttachment(RootComponent);

    TriggerCapsule->OnComponentBeginOverlap.AddDynamic(
        this, &AMyProjectCharacter::OnOverlapBegin);
    TriggerCapsule->OnComponentEndOverlap.AddDynamic(
        this, &AMyProjectCharacter::OnOverlapEnd);

    CurrentLightSwitch = NULL;
}

void AMyProjectCharacter::SetupPlayerInputComponent(
    class UInputComponent* PlayerInputComponent)
{
    // вызываем функцию OnAction при нажатии на кнопку F
    PlayerInputComponent->BindAction(
        "Action", IE_Pressed, this, &AMyProjectCharacter::OnAction);
}

void AMyProjectCharacter::OnAction()
{
    if (CurrentLightSwitch)
        CurrentLightSwitch->ToggleLight();
}

void AMyProjectCharacter::OnOverlapBegin(
    UPrimitiveComponent* OverlappedComp,
    AActor* OtherActor,
    UPrimitiveComponent* OtherComp,
    int32 OtherBodyIndex,
    bool bFromSweep,
    const FHitResult& SweepResult)
{
    if (OtherActor &&
        (OtherActor != this) &&
        OtherComp &&
        OtherActor->GetClass()->IsChildOf(ARotation::StaticClass()))
        CurrentLightSwitch = Cast<ARotation>(OtherActor);
}

void AMyProjectCharacter::OnOverlapEnd(
    UPrimitiveComponent* OverlappedComp,
    AActor* OtherActor,
    UPrimitiveComponent* OtherComp,
    int32 OtherBodyIndex)
{
    if (OtherActor &&
        (OtherActor != this) &&
        OtherComp)
        CurrentLightSwitch = NULL;
}
Comments are closed.