UE5 C++&BP 02 EventBeginPlayでPrintStringを実行する
MetaSoundのMidiファイル連携が一番の目標だったので、目標を達成してしまうと惰性で記事を書いている気になってきました。
この後何をしたらいいのだろう?
プラグイン開発は出来るようになりたいけど、作りたいプラグインも特には無いなぁ
一通り書けたらBoothに書籍を出してみよう
漠然と今日も何か書かないとなぁ
そんな時に目に入ったツイート
Zennを覗いてみると素晴らしい内容のUnrealEngineのBookが無料で公開されています!
Zennというサイトで本が出せるのか!
朝活でやっている「C++&Blueprint」の書けたところまでの内容や「プラグイン開発」のVisual Studioの初期設定をがっちゃんこしたBookを作成してみました。
目標を宣言するのが大事!
2022年始まって、目標を宣言するツイートが沢山流れてきました。
宣言するのも大事だなぁ
宣言してみたらスゴイ反響が多くて
書かなくては!何より自分が一番必要としている!
朝から頑張る気力を頂きありがとうございます。
日々書いている内容を考えるとnoteよりQiitaやZennの方が適しているので、移行しようかとも考えたのですが、こういった落語の枕(落語で最初に話す話の前の挨拶)を書けるのもnoteの良さなのでnoteで継続的に執筆することにしました。
始めたことを途中で変えるのも良くないので、ベースであるnoteを拡張していけるように頑張って続けていきます。
今後ともnoteやtwitterにいいね、フォロー、リツイートなどで応援して頂けると嬉しいです。
たった1日で12月から初めてから1ヶ月のフォロワー数を越えてしまって
もうやるっきゃない!
最後までやり切りますのでのんびりとお付き合いください。
前回は「クラスを作成する」方法について調査しました。
今回は「EventBeginPlayでPrintStringを実行する」方法について調査していきます。
前回からの続きになります。
EventBeginPlayでPrintStringを実行する
【Blueprint】EventBeginPlayでPrintStringを実行する
Mapsフォルダを作成してレベルを保存する
学習用のレベルとして保存します。
Mapsフォルダを作為します。
[File]メニューからCurrent Levelを保存します。
ショートカット「Ctrl + S」でも保存できます。
「Maps」フォルダに[Chapter_2_3_HelloWorld]という名称で保存します。
Blueprint Editorについて
Class Blueprint「BP_SampleActor」をダブルクリックし、Blueprint Editorを開きます。
Blueprint Editorが開きます。
Actor ClassのBlueprintには3種類のタブが用意されています。
Viewport:コンポーネントを設定します。
Construction Script:レベルのViewportに配置した時に、変更できる処理を実装します。
Event Graph:ブループリントの処理を実装します。
今回はEvent Graphに処理を実装します。
1つ1つのメニューやボタンについては、公式ドキュメントを参照してください。
Event BeginPlayにPrint Stringを実行する処理を実装する
Print Stringノードを実行して、プレイした時にLevelEditorのViewportに文字を出力する処理を実装します。
まずはPrint Stringノードを追加します。
次の手順はノードを追加する時の基本的な手順です。
EventGraphを選択します。
EventGraphの空きスペースを右クリックします。
メニューの検索バーに[print string]と入力して検索します。
メニューから[Print String]を選択します。
Event BeginPlayからPrintStringを実行するように実装します。
BeginPlayイベントノードは[Play]ボタンをクリックした時に実行されるイベントです。
Event BeginPlayの実行ピンとPrintStringの実行ピンを接続します。
実行ピンは野球のホームベースを横にしたようなアイコンです。
マウスを左クリックし、ドラッグ&ドロップすることで接続することが出来ます。Print StringのIn Stringに「Hello World!」と設定します。
処理が実行されるかどうか確認します。
その前に、ノードを変更して確認したい時は左上の[Compile]ボタンをクリックして、[Save]ボタンをクリックするようにしてください。
[Compie]ボタンをクリックすると、エラーかどうか分かったり、Blueprintの変更が反映されます。[Compile]ボタンをクリックしないと正しい結果を確認することが出来ないことがありますので、プレイして確認する前には必ず[Compile]ボタンをクリックする癖をつけましょう。
LevelEditorのViewportにBlueprintを追加してプレイする
「BP_SampleActor」をLevelEditorのViewportに追加します。
追加する方法は2つあります。
コンテンツブラウザから「BP_SampleActor」をレベルのビューポートにドラッグ&ドロップ
Place Actorsの検索バーで「BP_SampleActor」を検索して、レベルのビューポートにドラッグ&ドロップ
「Play」ボタンをクリックします
Level EditorのViewport左上に「Print String」ノードの[In String]に設定した文字列の「Hello World!」が表示されます。
キーボードの[ESC]キーで終了します。
PrintStringノードの隠された設定について
「BP_SampleActor」をダブルクリックし、Blueprint Editorを開きます。
Print Stringノードの[Development Only]にある▽ボタンをクリックします。
Print Stringの左側に隠されていた入力ピンが表示されます。
Text Colorの右側の水色をクリックすると[Color Picker]ダイアログが表示されます。[Color Picker]から別の色を選択して、[OK]ボタンをクリックします。
Text Colorの右側の色が[Color Picker]で選択色に変わりました。
Durationの入力値を[10.0]に変更します。
Durationは表示時間を設定することが出来ます。[1.0]の時に1秒表示するので、[10.0]を設定すると10秒間表示するようになります。
英単語が分からない時に、「英単語 意味」で検索すると何をしてくれるピンなのか分かります。
最近ではGoogle ChromeのURLに文字を入力するだけで日本語の訳が表示されます。
ノードを変更したので、忘れずに[Compile] > [Save]を実行します。
Level Editor側に戻って、[Play]ボタンをクリックします。
Level EditorのViewport左上に表示される文字列が10秒間表示されます。
文字列の色もColor Pickerで設定した色に表示されました。
Outputタブを表示します。
一番下までスクロールすると[Print String]の[In String]で設定した文字列が出力されています。
LogBlueprintUserMessages: [BP_SampleActor_C_1] Hello World!
Print Stringは2個所にIn Stringで設定した文字列を出力することが出来ます。
Output Logタブが表示されていない時は、[Window]>[Output Log]から表示することが出来ます。
[Print String]の[Print to Screen]、[Print to Log]のチェックボックスを無効にすると、[In String]の文字列が出力されなくなります。
Print Stringノードはまた使用するので、確認が済んだらチェックボックスを有効に戻しましょう。
Blueprintのコメント
Blueprintにコメントを書きます。
コメントは処理を読む人のヒントになります。自分自身でも久しぶりに読む処理は分からないこともあるので、未来の自分に当てたヒントにもなります。
コメントは次の2種類あります。
ノードコメント
コメントボックス
ノードコメントを入力する
ノードコメントはノード1つに対してコメントします。
ノードを右クリックし、Node Commentのテキストボックスにコメントを入力します。
ノードコメントを入力するとノードの右上にコメントが表示されます。
このノードが何をしているのか伝えるのに重宝します。
コメントボックスを追加する
コメントボックスは複数ノードにコメントする時に重宝します。1つのノードに対しても追加することが出来ます。
複数のノードを選択したら、キーボードの「C」キーを入力します。
文字列部分にコメント入力します。
詳細パネルから更にコメントボックスの色やフォントサイズの設定を変更することが出来ます。
[Show Bubble When Zoomed]を有効にすると,ズームアウトした時にノードコメントのようにコメントボックスのコメントを表示することが出来ます。
[Color Bubble]を有効にすると、コメントの色を[Comment Color]の色で表示することが出来ます。
[Move Mode]はコメントボックスを動かした時の設定です。
デフォルトで設定されている[Group Movement]はコメントボックスを動かすと、コメントボックスで囲まれているノードを一緒に動かすことが出来ます。
[Move Mode]を[Comment]に変更すると、コメントボックスだけを移動することが出来るようになります。
コメントを残すことは大事ですが、処理を変更した時にコメントを変更し忘れてしまうことがよくあります。処理とコメントが違うと混乱してしまうので、処理を修正したらコメントも必ず修正しましょう。
【C++】EventBeginPlayでPrintStringを実行する
Event BeginPlayにPrint String関数を呼び出す処理を実装する
Visual Studio 2019を開きます。
UE5のエディタからはToolsメニューか、コンテンツブラウザから作成したActorクラスをダブルクリックすることで開くことが出来ます。
CPPSampleActor.cppのBeginPlay関数にPrintString実装します。
まず、#inclueにKismet/KismetSystemLibrary.hを追加します。
#include "CPPSampleActor.h"
#include "Kismet/KismetSystemLibrary.h" //追加
Blueprintで普段使用しているPrintStringノードは、UKismetSystemLibraryクラスにPrintString関数として定義されています。
UKismetSystemLibraryクラスのPrintString関数を呼び出すように処理を追加します。
void ACPPSampleActor::BeginPlay()
{
Super::BeginPlay();
// PrintStringノードと同じ処理
// UKismetSystemLibraryクラスのPrintString関数を呼び出す
UKismetSystemLibrary::PrintString(this, "C++ Hello World!", true, true, FColor::Cyan, 2.f);
}
ソースコードを変更したら、保存します。(ショートカット:Ctrl+Sで保存すると早いです。)
Build >Build Solutionを行いコンパイルを行います。
他の手段として、UE5のLevelEditor右下の小さいアイコンがC++のソースコードをコンパイルするボタンなので、アイコンをクリックするとコンパイルが実行されます。
「Compile Complite!」が表示されればコンパイル成功です。
LevelEditorのViewportにC++のクラスを追加してプレイする
「CPPSampleActor」をLevelEditorのViewportに追加します。
追加する方法はBlueprintと同じです。
コンテンツブラウザから「CPPSampleActor」をレベルのビューポートにドラッグ&ドロップ
Place Actorsの検索バーで「CPPSampleActor」を検索して、レベルのビューポートにドラッグ&ドロップ
C++のActorクラスにはコンポーネントが何もないのでアイコンが表示されません。レベルに追加されたかは[World Outliner]から確認してください。
[Play]ボタンをクリックします。
BluepintのPrintStringノードと同様にLevelEditorのVieport左上に出力がされました。
Output LogタブにもBlueprintノードのPrintStringノードと同様の出力がされています。
UKismetSystemLibraryクラスのPrintString関数とBlueprintのPrintStringノードを比較する
UKismetSystemLibraryクラスのPrintString関数とBlueprintのPrintStringノードを比較します。
UKismetSystemLibraryクラスのPrintString関数の引数と、BlueprintのInputピンの関係している箇所を矢印で示してみました。
UKismetSystemLibraryクラスのPrintString関数の関係している箇所を変更するとPrintStringノードのように文字列の出力方法を変更することが出来ます。
・”文字列”の個所を別の文字に変えれば、出力される文字が変更されます。
・左側の[true]を[false]と書くと、LevelEditorのViewport左上の文字列が出力されなくなります。
・右側の[true]を[false]と書くと、Output Logタブの文字列が出力されなくなります。
・[FColor::Cyan]と書かれているところを、別の色に変更すれば色が変わります。
・[2.f]と書かれている箇所を、[10.0f]に変えれば10秒間表示されるようになっています。
関数の引数については関数について書くときに詳しく説明します。
UKismetSystemLibrary::PrintString(this, "C++ Hello World!", true, true, FColor::Cyan, 2.f);
↓
UKismetSystemLibrary::PrintString(this, "C++ Hello World! Change", false, false, FColor::White, 10.f);
ブループリントは左から右に、C++は上から下に実行される
C++とBlueprintでは処理の実行される方向が違います。
C++は処理の実行が「上から下」
Blueprintは処理の実行が「左から右」
ソースコードのプログラミングに慣れていたので、Blueprintは処理の流れる方向が違うことが最初慣れませんでした。
UE4でBlueprintに慣れている人がC++を触りだすと、上から下に処理が流れることに慣れるのに時間がかかるかもしれません。
慣れの問題なので、どちらも慣れるまで書くしか解決法が無いのでたくさん書きましょう。
PrintStringノードの正体
PrintStringノードは、KismetSystemLibrary.hで定義され、KismetInputLibrary.cppで実装されています。
ヘッダーの場所:Program Files\Epic Games\UE_5.0EA\Engine\Source\Runtime\Engine\Classes\Kismet\KismetSystemLibrary.h
PrintStringノードの定義
/**
* Prints a string to the log, and optionally, to the screen
* If Print To Log is true, it will be visible in the Output Log window. Otherwise it will be logged only as 'Verbose', so it generally won't show up.
*
* @param InString The string to log out
* @param bPrintToScreen Whether or not to print the output to the screen
* @param bPrintToLog Whether or not to print the output to the log
* @param bPrintToConsole Whether or not to print the output to the console
* @param TextColor Whether or not to print the output to the console
* @param Duration The display duration (if Print to Screen is True). Using negative number will result in loading the duration time from the config.
*/
UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject", CallableWithoutWorldContext, Keywords = "log print", AdvancedDisplay = "2", DevelopmentOnly), Category="Utilities|String")
static void PrintString(const UObject* WorldContextObject, const FString& InString = FString(TEXT("Hello")), bool bPrintToScreen = true, bool bPrintToLog = true, FLinearColor TextColor = FLinearColor(0.0, 0.66, 1.0), float Duration = 2.f);
cppの場所:Program Files\Epic Games\UE_5.0EA\Engine\Source\Runtime\Engine\Private\KismetInputLibrary.cpp
PrintStringノードの処理
void UKismetSystemLibrary::PrintString(const UObject* WorldContextObject, const FString& InString, bool bPrintToScreen, bool bPrintToLog, FLinearColor TextColor, float Duration)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // Do not Print in Shipping or Test
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
FString Prefix;
if (World)
{
if (World->WorldType == EWorldType::PIE)
{
switch(World->GetNetMode())
{
case NM_Client:
// GPlayInEditorID 0 is always the server, so 1 will be first client.
// You want to keep this logic in sync with GeneratePIEViewportWindowTitle and UpdatePlayInEditorWorldDebugString
Prefix = FString::Printf(TEXT("Client %d: "), GPlayInEditorID);
break;
case NM_DedicatedServer:
case NM_ListenServer:
Prefix = FString::Printf(TEXT("Server: "));
break;
case NM_Standalone:
break;
}
}
}
const FString FinalDisplayString = Prefix + InString;
FString FinalLogString = FinalDisplayString;
static const FBoolConfigValueHelper DisplayPrintStringSource(TEXT("Kismet"), TEXT("bLogPrintStringSource"), GEngineIni);
if (DisplayPrintStringSource)
{
const FString SourceObjectPrefix = FString::Printf(TEXT("[%s] "), *GetNameSafe(WorldContextObject));
FinalLogString = SourceObjectPrefix + FinalLogString;
}
if (bPrintToLog)
{
UE_LOG(LogBlueprintUserMessages, Log, TEXT("%s"), *FinalLogString);
APlayerController* PC = (WorldContextObject ? UGameplayStatics::GetPlayerController(WorldContextObject, 0) : NULL);
ULocalPlayer* LocalPlayer = (PC ? Cast<ULocalPlayer>(PC->Player) : NULL);
if (LocalPlayer && LocalPlayer->ViewportClient && LocalPlayer->ViewportClient->ViewportConsole)
{
LocalPlayer->ViewportClient->ViewportConsole->OutputText(FinalDisplayString);
}
}
else
{
UE_LOG(LogBlueprintUserMessages, Verbose, TEXT("%s"), *FinalLogString);
}
// Also output to the screen, if possible
if (bPrintToScreen)
{
if (GAreScreenMessagesEnabled)
{
if (GConfig && Duration < 0)
{
GConfig->GetFloat( TEXT("Kismet"), TEXT("PrintStringDuration"), Duration, GEngineIni );
}
GEngine->AddOnScreenDebugMessage((uint64)-1, Duration, TextColor.ToFColor(true), FinalDisplayString);
}
else
{
UE_LOG(LogBlueprint, VeryVerbose, TEXT("Screen messages disabled (!GAreScreenMessagesEnabled). Cannot print to screen."));
}
}
#endif
}
難しそうですが、実際に関係あるのはAddOnScreenDebugMessage関数とUE_LOGマクロです
・GEngine->AddOnScreenDebugMessage関数:LevelEditorのViewport左上に文字列を出力する関数
・UE_LOGマクロ:Output Tagに文字列を出力するマクロ
// LevelEditorのViewport左上に文字列を出力する関数
GEngine->AddOnScreenDebugMessage((uint64)-1, Duration, TextColor.ToFColor(true), FinalDisplayString);
// Output Tagに文字列を出力するマクロ
UE_LOG(LogBlueprint, VeryVerbose, TEXT("Screen messages disabled (!GAreScreenMessagesEnabled). Cannot print to screen."));
GEngine->AddOnScreenDebugMessage関数とUE_LOGマクロを使用する処理を追加して、文字列を出力してみましょう。
CPPSampleActor.cpp BeginPlay関数に処理を追加します。
void ACPPSampleActor::BeginPlay()
{
Super::BeginPlay();
// PrintStringノードと同じ処理
// UKismetSystemLibraryクラスのPrintString関数を呼び出す
UKismetSystemLibrary::PrintString(this, "C++ Hello World!", true, true, FColor::Cyan, 2.f);
// Output Logに文字列を出力するマクロ
UE_LOG(LogTemp, Display, TEXT("Display Message"));
UE_LOG(LogTemp, Warning, TEXT("Warning Message"));
UE_LOG(LogTemp, Error, TEXT("Error Message"));
// Viewportの左上に文字列を出力する関数
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::White, "C++ Hello World!", true, FVector2D(2.0f, 2.0f));
}
ソースコードを保存して、Compileを実行します。
[Play]ボタンをクリックします。
PrintString関数とは違うサイズで出力されました。
Output Logにはそれぞれ色の違う文字列が出力されています。
【まとめ】Blueprintは使いやすいように作られていて、C++は使いやすいように作ることが出来る
Blueprintのノードは使いやすいようにカスタマイズされており、C++ではBlueprintでは出来なかったことが実装することが出来ます。
車の運転(マニュアル・オートマ)みたいですね。
マニュアルがC++
オートマがBlueprint
現代ではマニュアル車よりオートマ車が普及しているので、Blueprintが普及すると車の運転のようになるかもしれません。(すでになっている?)
運転にもいろいろあります。
イニシャルDのように峠をドリフトで誰よりも早さを競う運転
カーナビに従って安全に時間通り目的地に着く運転
AIによる自動運転で運転しない
自分に適した運転をするのが一番ですね。