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

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

Windows
MacOS
Linux
On this page
  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 로 할 수 있는 일은 많으며, BlueprintReadWriteCategory 같은 것도 다음 단계로 살펴보기 좋겠지만, 지금 필요한 작업은 다 됐습니다.

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

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

    void CountdownHasFinished();

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

    UFUNCTION()
    void CountdownHasFinished();

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

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

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

    3. BlueprintNativeEvent 함수는 BlueprintCallableBlueprintImplementableEvent 함수를 조합한 것 같은 것입니다. 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!"));
}
Select Skin
Light
Dark

Welcome to the new Unreal Engine 4 Documentation site!

We're working on lots of new features including a feedback system so you can tell us how we are doing. It's not quite ready for use in the wild yet, so head over to the Documentation Feedback forum to tell us about this page or call out any issues you are encountering in the meantime.

We'll be sure to let you know when the new system is up and running.

Post Feedback