Chapter 47無料公開

【C++】Structure(構造体)

ポジTA
ポジTA
2024.05.13に更新

【C++】Structure(構造体)

C++でBlueprintを再現すること

BlueprintのStructure(構造体)を使用した処理をC++で再現します。
Structure(構造体)は異なるVariableTypeの変数を1つにまとめられます。
Array(配列)との違いは、Array(配列)は1つのVariableTypeしか持てませんが、Structure(構造体)は異なるVariableTypeの変数を持つことができます。

C++の構造体の作り方について知ることができます。

PrintCalcResultのInputをStructer(構造体)に変更することでより処理が見やすくなります。
C++では参照渡しをConst宣言できるので安全に処理が軽くなります。

編集するActorクラスを作成する

プロジェクトを閉じていたら、プロジェクトを開き、「Chapter_2_Structure」を開きます。

[Tools]メニューから[New C++ Class]を開きます。

親クラスに[Actor]を選択します。

ClassTypeとClass名を設定します。

Property Value
Class Type Public
Name CPPStructure

Solution Explorerから今回編集する2つのファイルを開きます。

  • CPPStructure.h
  • CPPStructure.cpp

開いたファイルを学習する初期状態に修正します。

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CPPCalcType.h"
#include "CPPStructure.generated.h"

UCLASS()
class CPP_BP_API ACPPStructure : public AActor
{
	GENERATED_BODY()

public:
	ACPPStructure();

	// Event Dispatcher[OnPrintHello]
	DECLARE_DYNAMIC_MULTICAST_DELEGATE(FPrintHelloDelegate);

	UPROPERTY(BlueprintAssignable, Category = "CPP_BP")
		FPrintHelloDelegate OnPrintHello;

	// Custom Event[PrintHello] 
	UFUNCTION()
		void PrintHello();

	int32 Sum(int32 A, int32 B);

	// Action Mappingsに設定したActionを処理する関数
	void PressedActionPrintCalcResult();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

private:
	TArray<FString> Messages = { TEXT("C++ Hello World!"), TEXT("你好 世界!"), TEXT("Bonjour le monde!"), TEXT("Hallo Welt!"), TEXT("こんにちは世界!") };

	// 計算結果を出力する関数
	void PrintCalcResult(const ECPPCalcType Type, const int32 A, const int32 B, const float PrintDuration);

	// PrintString関数のDurationに設定する変数
	const float Duration = 10.0f;

	// PrintString関数のTextColorに設定する変数
	const FLinearColor TextColor = FColor(255, 255, 255);

	// 計算用の変数
	int32 CalcVarA = 7;
	int32 CalcVarB = 3;

	// Flow Control用の変数
	bool IsPrintHello = false;
	int32 TypeIndex = 0;
	TArray<ECPPCalcType> CalcTypes = { ECPPCalcType::Add, ECPPCalcType::Subtract, ECPPCalcType::Multiply, ECPPCalcType::Divide };

	// Input設定
	void SetupInput();

	// Input Eventを処理する関数
	void PressedH();

};
CPPStructure.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "CPPStructure.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Kismet/GameplayStatics.h"

ACPPStructure::ACPPStructure()
{
	// Event Dispathcer[OnPrintHello]にCustom Event[PrintHello]をバインドする
	OnPrintHello.AddDynamic(this, &ACPPStructure::PrintHello);
}

int32 ACPPStructure::Sum(int32 A, int32 B)
{
	return A + B;
}

// Called when the game starts or when spawned
void ACPPStructure::BeginPlay()
{
	SetupInput();

	if (IsPrintHello)
	{
		// Hello World!を出力する処理
		PrintHello();
	}
	else
	{
		// 計算結果を出力する処理
		PressedActionPrintCalcResult();
	}
}

