2. 에디터에 변수와 함수를 노출

UPROPERTY 와 UFUNCTION 매크로를 사용하여 변수와 함수를 에디터에 노출시킵니다.

Choose your operating system:

Windows

macOS

Linux

  1. 우리 카운트다운 타이머는 현재 3 초 값을 사용하도록 하드코딩되어 있습니다. 카운트다운 시간을 에디터에서 원하는 값으로 설정할 수 있다면 더욱 유용할텐데, 쉽게 가능한 일입니다. Visual Studio 에서 Countdown.h 를 열고 다음 줄을 찾습니다:

    int32 CountdownTime;

    이 변수를 언리얼 엔진 에 노출시키기 위해서는, UPROPERTY 로 만들어 줘야 합니다. 그러면 엔진이 게임 실행시나 저장된 레벨 로드시에도 변수 값을 보존합니다. UPROPERTY 태그는 빈 괄호를 포함해서, 영향을 끼치고자 하는 변수 바로 위에 붙습니다:

    UPROPERTY()
    int32 CountdownTime;

    UPROPERTY 언리얼 엔진 에서 변수를 사용하는 방식을 변경하는 인수를 지원합니다. 변수를 편집가능하도록 하고프니, EditAnywhere 인수를 붙여주면 됩니다:

    UPROPERTY(EditAnywhere)
    int32 CountdownTime;

    ExposingVariable.png

    C++ 에서 변수에 코멘트를 추가할 수도 있으며, 그러면 그 코멘트가 언리얼 에디터에서의 변수 설명이 되는데, 이런 식입니다:

    //초 단위의 카운트다운 실행 시간입니다.
    UPROPERTY(EditAnywhere)
    int32 CountdownTime;

    CommentingVariable.png

    UPROPERTY 로 할 수 있는 일은 많으며, BlueprintReadWrite Category

    언리얼 에디터로 돌아와서 컴파일 을 누르면, 앞서 배치한 ACountdown 디테일 패널 에 변수가 나타나며, 이 수치를 변경하고 플레이 를 누르는 것으로 다른 타이머를 테스트할 수 있습니다.

  2. 타이머 값 변경에 추가로, 프로그래머가 아닌 개발자가 타이머가 다 되면 벌어지는 일을 바꿀 수 있도록도 해 줍시다. Visual Studio 에서 Countdown.h 를 열고 다음 줄을 찾습니다:

    void CountdownHasFinished();

    이 함수를 UFUNCTION 으로 만들어 언리얼 엔진 에 노출시킬 수 있습니다. 이렇게요:

    UFUNCTION()
    void CountdownHasFinished();

    UPROPERTY 매크로와 마찬가지로 프로그래머가 아닌 개발자에게 좀 더 많은 권한을 주기 위해 무엇을 할 수 있는지에 대한 정보를 제공해 줘야 합니다. 고려해야 할 옵션이 세 가지 있습니다:

    1. BlueprintCallable 함수는 C++ 로 작성되어 블루프린트 그래프 에서 호출 가능하지만, C++ 코드 편집 없이는 변경이나 덮어쓰기가 불가능합니다. 이런 식으로 마킹된 함수는 프로그래머가 아닌 사람이 쓰도록 프로그래밍된 것이지만, 변경을 해서는 안되거나 변경하는 것이 바람직하지 않은 경우입니다. 쉬운 예제로 수학 함수를 들 수 있습니다.

    2. BlueprintImplementableEvent 함수는 C++ 헤더 ( .h ) 파일에 구성되나, 함수 본문은 C++ 가 아닌 전적으로 블루프린트 그래프 에서 작성되는 것입니다. 이는 보통 프로그래머가 아닌 사람에게 예상된 기본 동작이나 표준 동작이 없는 특수 상황에 대한 반응을 입맛대로 만들 수 있는 능력을 주기 위해 쓰입니다. 이에 대한 예제로는 우주선 게임에서 플레이어의 우주선이 파워업을 먹었을 때 발생하는 이벤트같은 것을 들 수 있습니다.

    3. BlueprintNativeEvent 함수는 BlueprintCallable BlueprintImplementableEvent 함수를 조합한 것 같은 것입니다. C++ 로 기본 작동방식이 프로그래밍되어 있지만, 블루프린트 그래프 로 덮어써서 보조 또는 대체 가능합니다. 이에 대한 프로그래밍을 할 때 C++ 코드는 아래처럼 항상 이름 끝에 _Implementation 이 붙는 가상 함수에 들어갑니다. 가장 유연한 옵션이므로, 이 튜토리얼에서는 이 옵션을 사용하겠습니다.

    프로그래머가 아닌 사람들에게 C++ 함수를 호출하고 블루프린트 로 덮어쓸 수 있도록 하기 위해서, Countdown.h 를 다음과 같이 변경해 줍니다:

    UFUNCTION(BlueprintNativeEvent)
    void CountdownHasFinished();
    virtual void CountdownHasFinished_Implementation();

    그리고 Countdown.cpp 에서, 다음과 같은 줄을:

    void ACountdown::CountdownHasFinished()

    이렇게 변경해 줍니다:

    void ACountdown::CountdownHasFinished_Implementation()

