実装方法を選んでください。
Blueprints
C++
概要
インターフェースは、さまざまなアクタ クラスによって実装可能な共通の動作や関数を定義します。この通信方法は、さまざまなアクタ クラスに同じ種類の関数を実装する際に最適です。
例えば、プロジェクト内の複数アクタ クラスに共通のアクティベーション動作を実装する場合です。その場合のアクタ クラスには、ドアや窓、車などが考えられます。各アクタ クラスは異なるので、Open Interface 関数が呼び出されたときに別々の動作をします。
このような場合は、キャスティングよりもインターフェースのほうが適しています。なぜなら、同じ関数呼び出しをすべてのアクタ クラスに使用することができ、各アクタにそれぞれキャスティングする必要がないからです。
パフォーマンス上もインターフェースのほうがキャスティングよりも優れています。キャスティングの場合は、別のアクタにキャスティングするアクタをロードすると、続いて別のアクタもメモリにロードすることになるからです。慎重に扱わないと、1 つのアクタのローディングが複数アクタのローディングを引き起こしてメモリに格納される「カスケード ローディング効果」を招くおそれがあります。
この方法では、共通の関数にアクセスするために各アクタにインターフェースを実装する必要があります。インターフェース関数を呼び出すためにアクタへの参照も必要です。この通信方法では、作業アクタとターゲット アクタ間で 1 対 1 の関係を使用します。
目標
このクイック スタート ガイドでは、インターフェースを使用して 2 種類のアクタと通信するシンプルなインタラクション システムを作成する方法を学習します。
目的
Interact 関数を使ってインターフェースを作成する。
インタラクティブなランプ アクタとドア アクタを作成し、インターフェースを実装する。
近くにあるオブジェクトの Interact インターフェース関数を呼び出すように ThirdPersonCharacter ブループリントを変更します。
1 - 必要な設定
メニューの [New Project Categories (新規プロジェクトカテゴリ)] セクションにある [Games (ゲーム)] を選択して [Next (次へ)] をクリックします。
[Third Person (サードパーソン)] テンプレートを選択し、[Next (次へ)] をクリックします。
[Blueprint (ブループリント)] と [With Starter Content (スターター コンテンツあり)] オプションを選択し、[Create Project (プロジェクトの作成)] をクリックします。
このセクションの結果
新規のサードパーソン プロジェクトを作成し、インターフェースについて学ぶ準備ができました。
2 - インターフェースを作成する
コンテンツ ブラウザ 内を右クリックし、 [Blueprints (ブループリント)] > [Blueprint Interface (ブループリント インターフェース)] を選択します。インターフェースに「BPI_Interact」という名前を付けます。
レフィックスの BPI_ を付けて複数のブループリント インターフェースに共通する名前にします。
コンテンツ ブラウザ で BPI_Interact をダブルクリックして開きます。[Functions (関数)] 一覧の 1 番目にある関数に Interact という名前を付けます。
インターフェースを コンパイル して、 保存 します。
このセクションの結果
このセクションでは、インターフェースを作成して「Interact」という関数を追加しました。これで、このインターフェースを実装するアクタに、この関数を追加することができるようになりました。
3 - インタラクティブなランプを作成する
コンテンツ ブラウザ で [Starter Content (スターター コンテンツ)] > [Blueprints (ブループリント)] に移動します。Blueprint_CeilingLight を右クリックして [Duplicate (複製)] を選択します。このブループリントに「BP_Lamp 」という名前を付け、ゲーム フォルダに移動させます。
コンテンツ ブラウザ で BP_Lamp をダブルクリックして開きます。イベント グラフ 内で右クリックして、「Add Custom Event」を検索、選択します。イベントに「ToggleLight」という名前を付けます。
ToggleLight ノードからドラッグして、「Flip Flop」を検索、選択します。
Point Light 1 コンポーネントを イベント グラフ までドラッグし、参照ノードを作成します。Point Light 1 ノードからドラッグして、「Set Visibility」を検索、選択します。以下のように、Flip Flop ノードの A pin を Set Visibility ノードに接続します。
Point Light 1 ノードと Set Visibility ノードを接続し、それらを Flip Flop ノードの B pin につなぎます。New Visibility を「True」に設定します。
メニュー バーから [Class Settings (クラス設定)] をクリックし、[Details (詳細)] パネルに移動します。
[Interfaces (インターフェース)] セクションまでスクロールし、[Add (追加)] のドロップダウンをクリックし、「BP_Interact」を検索、選択します。ブループリントを コンパイル して、保存 します。
My Blueprint タブの [Interfaces (インターフェース)] セクションに移動します。Interact インターフェース関数を右クリックして Implement イベントを選択します。すると、イベント グラフに Event Interact ノードが表示されます。
Event Interact ノードからドラッグして、 「ToggleLight」を検索、選択します。
ブループリントを コンパイル して、保存 します。
このセクションの結果
このセクションでは、Ceiling Lamp ブループリントを複製し、ライトのオンとオフを切り替えるカスタム イベントを追加しました。また、BPI_Interact インターフェースを実装し、ToggleLight イベントを実行する Interact 関数を設定しました。
4 - インタラクティブなドアを作成する
コンテンツ ブラウザ を右クリックして、[Create Basic Asset (基本アセットの作成)] セクション内の [Blueprint Class (ブループリント クラス)] をクリックします。
[Actor (アクタ)] クラスを親クラスとして選択し、ブループリントに「BP_Door」という名前を付けます。
コンテンツ ブラウザ で BP_Door をダブルクリックして開きます。次に、ブループリント エディタの [Components (コンポーネント)] パネルに移動して [Add Component (コンポーネントを追加)] ドロップダウンをクリックします。「Static Mesh」を検索、選択し、コンポーネントに「Frame」という名前を付けます。ブループリントに Static Mesh コンポーネントが追加されました。ブループリントに、Static Mesh コンポーネントが追加されます。
別の Static Mesh コンポーネントを追加し、「Door」という名前を付けます。
[Details (詳細)] パネルの Frame コンポーネントを選択し、[Static Mesh (スタティック メッシュ)]のドロップダウンをクリックし、「SM_DoorFrame」を検索し、選択します。
Door コンポーネントに対しても上の手順を繰り返し、SM_Door スタティック メッシュを追加します。
Door コンポーネントを選択した状態で、以下のように Y の位置を「45.0」に設定します。すると、ドアが枠にぴったり合っている状態で表示されるはずです。
イベント グラフ を右クリックして、「Add Custom Event」を検索、選択します。イベントに「OpenDoor」という名前を付けます。上の手順を繰り返して別のイベントを作成し、「CloseDoor」という名前を付けます。
OpenDoor イベント ノードからドラッグして、「Add Timeline」 を検索、選択します。タイムラインの名前を 「TM_Door」 にします。
CloseDoor イベントを TM_Door 上の Reverse 実行ピンに接続します。
TM_Door をダブルクリックして開きます。[Add Float Curve (Float カーブを追加)] ボタンをクリックして、Float トラックを追加し、「Alpha」という名前を付けます。[Length (長さ)] を「1.00」に設定します。
グラフを右クリックし、[Add key to CurveFloat_1 (CurveFloat_1 にキーを追加する)] を選択し、新しいポイントを追加します。[Time (時間)] と [Value (値)] を 「0.0」に設定します。
上の手順を繰り返して別のポイントを追加し、[Time (時間)] と [Value (値)] を 「1.0」に設定します。
イベント グラフ に戻って Door Static Mesh コンポーネントを イベント グラフ にドラッグし、ノードを作成します。Door ノードからドラッグして、「SetRelativeRotation」を検索、選択します。
TM_Door の Alpha ピンを Lerp ノードの Alpha ピンに接続します。最後に、B の値を以下のように「90.0」に設定します。
イベント グラフ を右クリックして、「Lerp Float」を検索、選択します。Lerp ノードの [Return Value (戻り値)] を SetRelativeRotation ノードの Yaw ピンに接続します。TM_Door の Alpha ピンを Lerp ノードの Alpha ピンに接続します。最後に、B の値を以下のように「90.0」に設定します。
TM_Door の Finished ピンからドラッグして、「Retriggerable Delay」を検索、選択します。ノードの値を「2.0」に設定します。
Retriggerable Delay ノードからドラッグして、「CloseDoor」を検索、選択します。
メニュー バーの [Class Settings (クラス設定)] をクリックします。
[Interfaces (インターフェース)] セクションの [Add (追加)] のドロップダウンをクリックし、「BP_Interact」を検索、選択します。
My Blueprint タブの [Interfaces (インターフェース)] セクションに移動します。Interact インターフェース関数を右クリックして Implement イベントを選択します。すると、イベント グラフに Event Interact ノードが表示されます。
Event Interact ノードからドラッグして、「OpenDoor」を検索、選択します。
ブループリントを コンパイル して、保存 します。
このセクションの結果
このセクションでは、BPI_Interact インターフェースの Interact 関数が呼び出されたときにドアを開くインタラクティブなドア アクタを作成しました。
5 - プレイヤー ブループリントを変更する
レベル内にある ThirdPersonCharacter ブループリントを選択します。[World Outliner (ワールド アウトライナ)] に移動し、[Edit ThirdPersonCharacter] をクリックしてブループリント エディタを開きます。
次に、ブループリント エディタの [Components (コンポーネント)] パネルに移動して [Add Component (コンポーネントを追加)] ボタンをクリックします。Sphere Collision を検索して選択します。ブループリントに、Sphere Collision コンポーネントが追加されます。
Sphere Collision コンポーネントが選択された状態で[Details (詳細)] パネルに移動し、[Radius (半径)] の値を 200 に設定します。
Sphere Collision コンポーネントを右クリックして OnComponentBeginOverlap イベントを選択し、イベント グラフ に追加します。
On Component Begin Overlap ノードからドラッグし、「Interact (Message)」を検索して選択します。[BPI Interact (BPI インタラクト)] カテゴリの下から関数を選択します。
On Component Begin Overlap イベントの Other Actor ノードを Interact 関数の Target ピンに接続します。
ブループリントを コンパイル して、保存 します。
このセクションの結果
このセクションでは、Sphere コリジョン コンポーネントを、オーバーラップするアクタの検出を行う ThirdPersonCharacter ブループリントに追加しました。アクタが球形にオーバーラップすると、ブループリントがそのアクタの BPI_Interact インターフェースから Interact 関数を呼び出します。アクタに実装されているインターフェースがない場合は失敗します (メッセージは表示されません)。
6 - インタラクション システムをテストする
BP_Door アクタと BP_Lamp アクタをレベルにドラッグします。
[Play (プレイ)] を押して、各アクタに近づいたときのインタラクションを確認します。
このセクションの結果
このセクションでは、インタラクティブなドア アクタとランプ アクタでテストし、インタラクション システムが意図したとおりに動作することを確認しました。
このクイック スタート ガイドでは、各アクタに同一の Interface 関数を実装し、別々の関数を指定する方法を学習しました。また、インターフェースがさまざまなアクタの類似の関数を実装するのに適しており、キャスティングを必要としない理由も学びました。
次のステップ
インターフェースの使い方が分かったところで、今度は「
概要
インターフェースは、さまざまなアクタによって実装可能な共通の動作やメソッドを定義します。この通信方法は、特にさまざまなアクタ クラス ブループリント上で同じ関数を実装する際に役立ちます。
例えば、プロジェクト内で複数のブループリント クラス (ドア、窓、車など) に共通の「開く」動作を実装するときにインターフェースを選択します。この例では、各アクタが異なるクラスであり、Open インターフェースが呼び出されたときはそれぞれ異なる反応をします。
パフォーマンス上もインターフェースのほうがキャスティングよりも優れています。キャスティングの場合は、別のアクタ クラス ブループリントにキャスティングするアクタ クラス ブループリントをロードすると、続いて別のブループリントもメモリにロードすることになるからです。慎重に扱わないと、1 つのブループリントのローディングが複数ブループリントのローディングを引き起こしてメモリに格納される「カスケード ローディング効果」を招くおそれがあります。
この方法では、共通の関数にアクセスするために各アクタ クラス ブループリントにインターフェースを実装する必要があります。
目標
このクイック スタート ガイドでは、インターフェースを使用して 2 種類のアクタ クラス ブループリントと通信するシンプルなインタラクション システムを作成する方法を学習します。
目的
Interact 関数を使ってインターフェースを作成する。
インタラクティブなランプとドアのアクタを作成し、インターフェースを実装する。
近くにあるオブジェクトの Interact インターフェース関数を呼び出すように BpCommunication Character クラスを変更します。
1 - 必要な設定
メニューの [New Project Categories (新規プロジェクトカテゴリ)] セクションにある [Games (ゲーム)] を選択して [Next (次へ)] をクリックします。
[Third Person (サードパーソン)] テンプレートを選択し、[Next (次へ)] をクリックします。
[C++] プロジェクトを選択して [With Starter Content (スターター コンテンツあり)] オプションを有効化して [Create Project (プロジェクトの作成)] をクリックします。
このセクションの結果
新規のサードパーソン プロジェクトを作成し、インターフェースについて学ぶ準備ができました。
2 - インターフェースを作成する
C++ Class Wizard から、新規 Unreal インターフェース クラスを「InteractInterface」という名前で作成します。
IInteractInterface.h
のクラス デフォルトで、以下のメソッドを実装します。public:
UFUNCTION() virtual void OnInteract() = 0;
コードを コンパイル します。
完成コード
InteractionInterface.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "InteractInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UInteractInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class BPCOMMUNICATION_API IInteractInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
UFUNCTION()
virtual void OnInteract() = 0;
};
このセクションの結果
このセクションでは、Unreal インターフェースを作成して「OnInteract」という関数を追加しました。これで、このインターフェースを実装するアクタ クラス ブループリントに、この関数を追加することができるようになりました。
2 - インタラクティブなシーリング ライト アクタを作成する
C++ Class Wizzard から CeilingLight という名前で新しいアクタ クラスを作成します。
「CeilingLight.h」のクラス デフォルトで、以下のクラス ライブラリを実装します。
#include "InteractInterface.h"
Then, implement the following code.
UCLASS() class BPCOMMUNICATION_API ACeilingLight : public AActor, public IInteractInterface { GENERATED_BODY() public: virtual void OnInteract(); protected: UPROPERTY(EditAnywhere, BlueprintReadWrite) class UPointLightComponent* PointLightComp; UPROPERTY(EditAnywhere, BlueprintReadWrite) UStaticMeshComponent* StaticMeshComp; UPROPERTY(EditAnywhere, BlueprintReadWrite) float Brightness; UPROPERTY(EditAnywhere, BlueprintReadWrite) float SourceRadius; UPROPERTY(EditAnywhere, BlueprintReadWrite) FLinearColor Color; UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bIsLightOn; UFUNCTION() void ToggleLight(); }
「CeilingLight.cpp」ファイルに移動し、次の Include ライブラリを宣言します。
#include "Components/PointLightComponent.h"
Then implement the following code.
ACeilingLight::ACeilingLight() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; RootComponent = CreateDefaultSubobject<URootComponent>(TEXT("RootComponent")); PointLightComp = CreateDefaultSubobject<UPointLightComponent>(TEXT("PointLightComp")); StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComp")); PointLightComp->AttachToComponent(RootComponent,FAttachmentTransformRules::KeepRelativeTransform); StaticMeshComp->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform); PointLightComp->SetWorldLocation(FVector(0, 0, -130)); Brightness = 1700.f; Color = FLinearColor(1.f, 0.77f, 0.46f); SourceRadius = 3.5f; PointLightComp->SetIntensity(Brightness); PointLightComp->SetLightColor(Color); PointLightComp->SetSourceRadius(SourceRadius); } void ACeilingLight::OnInteract() { ToggleLight(); } void ACeilingLight::ToggleLight() { if (bbIsLightOn) { PointLightComp->SetVisibility(false); bbIsLightOn = false; } else { PointLightComp->SetVisibility(true); bbIsLightOn = true; } }
コードを コンパイル します。
「C++ Classes」フォルダで CeilingLight アクタを右クリックし、[C++ Class Actions] ドロップダウン メニューから [Create Blueprint class based on CeilingLight] を選択します。ブループリントに「BP_CeilingLight」と名前を付けます。
BP_CeilingLight クラス デフォルトから [Components] パネルに移動し StaticMeshComp を選択します。
[Details (詳細)] パネルから [Static Mesh (スタティック メッシュ)] カテゴリに移動して Static Mesh 変数の隣にあるドロップダウンの矢印を選択し、「SM_Lamp_Ceiling」を検索、選択します。
完成コード
CeilingLight.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "InteractInterface.h"
#include "CeilingLight.generated.h"
UCLASS()
class BPCOMMUNICATION_API ACeilingLight : public AActor, public IInteractInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ACeilingLight();
virtual void OnInteract();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class UPointLightComponent* PointLightComp;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UStaticMeshComponent* StaticMeshComp;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Brightness;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float SourceRadius;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FLinearColor Color;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bbIsLightOn;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION()
void ToggleLight();
};
CeilingLight.cpp
//Copyright Epic Games, Inc. All Rights Reserved.
#include "CeilingLight.h"
#include "Components/PointLightComponent.h"
// Sets default values
ACeilingLight::ACeilingLight()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
RootComponent = CreateDefaultSubobject<URootComponent>(TEXT("RootComponent"));
PointLightComp = CreateDefaultSubobject<UPointLightComponent>(TEXT("PointLightComp"));
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComp"));
PointLightComp->AttachToComponent(RootComponent,FAttachmentTransformRules::KeepRelativeTransform);
StaticMeshComp->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
PointLightComp->SetWorldLocation(FVector(0, 0, -130));
Brightness = 1700.f;
Color = FLinearColor(1.f, 0.77f, 0.46f);
SourceRadius = 3.5f;
PointLightComp->SetIntensity(Brightness);
PointLightComp->SetLightColor(Color);
PointLightComp->SetSourceRadius(SourceRadius);
}
void ACeilingLight::OnInteract()
{
ToggleLight();
}
// Called when the game starts or when spawned
void ACeilingLight::BeginPlay()
{
Super::BeginPlay();
}
void ACeilingLight::ToggleLight()
{
if (bIsLightOn)
{
PointLightComp->SetVisibility(false);
bIsLightOn = false;
}
else
{
PointLightComp->SetVisibility(true);
bIsLightOn = true;
}
}
// Called every frame
void ACeilingLight::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
このセクションの結果
このセクションでは、Ceiling Light アクタ クラスを作成し、ライトのオンとオフを切り替えるカスタム関数を追加しました。また、ToggleLight イベントの実行をトリガーする Interact インターフェースを実装しました。
4 - インタラクティブなドアを作成する
C++ クラス ウィザード で、新規 Actor クラスを「DoorActor」という名前で作成します。
「DoorActor.h」ファイルに移動し、次のように宣言します。
#include "Components/TimelineComponent.h" #include "InteractInterface.h"
DoorActor クラスの名前空間では、Interact インターフェースから継承する必要があります。
UCLASS() class BPCOMMUNICATION_API ADoorActor : public AActor, public IInteractInterface
次に、以下のクラスを宣言します。
// Variable to hold the Curve asset UPROPERTY(EditAnywhere) UCurveFloat* DoorTimelineFloatCurve; UFUNCTION() virtual void OnInteract(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; //MeshComponents to represent Door assets UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UStaticMeshComponent* DoorFrame; UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UStaticMeshComponent* Door; //TimelineComponent to animate Door meshes UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UTimelineComponent* DoorTimelineComp; //Float Track Signature to handle our update track event FOnTimelineFloat UpdateFunctionFloat; //Function which updates our Door's relative location with the timeline graph UFUNCTION() void UpdateTimelineComp(float Output);
「DoorActor.cpp」内で、次のクラス定義を実装します。
ADoorActor::ADoorActor() { //Create our Default Components DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh")); Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh")); DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp")); //Setup our Attachments DoorFrame->SetupAttachment(RootComponent); Door->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform); Door->SetRelativeLocation(FVector(0, 35, 0)); } void ADoorActor::OnInteract() { DoorTimelineComp->Play(); } // Called when the game starts or when spawned void ADoorActor::BeginPlay() { Super::BeginPlay(); //Binding our float track to our UpdateTimelineComp Function's output UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp); //If we have a float curve, bind its graph to our update function if (DoorTimelineFloatCurve) { DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat); } } void ADoorActor::UpdateTimelineComp(float Output) { // Create and set our Door's new relative location based on the output from our Timeline Curve FRotator DoorNewRotation = FRotator(0.0f, Output, 0.f); Door->SetRelativeRotation(DoorNewRotation); }
コードを コンパイル します。
コンテンツ ブラウザ で [Add/Import] > [Miscellaneous] > [Curve] を選択します。
[CurveFloat] を選択して CurveFloat アセットに DoorCurveFloat と名前を付けます。
DoorCurveFloat アセットをダブルクリックします。2 つのキーを Float カーブに追加し、1 つのキーに時間の値「(0,0)」を、もう 1 つ のキーに時間の値「(4,90)」を割り当てます。
Shift キーを押しながら 2 つのキーを選択して Auto Cubic interpolation に設定し、カーブを保存します。
DoorCurveFloat を保存します。
コンテンツ ブラウザから「C++ Classes」フォルダに移動して DoorActor クラスを右クリックします。[Create Blueprint Class based on Door Actor (Door アクタに基づいてブループリント クラスを作成する)] を選択し、「BP_DoorActor 」という名前を付けます。
BP_DoorActor の [Class Defaults (クラスのデフォルト)] 内で[Components (コンポーネント)] タブを探し、DoorFrame Static Mesh コンポーネント を選択して [Details (詳細)] パネルに移動して [Static Mesh (スタティック メッシュ)]を「SM_DoorFrame」に変更します。
次に、[Components (コンポーネント)] タブから DoorMesh コンポーネントを選択します。[Details (詳細)] パネルに移動してスタティック メッシュを「SM_Door」に変更します。
[Details (詳細)] パネルの [Door Timeline Float Curve (ドア タイムライン フロート カーブ)] から [DoorCurveFloat] を選択します。
ブループリントを コンパイル して 保存 します。
完成コード
DoorActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimelineComponent.h"
#include "InteractInterface.h"
#include "DoorActor.generated.h"
UCLASS()
class BPCOMMUNICATION_API ADoorActor : public AActor, public IInteractInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ADoorActor();
// Variable to hold the Curve asset
UPROPERTY(EditAnywhere)
UCurveFloat* DoorTimelineFloatCurve;
UFUNCTION()
virtual void OnInteract();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
//MeshComponents to represent Door assets
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* DoorFrame;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* Door;
//TimelineComponent to animate Door meshes
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UTimelineComponent* DoorTimelineComp;
//Float Track Signature to handle our update track event
FOnTimelineFloat UpdateFunctionFloat;
//Function which updates our Door's relative location with the timeline graph
UFUNCTION()
void UpdateTimelineComp(float Output);
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
DoorActor.cpp
#include "DoorActor.h"
// Sets default values
ADoorActor::ADoorActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//Create our Default Components
DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh"));
Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp"));
//Setup our Attachments
DoorFrame->SetupAttachment(RootComponent);
Door->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform);
Door->SetRelativeLocation(FVector(0, 35, 0));
}
void ADoorActor::OnInteract()
{
DoorTimelineComp->Play();
}
// Called when the game starts or when spawned
void ADoorActor::BeginPlay()
{
Super::BeginPlay();
//Binding our float track to our UpdateTimelineComp Function's output
UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp);
//If we have a float curve, bind it's graph to our update function
if (DoorTimelineFloatCurve)
{
DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat);
}
}
void ADoorActor::UpdateTimelineComp(float Output)
{
// Create and set our Door's new relative location based on the output from our Timeline Curve
FRotator DoorNewRotation = FRotator(0.0f, Output, 0.f);
Door->SetRelativeRotation(DoorNewRotation);
}
// Called every frame
void ADoorActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
このセクションの結果
このセクションでは、Interact インターフェースの OnInteract メソッドが呼び出されたときに開くインタラクティブな Door アクタを作成しました。
5 - BPCommunicationCharacter クラスを変更する
「BpCommunicationCharacter.h」ファイルを開き、以下のクラス定義を宣言します。
protected: virtual void NotifyActorBeginOverlap(AActor* OtherActor); class USphereComponent* SphereComp;
「BpCommunicationCharacter.cpp」ファイルに移動し、以下のクラス ライブラリを宣言します。
#include "Components/SphereComponent.h" #include "InteractInterface.h"
次に、以下のクラス メソッドを実装します。
ABPCommunicationCharacter::ABPCommunicationCharacter() { SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp")); SphereComp->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform); SphereComp->SetSphereRadius(200); } void ABPCommunicationCharacter::NotifyActorBeginOverlap(AActor* OtherActor) { if (IInteractInterface* ActorCheck = Cast<IInteractInterface>(OtherActor)) { ActorCheck->OnInteract(); } }
コードを コンパイル します。
完成コード
BpCommunicationCharacter.h
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BPCommunicationCharacter.generated.h"
UCLASS(config=Game)
class ABPCommunicationCharacter : public ACharacter
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FollowCamera;
public:
ABPCommunicationCharacter();
/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
float BaseTurnRate;
/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
float BaseLookUpRate;
protected:
virtual void NotifyActorBeginOverlap(AActor* OtherActor);
class USphereComponent* SphereComp;
/** Resets HMD orientation in VR. */
void OnResetVR();
/** Called for forwards/backward input */
void MoveForward(float Value);
/** Called for side to side input */
void MoveRight(float Value);
/**
* Called via input to turn at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void TurnAtRate(float Rate);
/**
* Called via input to turn look up/down at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void LookUpAtRate(float Rate);
/** Handler for when a touch input begins. */
void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);
/** Handler for when a touch input stops. */
void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// End of APawn interface
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};
BpCommunicationCharacter.cpp
// Copyright Epic Games, Inc. All Rights Reserved.
#include "BPCommunicationCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "Components/SphereComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "InteractInterface.h"
#include "GameFramework/SpringArmComponent.h"
//////////////////////////////////////////////////////////////////////////
// ABPCommunicationCharacter
ABPCommunicationCharacter::ABPCommunicationCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
GetCharacterMovement()->JumpZVelocity = 600.f;
GetCharacterMovement()->AirControl = 0.2f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
SphereComp->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform);
SphereComp->SetSphereRadius(200);
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}
//////////////////////////////////////////////////////////////////////////
// Input
void ABPCommunicationCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
// Set up gameplay key bindings
check(PlayerInputComponent);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward", this, &ABPCommunicationCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &ABPCommunicationCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &ABPCommunicationCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &ABPCommunicationCharacter::LookUpAtRate);
// handle touch devices
PlayerInputComponent->BindTouch(IE_Pressed, this, &ABPCommunicationCharacter::TouchStarted);
PlayerInputComponent->BindTouch(IE_Released, this, &ABPCommunicationCharacter::TouchStopped);
// VR headset functionality
PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &ABPCommunicationCharacter::OnResetVR);
}
void ABPCommunicationCharacter::NotifyActorBeginOverlap(AActor* OtherActor)
{
if (IInteractInterface* ActorCheck = Cast<IInteractInterface>(OtherActor))
{
ActorCheck->OnInteract();
}
}
void ABPCommunicationCharacter::OnResetVR()
{
// If BPCommunication is added to a project via 'Add Feature' in the Unreal Editor the dependency on HeadMountedDisplay in BPCommunication.Build.cs is not automatically propagated
// and a linker error will result.
// You will need to either:
// Add "HeadMountedDisplay" to [YourProject].Build.cs PublicDependencyModuleNames in order to build successfully (appropriate if supporting VR).
// or:
// Comment or delete the call to ResetOrientationAndPosition below (appropriate if not supporting VR)
UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}
void ABPCommunicationCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
Jump();
}
void ABPCommunicationCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
StopJumping();
}
void ABPCommunicationCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void ABPCommunicationCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
void ABPCommunicationCharacter::MoveForward(float Value)
{
if ((Controller != nullptr) && (Value != 0.0f))
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
void ABPCommunicationCharacter::MoveRight(float Value)
{
if ( (Controller != nullptr) && (Value != 0.0f) )
{
// find out which way is right
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get right vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement in that direction
AddMovementInput(Direction, Value);
}
}
このセクションの結果
このセクションでは、Sphere コンポーネントを、オーバーラップするアクタの検出を行う ThirdPersonCharacter クラスに追加しました。アクタが球形にオーバーラップすると、キャラクターがキャスティングしてインターフェースとインタラクトし、オーバーラップしているアクタに OnInteract 関数をトリガーします。
6 - インタラクション システムをテストする
BP_DoorActor のインスタンスと BP_CeilingLamp ブループリントをレベル ビューポートにドラッグします。
[Play (プレイ)] を押して、各ブループリントに近づいたときのプレイヤーとのインタラクションを確認します。
このセクションの結果
このセクションでは、インタラクティブなドアとシーリング ランプのアクタ ブループリントでテストし、インタラクション システムが意図したとおりに動作することを確認しました。
このクイック スタート ガイドでは、各アクタ クラス ブループリントに同一のインターフェースを実装し、別々の関数を指定する方法を学習しました。また、インターフェースがさまざまなブループリント クラスの類似の関数を実装するのに適しており、キャスティングを必要としない理由も学びました。
次のステップ
ブループリント インターフェースの使い方が分かったところで、今度は「