void ACPPStructure::PrintCalcResult(const ECPPCalcType Type, const int32 A, const int32 B, const float PrintDuration)
{
	switch (Type)
	{
		case ECPPCalcType::Add:
		{
			// Add(足し算)の処理
			int32 ResultAdd = Sum(CalcVarA, CalcVarB);
			FString StrResultAdd = FString::Printf(TEXT("%d"), ResultAdd);
			UKismetSystemLibrary::PrintString(
				this
				, StrResultAdd
				, true
				, true
				, FColor::Red
				, Duration
				, TEXT("None"));
			break;
		}
		case ECPPCalcType::Subtract:
		{
			// Subtract(引き算)の処理
			int32 ResultSubtract = CalcVarA - CalcVarB;
			FString StrResultSubtract = FString::Printf(TEXT("%d"), ResultSubtract);
			UKismetSystemLibrary::PrintString(
				this
				, StrResultSubtract
				, true
				, true
				, FColor::Yellow
				, Duration
				, TEXT("None"));
			break;
		}
		case ECPPCalcType::Multiply:
		{
			// Multiply(掛け算)の処理
			int32 ResultMultiply = CalcVarA * CalcVarB;
			FString StrResultMultiply = FString::Printf(TEXT("%d"), ResultMultiply);
			UKismetSystemLibrary::PrintString(
				this
				, StrResultMultiply
				, true
				, true
				, FColor::Green
				, Duration
				, TEXT("None"));
			break;
		}
		case ECPPCalcType::Divide:
		{
			// Divide(割り算)の処理
			float ResultDivide = (float)CalcVarA / (float)CalcVarB;
			FString StrResultDivide = FString::Printf(TEXT("%f"), ResultDivide);
			UKismetSystemLibrary::PrintString(
				this
				, StrResultDivide
				, true
				, true
				, FColor::Blue
				, Duration
				, TEXT("None"));
			break;
		}
	}
}

void ACPPStructure::SetupInput()
{
	// 入力を有効にする
	EnableInput(UGameplayStatics::GetPlayerController(GetWorld(), 0));

	// HキーのPressedとReleasedをバインドする
	InputComponent->BindKey(EKeys::H, IE_Pressed, this, &ACPPStructure::PressedH);

	// ActionMappingsに設定したActionをバインドする
	InputComponent->BindAction("ActionPrintCalcResult", IE_Pressed, this, &ACPPStructure::PressedActionPrintCalcResult);
}

void ACPPStructure::PressedH()
{
	// Event Dispathcer[OnPrintHello]をコールする
	OnPrintHello.Broadcast();
}

void ACPPStructure::PressedActionPrintCalcResult()
{
	// 計算結果を出力する処理
	PrintCalcResult(CalcTypes[TypeIndex], CalcVarA, CalcVarB, Duration);

	TypeIndex++;
	TypeIndex = TypeIndex % CalcTypes.Num();
}

void ACPPStructure::PrintHello()
{
	bool NotBonjour = true;
	int32 HelloIndex = 0;

	while (NotBonjour)
	{
		// 文字列に"Bonjour"が含まれているか
		if (Messages[HelloIndex].Contains(TEXT("Bonjour")))
		{
			// While Loopの条件をfalseに設定する
			NotBonjour = false;
		}
		else
		{
			// Messagesの値を出力する
			UKismetSystemLibrary::PrintString(this, Messages[HelloIndex], true, true, TextColor, Duration, TEXT("None"));
		}
		// HelloIndexをインクリメント
		HelloIndex++;
	}

	// CompletedをPrintStringで出力する
	UKismetSystemLibrary::PrintString(this, TEXT("Completed"), true, true, FColor::Cyan, Duration, TEXT("None"));
}

Structure(構造体)「FCPPCalcInfo」を作成する

Structure(構造体)「FCPPCalcInfo」を作成します。

[Tools]メニューから[New C++ Class]を開きます。

親クラスに[None]を選択します。

ClassTypeとClass名を設定します。

Property Value
Class Type Public
Name CPPCalcInfo

「CPPCalcInfo.h」のみ使用するので、「CPPCalcInfo.cpp」は削除します。

