関連ブログ
- [UE5] 元の位置に戻るカメラの実装 2024.12.18UE
- [UE5]難易度変更に対応したシューティングゲームを作ってみよう 2024.12.11UE
- [UE5] インタラクト可能なモノの量産に役立つBPを作ってみよう 2024.12.04UE
CATEGORY
2024.02.07UE5UE/ C++
執筆バージョン: Unreal Engine 5.3 |
皆様こんにちは。
ゲーム開発の中盤~終盤で、「CPU処理が重いから最適化してほしい」「BPのTickは重いからC++にしよう!」
なんてことよくありますよね?
実際にはBPの処理負荷が増える原因はループ処理や配列処理の書き方によるものですが、
「C++で書けるならそれに越したことはない!」ということで、書き方を学んでいきましょう!
まずは今回の要件はこちらになります
①レベルに配置するアセットはBlueprint
②回転開始のイベントはBlueprintから呼び出す
③回転動作の処理はC++に記述
④ドアの動き方をCurveAssetでいじれるようにする
今回はC++を使いますので、C++が有効になったプロジェクトを用意していきます。
テンプレートに関してはFirst Personを使っていきます。
「Tools」のメニューから「New C++ Class…」を選択します。
今回は親クラスにはActorを指定します。
追加するクラス名を「MyDoor」として、ヘッダーファイルとcppファイルを作成します。
作成したADoorクラスに以下の定義と処理を記述します。
ポイントは「UPROPERTY(EditDefaultsOnly)」を指定することで
開いたときの角度と開閉時の動き方をBPクラス側から指定できることと、
「 UFUNCTION(BlueprintImplementableEvent)」を指定した関数を使って、
BPクラス側で用意した回転の支点となるコンポーネントを取得できることです。
BlueprintImplementableEventを使うと、C++のヘッダーで宣言をしたうえで
BPクラス側でオーバーライドして実装を記述できるため、C++から呼び出せるBPイベントを作ることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "MyDoor.generated.h" UCLASS() class MYPROJECT_API AMyDoor : public AActor { GENERATED_BODY() protected: virtual void BeginPlay() override; public: virtual void Tick(float DeltaTime) override; private: //開閉処理の進行時間 float ElapsedTime; //開閉方向 1=開く 0=何もしない -1=閉じる int MoveDir = 0; //ドアが閉まっているときのアクターのローテーション float ClosedDoorYawRotation; //ドアが開ききっているのアクターのローテーション float OpenedDoorYawRotation; public: //開いた時の角度 UPROPERTY(EditDefaultsOnly) float OpenAngle = 120.0f; //開閉の仕方を指定するカーブアセット UPROPERTY(EditDefaultsOnly) class UCurveFloat* MovementCurve; public: //ドアを開く処理 BPから呼び出す想定 UFUNCTION(BlueprintCallable) void Open(); //ドアを閉じる処理 BPから呼び出す想定 UFUNCTION(BlueprintCallable) void Close(); //回転の支点となるSceneコンポーネントを取得する関数 UFUNCTION(BlueprintImplementableEvent) class USceneComponent* GetRotationRoot(); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
#include "MyDoor.h" void AMyDoor::BeginPlay() { Super::BeginPlay(); USceneComponent* RotationRootComponent = GetRotationRoot(); if(IsValid(RotationRootComponent)) { ClosedDoorYawRotation = RotationRootComponent->GetRelativeRotation().Yaw; OpenedDoorYawRotation = ClosedDoorYawRotation + OpenAngle; } SetActorTickEnabled(false); } void AMyDoor::Tick(float DeltaTime) { Super::Tick(DeltaTime); //ドアが開閉動作中か? if ((MoveDir == 1) || (MoveDir == -1)) { //回転の支点となるシーンコンポーネントの取得 USceneComponent* RotationRootComponent = GetRotationRoot(); //カーブアセットと支点コンポーネントは有効か? if(IsValid(MovementCurve) && IsValid(RotationRootComponent)) { //進行時間を更新する ElapsedTime += DeltaTime * MoveDir; //Curveの進行時間の最小値と最大値を取得 float MinCurveTime, MaxCurveTime; MovementCurve->GetTimeRange(MinCurveTime, MaxCurveTime); //進行時間の範囲を超えないように丸め込み ElapsedTime = FMath::Clamp(ElapsedTime, MinCurveTime, MaxCurveTime); //閉じてる時から開いてる時までの割合(0.0~1.0)を取得 float MoveAlpha = MovementCurve->GetFloatValue(ElapsedTime); //座標の更新 FRotator NewRotation = RotationRootComponent->GetRelativeRotation(); NewRotation.Yaw = FMath::Lerp(ClosedDoorYawRotation, OpenedDoorYawRotation, MoveAlpha); RotationRootComponent->SetRelativeRotation(NewRotation); //進行時間がCurveの最大値を超えたらTickを停止する if (ElapsedTime >= MaxCurveTime) { MoveDir = 0; SetActorTickEnabled(false); } } } } void AMyDoor::Open() { //移動フラグとTick処理を有効にする MoveDir = 1; SetActorTickEnabled(true); } void AMyDoor::Close() { //移動フラグとTick処理を有効にする MoveDir = -1; SetActorTickEnabled(true); } |
ここからはエディタでの作業です。
ContentDrawer(またはContentBrowser)からAdd→Miscellaneous→Curveと選択し、CurveFloatを選択します。
アセット名はCV_Doorとしておきます。
以下の画像のようにコンポーネントを追加します。
それぞれ追加する際のクラスはこちらです。
RotationRoot:SceneComponent
Cube:StaticMeshComponent
Box:Box Collision
CubeはMeshにはBasicShapesのCubeを指定したうえで、2枚目の画像のように大きさと位置を調節してください。
Boxコリジョンの大きさと位置に関してはドア(Cube)の周りになるように適切と思う位置に配置してください。
コンポーネントの追加等が終わったら、EventGraphに処理を書きます。
BlueprintImplementableEventでBPクラス側で実装するようにした「GetRotationRoot」を実装します。
FUNCTIONSのOverrideから「GetRotationRoot」を選び、関数定義を作成します。
作成できたら2枚目の画像のように「RotationRoot」をReturnValueに繋ぎましょう。
ここまでの設定が終わったらEditDefaultsOnlyでBPクラス側から指定可能にした変数を変更していきます。
ClassDefaultsを押した後、DetailsタブからOpenAngleとMovementCurveを指定します。
カーブアセットの用意で作成したCV_Doorを使用した際の挙動です。クリックすると再生されます。
完成したドアの挙動はこちらのgifです。
無事ドアに近づくと開き、離れると閉じていますね。
ここでもう一つカーブアセットを用意してみます。
先ほどよりも早く動作し、最後に揺れるようにしています。
BP_DoorのMovementCurveを差し替えた挙動はこちらのgifです。クリックすると再生されます。
ちゃんと挙動が変わりましたね。
Curveアセットはこだわりの挙動を作る際にとても便利な調整方法ですが、
C++で使っている記事が少なかったので今回のブログを執筆いたしました。
今回使用したUCurveFloat以外にもVectorならUCurveVector、
リニアカラーならUCurveLinearColorなど対応したものがあります。
Curveアセットをはじめ、UEにはエディタで調整する便利なアセットが多数あります。
用意や調整はエディタでしかできませんが、値の取得などはC++でも可能なので、
今回紹介した方法等でC++側から参照して活用できるといいですね!