見出し画像

UE5から始める C++ & BP 13 【C++版】Array(配列)


この記事を書いていて、改めてBlueprintとC++では同じ処理を書いても違った思考で処理を書いていることに気付きました。

処理が同じだけど、思考が違う

不思議な感覚です。

ニュータイプがこれからドンドン生まれてくる予感がします。

ニュータイプはファンネルでビュンビュン360度から攻撃してきます。

ビジュアルスクリプトの次は立体の球体に手を突っ込んでノードを組み合わせているプログラミングかもしれません。

新しい思考で新しい作品ができていく必然

C++がだんだんCOBOLのようになっていく

COBOLは書けないのです

C++書けないけどBlueprintなら書けるがスタンダードになっていく

歴史は繰り返すし、歴史のスピードが加速している

何処へ行くのだろう

それでは、Blueprintの内容をC++で再現します。

【C++】Event Dispatcher

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

変数をArray(配列)に変更します。
Array(配列)の設定方法や取得について解説します。

Array(配列)の取得でよく使用する2つの方法について解説します。

  • Array(配列)をランダムに取得する。

  • Array(配列)を剰余(%)を使用して順番に取得する。

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

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

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

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

ClassTypeとClass名を設定します。

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

  • CPPArray.h

  • CPPArray.cpp

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

CPPArray.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 "CPPArray.generated.h"

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

public:
	ACPPArray();

	// 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:
	FString Message = "C++ Hello World!";

	// 計算結果を出力する関数
	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;
	ECPPCalcType CalcType = ECPPCalcType::Add;

	// Input設定
	void SetupInput();

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

};

CPPArray.cpp

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


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

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

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

// Called when the game starts or when spawned
void ACPPArray::BeginPlay()
{
	Super::BeginPlay();

	SetupInput();

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

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

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

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

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

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

void ACPPArray::PressedActionPrintCalcResult()
{
	// 計算結果を出力する処理
	PrintCalcResult(CalcType, CalcVarA, CalcVarB, Duration);
}

void ACPPArray::PrintHello()
{
	// Hello World!を出力する処理
	UKismetSystemLibrary::PrintString(this, Message, true, true, TextColor, Duration);
}

変数[Message]を配列の変数[Messages]に変更してDefaultValueを設定する

変数[Message]を配列の変数[Messages]に変更してDefaultValueを設定しました。

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

CPPArray.h

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

TArrayの<>内にVariableTypeを設定すると、VariableTypeの配列を宣言することになります。
Default Valueを設定するには{}内に","(カンマ)区切りで値を設定します。

// 配列の初期化
TArray<(VariableType)> VariableName = { [0], [1], [2] };

変数[Messages]から値を取得する

変数[Messages]から値を取得しました。

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

CPPArray.cpp PrintHello()

void ACPPArray::PrintHello()
{
	// Hello World!を出力する処理
	UKismetSystemLibrary::PrintString(this, Messages[4], true, true, TextColor, Duration);
}

C++の配列のGetは配列の変数の後ろに[]を入力し、[]内に取得したいIndexNoの数値を入力します。

// 配列の値を取得する
ArrayVariable[IndexNo]

Ctrl + Sでファイルを保存し、Compileを行います。

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

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

[H]キーをPressすると、変数[Messages]の[4]を取得するので、日本語の文字列が表示されました。

TEXT("こんにちは世界!")を"こんにちは世界!"に変えてみます。

CPPArray.h

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

Ctrl + Sでファイルを保存し、Compileを行います。

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

”?????????????????????!”と文字化けして出力されてしまいました。
これはUnreaEngineとテキストの文字コードが違うので起こってしまった現象です。

TEXTマクロで文字列をUnrealEngineが使用している文字コードに文字列を変換してくれます。日本語のような文字列(2バイト文字)を扱いたい時にはTEXTマクロを使用してFStringの変数に値を代入してください。

// 文字化けする
FString str = "文字列";

// 文字化けをしない
FString str = TEXT("文字列");

変数[Messages]からランダムに値を取得する

変数[Messages]からランダムに値を取得しました。

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

CPPArray.cpp PrintHello()

void ACPPArray::PrintHello()
{
	int32 randomIndex = FMath::RandRange(0, Messages.Num() - 1);

	// 1行で書くなら 
	// Messages[FMath::RandRange(0, Messages.Num() - 1)]

	// Hello World!を出力する処理
	UKismetSystemLibrary::PrintString(this, Messages[randomIndex], true, true, TextColor, Duration);
}

ランダムな範囲を取得するにはRandRange関数を使用します。

// ランダムな範囲を取得する
FMath::RandRange(Min, Max)

配列の値をランダムに取得できました。

Blueprintの処理はノードを複数つないでいますが、C++では1行で処理を書けます。

変数[CalcType]を配列の変数[CalcTypes]に変更する

剰余(%)を使用して、順番に配列を取得しました。

変数[CalcType]を配列の変数[CalcTypes]に変更します。

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

CPPArray.h

ECPPCalcType CalcType = ECPPCalcType::Add;
    ↓
TArray<ECPPCalcType> CalcTypes = { ECPPCalcType::Add, ECPPCalcType::Subtract, ECPPCalcType::Multiply, ECPPCalcType::Divide };

変数[CalcTypes]の値を順番に取得して出力する

変数[CalcTypes]の値を順番に取得して出力します。

配列のIndexNoを保持する変数を「CPPArray.h」に追加します。

CPPArray.h

private:
    int32 TypeIndex = 0;

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

ACPPArray.cpp PressedActionPrintCalcResult()

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

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

Ctrl + Sでファイルを保存し、Compileを行います。

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

[C]キーをPressすると、変数[CalcTypes]の値を順番([0:Add]> [1:Subtract] > [2:Multiply] > [3:Divide] > [0:Add] > 繰り返し)に出力します。

実行回数が配列数を越えるように実行した時の変数[TypeIndex]の値を表にしました。
[[3]:Divide]実行後、[[0]:Add]に戻って繰り返します。

BlueprintとC++の処理の対応図です。

すべて保存

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

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

まとめ

配列が終わったので最後の山場の「Loop」へ

長いようであっという間

でも、C++だけど、書いている内容はC言語

C++のStart地点にようやく立てる

Start地点に立つのはいつだって準備が大事

参照URL

【参照URL】

UE4から始めるC++&Blueprint 進捗とロードマップ

Zennで進捗報告を行い、GitHubでロードマップを公開中です。
よかったら覗いてみてください。


この記事が気に入ったらサポートをしてみませんか?