「CPPCalcInfo.h」を開きます。

Unreal Engineのc++でStructure(構造体)を宣言する時は以下のように書きます。

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

#pragma once

#include "CoreMinimal.h"
#include "(構造体名).generated.h"

USTRUCT(BlueprintType)
struct F(構造体名)
{
	GENERATED_BODY()

	変数宣言
};

BlueprintのStructure(構造体)「FBPCalcInfo」をC++で再現します。

C++で再現すると以下のような書き方になります。

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

#pragma once

#include "CoreMinimal.h"
#include "CPPCalcType.h"
#include "CPPCalcInfo.generated.h"

USTRUCT(BlueprintType)
struct FCPPCalcInfo
{
	GENERATED_BODY()

	ECPPCalcType Type = ECPPCalcType::Add;
	int32 NumA = 7;
	int32 NumB = 3;
};

Function「PrintCalcResultArgStructure」を作成する

引数にStructure(構造体)「FBPCalcInfo」を渡すFunction[PrintCalcResultArgStructure]を作成します。

C++でFunction[PrintCalcResultArgStructure]を宣言します。

Input/Output VariableName VariableType
Input CalcInfo FCPPCalcInfo
Input Duration float

引数にStructure(構造体)「FCPPCalcInfo」を使用するので、CPPCalcInfo.hをincludeに追加します。

CPPStructure.h

#include "CPPCalcInfo.h" // 追加

private// 引数に構造体を使用した計算結果を出力する関数
	void PrintCalcResultArgStructure(const FCPPCalcInfo& CalcInfo, const float PrintDuration);

Function[PrintCalcResultArgStructure]のBlueprintの処理です。

Structure(構造体)「FCPPCalcInfo」のメンバー変数にアクセスするには[構造体変数.メンバー変数名]と書きます。

void ACPPStructure::PrintCalcResultArgStructure(const FCPPCalcInfo& CalcInfo, const float PrintDuration)
{
	// 構造体変数名.メンバー変数名
	CalcInfo.Type
	CalcInfo.NumA
	CalcInfo.NumB
}

Function[PrintCalcResultArgStructure]の処理を以下のように書きます。

CPPStructure.cpp PrintCalcResultArgStructure()
void ACPPStructure::PrintCalcResultArgStructure(const FCPPCalcInfo& CalcInfo, const float PrintDuration)
{
	switch (CalcInfo.Type)
	{
		case ECPPCalcType::Add:
		{
			// Add(足し算)の処理
			// 値渡し
			int32 ResultAdd = Sum(CalcInfo.NumA, CalcInfo.NumB);
			FString StrResultAdd = FString::Printf(TEXT("%d"), ResultAdd);
			UKismetSystemLibrary::PrintString(
				this
				, StrResultAdd
				, true
				, true
				, FColor::Red
				, Duration
				, TEXT("None"));
			break;
		}
		case ECPPCalcType::Subtract:
		{
			// Subtract(引き算)の処理
			int32 ResultSubtract = CalcInfo.NumA - CalcInfo.NumB;
			FString StrResultSubtract = FString::Printf(TEXT("%d"), ResultSubtract);
			UKismetSystemLibrary::PrintString(
				this
				, StrResultSubtract
				, true
				, true
				, FColor::Yellow
				, Duration
				, TEXT("None"));
			break;
		}
		case ECPPCalcType::Multiply:
		{
			// Multiply(掛け算)の処理
			int32 ResultMultiply = CalcInfo.NumA * CalcInfo.NumB;
			FString StrResultMultiply = FString::Printf(TEXT("%d"), ResultMultiply);
			UKismetSystemLibrary::PrintString(
				this
				, StrResultMultiply
				, true
				, true
				, FColor::Green
				, Duration
				, TEXT("None"));
			break;
		}
		case ECPPCalcType::Divide:
		{
			// Divide(割り算)の処理(int > float)
			float ResultDivide = (float)CalcInfo.NumA / (float)CalcInfo.NumB;
			FString StrResultDivide = FString::Printf(TEXT("%f"), ResultDivide);
			UKismetSystemLibrary::PrintString(
				this
				, StrResultDivide
				, true
				, true
				, FColor::Blue
				, Duration
				, TEXT("None"));
			break;
		}
	}
}

