3. 블루프린트로 C++ 확장 및 덮어쓰기

블루프린트를 사용하여 C++ 함수성을 보조하거나 덮어씁니다.

Windows
MacOS
Linux
On this page

이번 튜토리얼 섹션에는 블루프린트 를 사용하여 C++ 클래스의 함수성을 확장시킵니다. 하지만 이는 C++ 코드가 제대로 작성되었나 테스트하기 위한 용도이지, 블루프린트 튜토리얼은 아닙니다. 블루프린트 에 대한 제대로 된 소개는, 블루프린트 퀵스타트 가이드 문서를 추천합니다.

  1. ACountdown 인스턴스 중 "Countdown1" 이라는 녀석의 작동방식을 에디터에서 변경하기 위해, 먼저 그 편집가능 블루프린트 버전을 만들어야 합니다. 그러기 위해서는, 월드 아웃라이너 에서 선택한 다음 디테일 패널블루프린트/스크립트 추가 버튼을 누릅니다.

    AddScript.png

    거기서, 우리 변경된 ACountdown 클래스가 저장될 블루프린트 애셋의 경로와 이름을 제공해 주면 됩니다.

    SelectBlueprintPath.png

    그러면 "Countdown1" 의 블루프린트 버전을 나타내는 애셋이 새로 생깁니다. "Countdown1" 을 새로 만든 블루프린트 인스턴스로 대체되기도 하므로, 블루프린트 에 가한 변경사항이 게임에서 "Countdown1" 에 영향을 끼칩니다.

  2. 언리얼 에디터 는 자동으로 콘텐츠 브라우저 에서 새 애셋 위치로 이동되며, 거기에 우클릭한 다음 "편집..." 을 선택하여 블루프린트 그래프, 컴포넌트 계층구조, 기본값 을 변경합니다.

    BlueprintInContentBrowser.png

    EditBlueprint.png

  3. 함수와 이벤트는 이벤트 그래프 탭에서 찾을 수 있으며, 먼저 선택하겠습니다.

    SelectEventGraph.png

    그런 다음 이벤트 그래프 창 아무데나 우클릭한 다음 우리 CountdownHasFinished 함수를 이벤트 노드로 추가하여 그 작동방식을 정의합니다.

    SelectEvent.png

  4. 이제 새로운 노드 오른편의 하양 ("실행") 핀을 좌클릭으로 끌어 놓는 것으로 함수성을 원하는 만큼 추가시킬 수 있습니다.

    DragExecPin.png

    왼쪽 마우스 버튼을 놓을 때, 어떤 함수나 이벤트를 실행하겠느냐고 묻습니다. 이 튜토리얼에서는 카운트 다운이 완료되면 파티클 시스템 을 스폰하겠습니다. Spawn Emitter At Location 노드가 필요하니, 목록에서 선택해 줍니다. 검색창에 "spawn loc" 정도 텍스트를 부분 입력하면 시간이 절약될 수 있습니다. 그런 다음 노랑 "Location" 핀을 좌클릭으로 끌어 Get Actor Location 함수에 연결합니다.

    GetActorLocation.png

    이제 보고픈 이펙트를 선택해 주면 됩니다. 이미터 템플릿 아래 애셋 선택 을 클릭하여 적합한 이펙트 애셋 목록을 구할 수 있습니다. "P_Explosion" 이 괜찮으니 선택해 주겠습니다.

    SelectParticle.png

  5. 블루프린트 에디터 좌상단의 컴파일 버튼을 클릭하여 변경사항을 저장합니다.

  6. 이제 플레이 를 누르면 카운트다운이 있은 후 0 에 도달하면 폭발이 생기는 것을 볼 수 있습니다.

    Explosion_Zero.png

    그런데 카운트다운이 "0" 이 아닌 "GO!" 라고 하도록 프로그래밍을 해 놨었습니다. C++ 함수성을 블루프린트 비주얼 스크립트로 완전히 대체했기에 더이상 벌어지지 않습니다. 이 경우는 의도했던 바가 아닌데, 이 함수의 C++ 버전 호출을 추가해 주겠습니다. Countdown Has Finished 노드를 우클릭하고 컨텍스트 메뉴에서 "Add call to parent function" 를 선택해 주면 됩니다.

    CallToParent_Menu.png

    이렇게 하면 이벤트 그래프Parent: Countdown Has Finished 라는 이름의 노드가 배치됩니다. "부모" 노드는 보통 이벤트 노드에 직접 연결하므로, 여기서는 그렇게 해 주겠습니다. 하지만 필수는 아닌데, 부모 호출 노드는 다른 것과 마찬가지로 원하는 곳 아무데서나, 심지어 몇 번이고 호출할 수 있기 때문입니다.

    CallToParent_ConnectPins.png

    이렇게 하면 Spawn Emitter At Location 로의 접속이 대체되므로, Parent: Countdown Has Finished 노드의 오른편 (나가는) 실행 핀을 거기에 연결해 주지 않으면 실행되지 않습니다.

    CallToParent_FixPins.png

    이제 게임 실행하고, 카운트다운이 끝나면 (C++ 코드의) "GO!" 와 (블루프린트 그래프 의) 폭발을 둘 다 볼 수 있습니다.

    Explosion_Go.png

완성 코드

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