Language:
Page Info
Engine Version:
The translation of this page is out of date. Please see the English version for the latest version of the page.

3.ブループリントで C++ コードを拡張およびオーバーライドする

このチュートリアルでは、 ブループリント を使用した C++ クラスの機能拡張方法を紹介します。ブループリントのチュートリアルとしてではなく、C++ コードの記述にミスがないことを確認するテストを目的としています。ブループリント の導入に関しては ブループリント クイックスタートガイド を一読することをお勧めします。

  1. "Countdown1" と名付けられた ACountdown インスタンスのビヘイビアを変更するには、まず編集可能な "Countdown1" の ブループリント バージョンをエディタで作成しなくてはいけません。これを行うには、ワールド アウトライナー で "Countdown1" を選択して [Details (詳細)] パネル[Blueprint/Add Script] ボタンをクリックします。

    AddScript.png

    ここで修正する ACountdown クラスを格納する Blueprint アセットにパスを割り当てて名前を付けることができます。

    SelectBlueprintPath.png

    "Countdown1" のブループリント バージョンを表す新規アセットが作成されます。"Countdown1" もこの新規ブループリントのインスタンスと置き換えられるため、ブループリントを変更するとゲーム内の "Countdown1" に影響を及ぼします。

  2. アンリアル エディタコンテンツ ブラウザ に表示される新規アセットへ自動的に移動します。アセットを 右クリック し、[Edit (編集) ...] を選択して、 Blueprint Graph (ブループリントグラフ)Component (コンポーネント) 階層、 Default Values (デフォルト値) を修正します。

    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

    後は作成したいエフェクトを選択するだけです。"Emitter Template" で "Select Asset" をクリックすると、適切なエフェクト アセットのリストを取得できます。この場合、"P_Explosion" が適切であるため、これを選択します。

    SelectParticle.png

  5. ブループリント エディタ の左上にある [Compile (コンパイル)] ボタンをクリックして変更を保存します。

  6. ここで [Play (再生)] を押すと、カウントダウンが始まり、カウントダウン数がゼロになると爆発します。

    Explosion_Zero.png

    しかし、カウントダウンは最後に「0」ではなく「GO!」となるようにプログラミングしました。C++ の機能を ブループリント ビジュアルスクリプティングに完全に置きかえたため、これはもう起こりません。この状況は意図したものではないため、この関数の C++ バージョンへの呼び出しを追加しなくてはいけません。Countdown Has Finished ノードを右クリックして、コンテキスト メニューから Add call to parent function を選択します。

    CallToParent_Menu.png

    この作業が終了すると Parent:Countdown Has Finished とラベル付けされたノードが イベントグラフ に配置されます。Parent ノードを接続する典型的な場所は、イベントノードへの直接接続です。やってみましょう。Parent-call ノードはその他のノード同様に、何度でもどこからでも呼び出すことができるため、これは必須ではありません。

    CallToParent_ConnectPins.png

    これにより Spawn Emitter At Location への接続が置換されるため、 Parent:Countdown Has Finished ノードの右側 (出力) にある実行ピンを Spawn Emitter At Location に接続する必要があります。接続しなければ実行しません。

    CallToParent_FixPins.png

    これでゲームの実行時にカウントダウンが終了すると "GO!" (C++ コードから) という文字と爆発 (ブループリント グラフから) が表示されます!

    Explosion_Go.png

完成コード

Countdown.h

// Copyright 1998-2017 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-2017 Epic Games, Inc. All Rights Reserved.

#include "HowTo_VTE.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!"));
}