Function[PrintCalcResultArgStructure]を使用した処理に編集する

Structure(構造体)の配列をBlueprintと同じになるように、C++でStructure(構造体)の配列を宣言します。

C++で構造体を初期化するには以下のように書きます。

// 構造体の初期化
FCPPCalcInfo CalcInfo = {ECPPCalcType::Add, 7, 3};

// 構造体の配列の初期化
TArray<FCPPCalcInfo> CalcInfos = {{ECPPCalcType::Add, 7, 3}, {ECPPCalcType::Subtract, 7, 3}}

「CPPStructure.h」に構造体の配列[CalcInfos]を宣言します。

CPPStructure.h
private:
	TArray<FCPPCalcInfo> CalcInfos = { {ECPPCalcType::Add, 7, 3}
	                                 , {ECPPCalcType::Subtract, 7, 3}
	                                 , {ECPPCalcType::Multiply, 7, 3}
	                                 , {ECPPCalcType::Divide, 7, 3} 
	                                 };

Blueprintの構造体の配列を取得する処理をC++で再現します。

C++で処理を再現するには以下のように書きます。

CPPStructure.cpp PressedActionPrintCalcResult()
void ACPPStructure::PressedActionPrintCalcResult()
{
	// 構造体を引数に持った計算結果を出力する処理
	PrintCalcResultArgStructure(CalcInfos[TypeIndex], Duration);

	TypeIndex++;
	TypeIndex = TypeIndex % CalcTypes.Num();
}

ソースコードを保存して、Compileを実行します。

「CPPStructure」をViewportにDrag&Dropします。
PrintStringの出力結果が分かりづらくなるので、「BP_Structure」を削除します。

Level Editorの[Play]ボタンをクリックします。

[C]キーをPressするとStructure(構造体)の配列を順番に処理します。

すべて保存

C++側の説明は以上になります。
プロジェクトをすべて保存しましょう。

Visual StudioのSolutionもすべて保存しましょう。

Visual StudioでBuild

LiveCodingを有効にした状態では、Visual StudioでプロジェクトをBuildできないです。
以下のどちらかの対応後にVisual StudioからBuildを実行します。

  • LiveCodingを無効にする
  • プロジェクトを閉じる


[LiveCodingを無効にする]か[プロジェクトを閉じる]


Visual Studioで Build > Build Solution

参照URL

https://dev.epicgames.com/documentation/ja-jp/unreal-engine/structs-in-unreal-engine

https://www.youtube.com/watch?v=F9zvOW4cm28

https://docs.microsoft.com/ja-jp/cpp/cpp/initializing-classes-and-structs-without-constructors-cpp?view=msvc-170

ソースコードとプロジェクト

ここまでのソースコードとプロジェクトファイルをGitHubからダウンロードできます。

https://github.com/posita33/UE5Starter-CPPAndBP_Projects/tree/main/Resources/Chapter_02/Structure

CPPCalcInfo.h

https://github.com/posita33/UE5Starter-CPPAndBP_Projects/blob/main/Resources/Chapter_02/Structure/Source_end/CPP_BP/Public/CPPCalcInfo.h

CPPStructure.h

https://github.com/posita33/UE5Starter-CPPAndBP_Projects/blob/main/Resources/Chapter_02/Structure/Source_end/CPP_BP/Public/CPPStructure.h

CPPStructure.cpp

https://github.com/posita33/UE5Starter-CPPAndBP_Projects/blob/main/Resources/Chapter_02/Structure/Source_end/CPP_BP/Private/CPPStructure.cpp