Choose your operating system:
Windows
macOS
Linux
-
우리 카운트다운 타이머는 현재 3 초 값을 사용하도록 하드코딩되어 있습니다. 카운트다운 시간을 에디터에서 원하는 값으로 설정할 수 있다면 더욱 유용할텐데, 쉽게 가능한 일입니다. Visual Studio 에서
Countdown.h
를 열고 다음 줄을 찾습니다:int32 CountdownTime;
이 변수를 언리얼 엔진 에 노출시키기 위해서는,
UPROPERTY
로 만들어 줘야 합니다. 그러면 엔진이 게임 실행시나 저장된 레벨 로드시에도 변수 값을 보존합니다.UPROPERTY
태그는 빈 괄호를 포함해서, 영향을 끼치고자 하는 변수 바로 위에 붙습니다:UPROPERTY() int32 CountdownTime;
UPROPERTY
는 언리얼 엔진 에서 변수를 사용하는 방식을 변경하는 인수를 지원합니다. 변수를 편집가능하도록 하고프니,EditAnywhere
인수를 붙여주면 됩니다:UPROPERTY(EditAnywhere) int32 CountdownTime;
C++ 에서 변수에 코멘트를 추가할 수도 있으며, 그러면 그 코멘트가 언리얼 에디터에서의 변수 설명이 되는데, 이런 식입니다:
//초 단위의 카운트다운 실행 시간입니다. UPROPERTY(EditAnywhere) int32 CountdownTime;
UPROPERTY
로 할 수 있는 일은 많으며,BlueprintReadWrite
나Category
언리얼 에디터로 돌아와서 컴파일 을 누르면, 앞서 배치한
ACountdown
의 디테일 패널 에 변수가 나타나며, 이 수치를 변경하고 플레이 를 누르는 것으로 다른 타이머를 테스트할 수 있습니다. -
타이머 값 변경에 추가로, 프로그래머가 아닌 개발자가 타이머가 다 되면 벌어지는 일을 바꿀 수 있도록도 해 줍시다. Visual Studio 에서
Countdown.h
를 열고 다음 줄을 찾습니다:void CountdownHasFinished();
이 함수를
UFUNCTION
으로 만들어 언리얼 엔진 에 노출시킬 수 있습니다. 이렇게요:UFUNCTION() void CountdownHasFinished();
UPROPERTY
매크로와 마찬가지로 프로그래머가 아닌 개발자에게 좀 더 많은 권한을 주기 위해 무엇을 할 수 있는지에 대한 정보를 제공해 줘야 합니다. 고려해야 할 옵션이 세 가지 있습니다:-
BlueprintCallable
함수는 C++ 로 작성되어 블루프린트 그래프 에서 호출 가능하지만, C++ 코드 편집 없이는 변경이나 덮어쓰기가 불가능합니다. 이런 식으로 마킹된 함수는 프로그래머가 아닌 사람이 쓰도록 프로그래밍된 것이지만, 변경을 해서는 안되거나 변경하는 것이 바람직하지 않은 경우입니다. 쉬운 예제로 수학 함수를 들 수 있습니다. -
BlueprintImplementableEvent
함수는 C++ 헤더 (.h
) 파일에 구성되나, 함수 본문은 C++ 가 아닌 전적으로 블루프린트 그래프 에서 작성되는 것입니다. 이는 보통 프로그래머가 아닌 사람에게 예상된 기본 동작이나 표준 동작이 없는 특수 상황에 대한 반응을 입맛대로 만들 수 있는 능력을 주기 위해 쓰입니다. 이에 대한 예제로는 우주선 게임에서 플레이어의 우주선이 파워업을 먹었을 때 발생하는 이벤트같은 것을 들 수 있습니다. -
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!"));
}