이제 C++ 로 기본적인 값과 함수성을 제공해 주면서도 프로그래머가 아닌 사람들이 접근하고 변경할 수 있는 변수와 함수를 만들었습니다. 프로그래머가 아닌 사람들이 어떻게 사용하는지를 확인하기 위해, ACountdown 클래스의 블루프린트 확장을 만들어 직접 변경해 보도록 하겠습니다.

완성 코드

Countdown.h

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/Actor.h"
#include "Countdown.generated.h"

UCLASS()
class HOWTO_VTE_API ACountdown : public AActor
{
    GENERATED_BODY()

public: 
    // Sets default values for this actor's properties
    ACountdown();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:
    // Called every frame
    virtual void Tick( float DeltaSeconds ) override;

    //How long, in seconds, the countdown will run
    UPROPERTY(EditAnywhere)
    int32 CountdownTime;

    UTextRenderComponent* CountdownText;

    void UpdateTimerDisplay();

    void AdvanceTimer();

    UFUNCTION(BlueprintNativeEvent)
    void CountdownHasFinished();
    virtual void CountdownHasFinished_Implementation();

    FTimerHandle CountdownTimerHandle;
};

Countdown.cpp

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "HowTo_VTE.h"
#include "Components/TextRenderComponent.h"
#include "Countdown.h"

// Sets default values
ACountdown::ACountdown()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = false;

    CountdownText = CreateDefaultSubobject<UTextRenderComponent>(TEXT("CountdownNumber"));
    CountdownText->SetHorizontalAlignment(EHTA_Center);
    CountdownText->SetWorldSize(150.0f);
    RootComponent = CountdownText;

    CountdownTime = 3;
}

// Called when the game starts or when spawned
void ACountdown::BeginPlay()
{
    Super::BeginPlay();

    UpdateTimerDisplay();
    GetWorldTimerManager().SetTimer(CountdownTimerHandle, this, &ACountdown::AdvanceTimer, 1.0f, true);
}

// Called every frame
void ACountdown::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );

}

void ACountdown::UpdateTimerDisplay()
{
    CountdownText->SetText(FString::FromInt(FMath::Max(CountdownTime, 0)));
}

void ACountdown::AdvanceTimer()
{
    --CountdownTime;
    UpdateTimerDisplay();
    if (CountdownTime < 1)
    {
        // We're done counting down, so stop running the timer.
        GetWorldTimerManager().ClearTimer(CountdownTimerHandle);
        //Perform any special actions we want to do when the timer ends.
        CountdownHasFinished();
    }
}

void ACountdown::CountdownHasFinished_Implementation()
{
    //Change to a special readout
    CountdownText->SetText(TEXT("GO!"));
}
언리얼 엔진 문서의 미래를 함께 만들어주세요! 더 나은 서비스를 제공할 수 있도록 문서 사용에 대한 피드백을 주세요.
설문조사에 참여해 주세요
건너뛰기