[UE4] チュートリアル時の試行錯誤 (変数、タイマー、イベント)

# 背景

https://docs.unrealengine.com/ja/Programming/Tutorials/VariablesTimersEvents/1/index.html

こちらの変数、タイマー、イベントのチュートリアルを見ながら実装したところ、コンパイルエラーが起きたので、直し方を書きます。

# 結論

- GetWorldTimerManager()使う時は`#include "TimerManager.h"`を追加する
- UTextRenderComponentなどコンポーネント使う時は、`.hファイル`にinclude忘れずに
- `.generated.h`はincludeヘッダー群の**一番最後に書く**

# コンパイルエラーがでたコード

CppTurorial2というプロジェクトにCountDown.hとCountDown.cppを実装しました。(チュートリアルではCountdown.cppと小文字である)

```cpp:CountDown.h
// Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.

#pragma once

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

UCLASS()
class CPPTUTORIAL2_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
    int32 CountdownTime;

    UTextRenderComponent* CountdownText;

    void UpdateTimerDisplay();

    void AdvanceTimer();

    void CountdownHasFinished();

    FTimerHandle CountdownTimerHandle;
};
```

```cpp:CountDown.cpp
// Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.

#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()
{
    //Change to a special readout
    CountdownText->SetText(TEXT("GO!"));
}
```

# エラー内容

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/3dfc18c5-3e65-8320-b478-a3950046e6f2.png)

これを見ると

` GetWorldTimerManager()` と `UTextRenderComponent`にエラーが出ています。

# チュートリアル通りなのになぜ？調べてみた

##  GetWorldTimerManager()について

不完全な型は使用できません。でググると
https://docs.microsoft.com/ja-jp/cpp/c-language/incomplete-types?view=vs-2019

... わからん、メンバーがまだ指定されていない構造体型。なのか？

GetWorldTimerManager()でググると、

https://docs.unrealengine.com/en-US/API/Runtime/Engine/GameFramework/AActor/GetWorldTimerManager/index.html

ちゃんと、#include "GameFramework/Actor.h"してるんだけどな...

http://papersloth.hatenablog.com/category/UE4?page=1541062800

UE4.20の記事、期待できそう。

サンプルコードを見てみると

> `#include "TimerManager.h"`

お？試しに、CountDown.cppに追加して、ビルドしてみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/b5e12632-90cc-77e8-8187-e14f3c727fbb.png)

不完全な型は使用できません。がなくなった！

## UTextRenderComponentについて

同じく、includeのし忘れを疑ってみる。文字化けしてみれない...

UTextRenderComponentでググる

https://docs.unrealengine.com/en-US/API/Runtime/Engine/Components/UTextRenderComponent/index.html

`#include "Components/TextRenderComponent.h"`かぁ、そういえばCountDown.hになかったな、追加してみよう。ビルド。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/eda90361-f07c-2942-5c5b-d2bc848ac678.png)

`#include "CountDown.generated.h"`はincludeヘッダの最後でなければならないらしい

```
#include "GameFramework/Actor.h"
#include "CountDown.generated.h"
#include "Components/TextRenderComponent.h"
```

ほんとだ

```
#include "GameFramework/Actor.h"
#include "Components/TextRenderComponent.h"
#include "CountDown.generated.h"
```

これでビルド。

やった正常終了！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/8a7ac838-aaa4-f4fa-4985-7fa5b354986e.png)

再生すると？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/840f1bd5-602e-7b51-742d-ddf88fc80771.png)

ちゃんとGO!が表示されました。

# 最終コード

残りのチュートリアルもやってみた後のコード

```cpp:CountDown.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TextRenderComponent.h"
#include "CountDown.generated.h"

UCLASS()
class CPPTUTORIAL2_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 DeltaTime) 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;
};

```


```cpp:CountDown.cpp



// Fill out your copyright notice in the Description page of Project Settings.


#include "CountDown.h"
#include "Components/TextRenderComponent.h"
#include "TimerManager.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) {
		GetWorldTimerManager().ClearTimer(CountdownTimerHandle);
		CountdownHasFinished();
	}
}

void ACountDown::CountdownHasFinished_Implementation() {
	CountdownText->SetText(TEXT("GO!"));

}

```