Choose your operating system:
Windows
macOS
Linux
コンポーネント
は
アクタ
内でサブオブジェクトとして使用されるように作られた
オブジェクト
のタイプです。
コンポーネントは、視覚効果の表示、サウンドの再生、動作ロジックの処理など、さまざまなクラス間において共通の動作を共有する上で役立ちます。
独自のコンポーネントの作成する場合に、 Actor コンポーネント 、 Scene コンポーネント 、および Primitive コンポーネント の 3 つのコンポーネントを使用することができます。
-
Actor コンポーネント (クラス UActorComponent) は、移動、インベントリ、または属性管理、およびその他の非物理的概念などの抽象的な動作に役立ちます。Actor コンポーネントにはトランスフォーム プロパティ (ワールドでの物理的な位置や回転) は含まれません。
-
Scene コンポーネント (クラス USceneComponent、UActorComponent の子) はジオメトリ表現を必要としない場所ベースのビヘイビアをサポートします。これには、スプリング アーム、カメラ、物理的な力、およびコンストレイント (物理オブジェクトは除く)、音声が含まれます。
-
Primitive コンポーネント (クラス UPrimitiveComponent、USceneComponent の子) は、ジオメトリ表現を備えた Scene コンポーネントです。これは、通常、ビジュアル要素のレンダリング、または物理オブジェクトとのコリジョンまたはオーバーラップに使用されます。これには、スタティック メッシュまたはスケルタル メッシュ、スプライトまたはビルボード、パーティクル システム、ボックス、カプセル、球体のコリジョン ボリュームが含まれます。
実装方法を選んでください。
Blueprints
C++
プロジェクトの設定
このチュートリアルでは、「HealthComponent」という名前のアクタ コンポーネントのゲームプレイ ロジックを作成するタスクに取り組みます。アクタにこのコンポーネントを接続すると、ダメージによってヘルスを減らす機能がアクタに搭載されます。
今回の例では、 Third Person Template プロジェクトに含まれている Third Person Character を使用します。
-
まず [New (新規)] > [Games (ゲーム)] > [Third Person (サードパーソン)] > [Blueprint (ブループリント)] プロジェクトの順に選択して、「 Health Component 」という名前のプロジェクトを作成します。
-
[Edit (編集)] > [Project Settings (プロジェクト設定)] > [Engine (エンジン)] > [Input (入力)] > [Bindings (バインディング)] > [Action Mappings (アクション マッピング)] の順に移動し、 [Add (追加) (+)] をクリックして「 OnDamagePressed 」という名前の新しいアクション マッピングを作成し、その値を 1 に設定します。
入力値 には、アクション マッピングが含まれており、プレイヤーのキャラクターに対するダメージの発生など、イベント駆動の動作にボタンまたはキーの押下をマッピングすることができます。
アクタ コンポーネント:Health Component を作成する
このセクションでは、任意のアクタに接続できるアクタ コンポーネントである Health Component を作成します。
以下の手順に従って、プレイヤーのヘルスの値を維持および減少させるロジックの作成を開始します。
-
コンテンツ ブラウザ で [Add/New (新規/追加)] > [Blueprint Class (ブループリント クラス)] の順にクリックします。次に、 [Pick a Native Class (ネイティブ クラスを選択)] メニューから、 [Actor Component (アクタ コンポーネント)] を選択し、「 Bp_HealthComponent 」という名前の新しいアクタ コンポーネントを作成します。
-
[Class Defaults (クラスのデフォルト)] で [My Blueprint (マイ ブループリント)] パネルに移動します。 [Variables (変数)] カテゴリの [Add (追加) (+)] ボタンをクリックして、「 Health 」および「 MaxHealth 」という名前の 2 つの float 変数を作成します。
-
Max Health 変数を選択し、[Details (詳細)] パネルに移動してデフォルトの Max Health の値を 100.0 に設定します。
-
Max Health float のコピーを イベント グラフ にドラッグします。
-
Health float を イベント グラフ にドラッグし、 [Set Health (Health を設定)] を選択して Set Health ノードを作成します。
-
Max Health float の 出力 ピンを、 Set Health ノードの Health の 入力 ピンに接続します。次に、 Event Begin Play ノードの出力実行ピンを Set Health ノードの入力実行ピンに接続します。
-
[Compile (コンパイル)] と [Save (保存)] を順にクリックします。
イベント ディスパッチャーを作成する:ダメージを処理するダメージを処理する
イベント ディスパッチャー
を使用すると、アクタでイベント ディスパッチャーにバインドする (イベント ディスパッチャーをリッスンする) メソッドを呼び出すことができます。続いて、イベント ディスパッチャーがイベントを呼び出すと、イベント ディスパッチャーをリッスンしているアクタがそのイベントに応答します。
以下の手順に従って、Handle Take Damage イベント ディスパッチャーの作成を開始します。
-
[My Blueprint (マイ ブループリント)] パネルから Event Dispatchers カテゴリへ移動し [Add (+)] ボタンをクリックして HandleTakeDamage** という名前のイベント ディスパッチャー を作成します。
-
[Details] パネルの Inputs カテゴリに移動して [Add ( +)] ボタンをクリックし、次の変数を作成します。
変数名 |
型 |
説明 |
---|---|---|
DamageActor |
Bp_HealthComponent |
ダメージを受けるアクタ。 |
Health |
Float |
アクタのベース ヘルス。 |
Damage |
Float |
適用するベース ダメージ。 |
DamageType |
Damage Type |
処理するダメージを説明するクラス。 |
InstigatedBy |
Controller |
ダメージの原因となったコントローラー。手りゅう弾を投げたプレイヤーなどがその例です。 |
DamageCauser |
Actor |
ダメージの原因を表すアクタ。爆発した手りゅう弾などがその例です。 |
-
[Compile (コンパイル)] と [Save (保存)] を順にクリックします。
関数を作成する:ダメージを受ける
Player Character がダメージを受けたときにヘルスを減らすロジックを処理するための関数を作成する必要があります。
これを行うには、以下の手順に従います。
-
[My Blueprint] ウィンドウから Functions カテゴリへ移動し [Add (+)] ボタンをクリックして TakeDamage という名前の新規関数を作成します。
-
[Details] パネルに移動します。 Inputs カテゴリに移動して [Add ( +)] ボタンをクリックし、次の入力を作成します。
変数名 |
型 |
---|---|
DamageActor |
Actor |
Damage |
Float |
DamageType |
Damage Type |
InstigatedBy |
Controller |
DamageCauser |
Actor |
-
[My Blueprint] パネルで Variables カテゴリに移動し、 Health 変数を イベント グラフ にドラッグします。
-
Take Damage ノードから Damage 変数を引き出します。次に、 [Actions (アクション)] メニューで float - float を検索して選択します。
-
Health ピンからドラッグし、 float - float ノードの B 減算 float に接続します。
-
float - float ノードから結果を引き出し、 [Actions] メニューで Clamp (float) を検索して選択します。
-
[My Blueprint] パネルで Variables カテゴリに移動し、 Max Health 変数を Clamp(float) ノードの Max float 変数 の ピン**** にドラッグします。
-
Take Damage ノードから実行ピンを引き出し、 Actions メニューから Branch ( if ) を検索して選択します。
-
Take Damage ノードから Damage 変数を引き出します。次に、 [Actions] メニューで <= ( Less Than Equal to ) 演算子を検索して選択します。
-
Less than equal to 演算子ノードから boolean リターン ピンを引き出し、 Branch ノードの Condition ピンに接続します。
-
Branch ノードから False 実行ピンを引き出します。次に、 [Actions] メニューで Set Health を検索して選択します。
-
Clamp (float) ノードに移動し、 Return Value からドラッグします。それを Set Health ノードの Health ピンに接続します。
-
Set Health ノードから出力実行ピンを引き出し、 [Actions] メニューで Call Handle Take Damage を検索して選択します。
-
Set Health ノードで、 Health 出力ピンを Call Handle Take Damage ノードの Health 入力ピンに接続します。
-
Take Damage ノードに移動し、 Damage ピン、 Damage Type ピン、 Instigated By ピン、および Damage Causer ピンを、 Call Handle Take Damage ノードのそれぞれのピンに接続します。
-
[Compile (コンパイル)] と [Save (保存)] を順にクリックします。
-
イベント グラフ > Begin Play ノードに移動し、 Set Max Health ノードから出力実行ピンを引き出します。 [Actions] メニューから [Context Sensitive (状況依存)] ボックスをオフにし、 Bind Event to On Take Any Damage を検索して選択します。
-
[Bind Event to On Take Any Damage] ノードから Target ピンを引き出し、 [Actions] メニューから Components カテゴリの Get Owner を検索して選択します。
-
[Bind Event to On Take Any Damage] ノードから Target ピンを引き出し、 [Actions] メニューから Components カテゴリの Get Owner を検索して選択します。
-
Create Event ノードで、 [Select Function (関数を選択)] ドロップダウン メニューをクリックし、関数 TakeDamage を選択します。
-
[Compile (コンパイル)] と [Save (保存)] を順にクリックします。
完成したブループリント
Take Damage 関数
Begin Play
Third Person Character に Health Component を追加する
Health Component クラスの作成が完了したら、そのアクタ コンポーネントを Third Person Character クラスに追加し、OnDamagePressed アクション マッピングをバインドしてプレイヤーがダメージを受けるようにする必要があります。
-
「 Content 」 > 「 ThirdPersonBp 」 > 「 Blueprints 」フォルダの順に移動し、 ThirdPersonCharacter をダブルクリックしてその [Class Defaults (クラスのデフォルト)] を開きます。
-
[Components (コンポーネント)] パネルで [Add Component (コンポーネントを追加)] ボタンをクリックし、 Bp Health Component を検索して選択します。
-
イベント グラフ を右クリックし、 [Actions] メニューから OnDamagePressed を検索して選択します。
-
InputAction OnDamagePressed ノードから Pressed 実行ピンを引き出し、 [Actions] メニューから Apply Damage を検索して選択します。
-
Apply Damage ノードから Damaged Actor ピンを引き出し、 [Actions] メニューから Get a reference to self を検索して選択します。 Base Damage 浮動小数値を「 20.0 」に設定します。
Apply Damage 関数は、ゲームプレイを目的とする便利な汎用関数を含む Gameplay Statics Library の一部です。
-
イベント グラフ を右クリックし、 [Actions] メニューから Event Begin Play を検索して選択します。
-
[Components (コンポーネント)] パネルから、 BP_HealthComponent のコピーを イベント グラフ にドラッグします。
-
. Bp Health Component ピンを引き出し、 [Actions] メニューで Bind Event to Handle Take Damage を検索して選択します。
-
Bind Event to Handle Take Damage から Event ピンを引き出し、 [Actions] メニューから Create Event を検索して選択し、 OnHealthChanged という名前のカスタム イベントを作成します。
-
OnHealthChanged ノードから Health ピンを引き出し、 [Actions] メニューで <= ( Less than equal to**) 演算子を検索して選択します。
-
OnHealthChanged ノードから Execution ピンを引き出し、 [Actions] メニューで Branch** を検索して選択します。
-
<= ( Less than equal to) 演算子ノードから boolean リターン ピンを引き出し、 Branch ノードの Condition ピンに接続します。
-
Branch ノードの True 実行ピンを引き出して、 [Actions] メニューで DestroyActor を検索して選択します。
注: プレイヤーのヘルス値がゼロに達すると、プレイヤーはワールドから除去されます。
-
OnHealthChanged ノードから Execution ピンを引き出し、 [Actions] メニューで Print String を検索して選択します。
Print String は、プレイヤーのキャラクターがダメージを受けるたびに、ヘルスの現在値を画面に表示するために使用します。
-
[Compile (コンパイル)] と [Save (保存)] を順にクリックします。
完成したブループリント
最終結果
Health Component の機能をテストする準備が整いました。
WASD のキーを使用してキャラクターを動かすこともできます。数字キーの 1 を押すと、キャラクターはヘルスが 0 に達するまでダメージを受け、ヘルスが 0 に達するとワールドから消去されます。
-
ツールバー に移動し、 [Play (プレイ)] をクリックします。
プロジェクトの設定
このチュートリアルでは、「HealthComponent」という名前のアクタ コンポーネントのゲームプレイ ロジックを作成するタスクに取り組みます。アクタにこのコンポーネントを接続すると、ダメージによってヘルスを減らす機能がアクタに搭載されます。
今回の例では、 Third Person Template プロジェクトに含まれている Third Person Character を使用します。
-
まず [New (新規)] > [Games (ゲーム)] > [Third Person (サードパーソン)] > [CPP] プロジェクトの順に選択して、「 Health Comp 」という名前のプロジェクトを作成します。
-
[Edit (編集)] > [Project Settings (プロジェクト設定)] > [Engine (エンジン)] > [Input (入力)] > [Bindings (バインディング)] > [Action Mappings (アクション マッピング)] の順に移動し、 [Add (追加) (+)] をクリックして「 OnDamagePressed 」という名前の新しいアクション マッピングを作成し、その値を 1 に設定します。
注: 入力値 には、アクション マッピングが含まれており、プレイヤーのキャラクターに対するダメージの発生など、イベント駆動の動作にボタンまたはキーの押下をマッピングすることができます。
アクタ コンポーネント:Health Component を作成する
デリゲート
は、汎用的かつ型安全な方法で C++ オブジェクト上でメンバ関数の呼び出しを可能にします。アクタはデリゲートにバインドされると、デリゲートのメンバー関数イベントに応答するようになります。
以下の手順に従って、On Health Changed デリゲートの作成を開始します。
-
[Add/Import (追加/インポート)] ボタンをクリックし、 新規 C++ クラス を作成し、 [Choose a Parent Class (親クラスを選択)] メニューから [Actor Component (アクタ コンポーネント)] を選択して「 HealthComponent 」という名前の新しいアクタ コンポーネントを作成します。
-
HealthComponent.h ファイルで、次のデリゲートを宣言します。
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComp, float, Health, float, DamageAmount, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
-
[Class Defaults (クラスのデフォルト)] へ移動して以下のメソッドと変数を宣言します。
public: UPROPERTY(BlueprintAssignable, Category = "Events") FOnHealthChangedSignature OnHealthChanged; protected: UPROPERTY(EditDefaultsOnly,BlueprintReadWrite) float Health; UPROPERTY(EditDefaultsOnly,BlueprintReadWrite) float MaxHealth; UFUNCTION(BlueprintCallable) void HandleTakeDamage(AActor* DamageActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
-
HealthComponent.cpp ファイルに移動し、 HealthComponent コンストラクタ 内で次のクラスの変数を初期化します。
UHealthComponent::UHealthComponent() { MaxHealth = 100.0f; }
-
BeginPlay クラス メソッドの次の定義を実装します。
void UHealthComponent::BeginPlay { Super::BeginPlay(); //Get the Owner of this Actor Component. AActor* MyOwner = GetOwner(); if (MyOwner) { //The Owner Object is now bound to respond to the OnTakeAnyDamage Function. MyOwner->OnTakeDamage.AddDynamic(this,&UHealthComponent::HandleTakeDamage); } //Set Health Equal to Max Health. Health = MaxHealth; }
-
次の HandleTakeAnyDamage メソッドのコードを宣言します。
void UHealthComponent::HandleTakeAnyDamage(AActor* DamageActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser) { if (Damage <= 0.0f) { return; } Health = FMath::Clamp(Health - Damage, 0.0f, MaxHealth); OnHealthChanged.Broadcast(this, Health, Damage, DamageType, InstigatedBy, DamageCauser); }
-
コードを コンパイル します。
完成コード
Health Component.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComponent, float, Health, float, DamageAmount, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class HEALTHCOMP_API UHealthComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UHealthComponent();
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnHealthChangedSignature OnHealthChanged;
protected:
// Called when the game starts
UFUNCTION(BlueprintCallable)
void HandleTakeDamage(AActor* DamageActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
virtual void BeginPlay() override;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float Health;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float MaxHealth;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};
Health Component.cpp
#include "HealthComponent.h"
// Sets default values for this component's properties
UHealthComponent::UHealthComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
MaxHealth = 100.0f;
}
// Called when the game starts
void UHealthComponent::BeginPlay()
{
Super::BeginPlay();
//Get the Owner of this Actor Component.
AActor* MyOwner = GetOwner();
if(MyOwner)
{
MyOwner->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::HandleTakeDamage);
//The Owner Object is now bound to respond to the OnTakeAnyDamage Function.
MyOwner->OnTakeAnyDamage.AddDynamic(this,&UHealthComponent::HandleTakeAnyDamage);
}
//Set Health Equal to Max Health.
Health = MaxHealth;
}
void UHealthComponent::HandleTakeDamage(AActor* DamageActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (Damage <= 0.0f)
{
//Damage amount was 0 or less.
return;
}
Health = FMath::Clamp(Health - Damage, 0.0f, MaxHealth); OnHealthChanged.Broadcast(this, Health, Damage, DamageType, InstigatedBy, DamageCauser);
}
// Called every frame
void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
HealthComp Character に Health Component を追加する
Health Component クラスの作成が完了したら、そのアクタ コンポーネントを Health Comp Character クラスに追加し、OnDamagePressed アクション マッピングをバインドしてプレイヤーがダメージを受けるようにする必要があります。
-
コンテンツ ブラウザ から、「 C++ Classes 」 > 「 HealthComp 」に移動し、 HealthComp Character をダブルクリックして HealthCompCharacter.h ファイルを開きます。
-
[Class Defaults] で、次のコードを宣言します。
protected: UPROPERTY(EditDefaultsOnly,BlueprintReadWrite) class UHealthComponent* HealthComponent; UFUNCTION() OnHealthChanged(UHealthComponent* HealthComp, float Health, float DamageAmount, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser); //Function which will call damage to our Player UFUNCTION() void DamagePlayerCharacter();
-
HealthCompCharacter.cpp ファイルへ移動し、以下のライブラリをインクルードします。
#include "HealthComponent.h" #include "Kismet/GameplayStatics.h"
Gameplay 統計ライブラリには、多数のユーティリティ ヘルパー関数が用意されており、ゲームプレイを目的とするニーズに対応できます。この例では、 :UGameplayStatics::ApplyDamageApplyDamage メソッドを利用します。
-
AHealthCompCharacter コンストラクタ で、次のようにして Health Component クラスを初期化します。
AHealthCompCharacter::AHealthCompCharacter() { HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"); }
-
AHealthCompChatacter::BeginPlay メソッドに移動し、次のコードを追加します。
void AHealthCompCharacter::BeginPlay() { Super::BeginPlay(); HealthComponent->OnHealthChanged.AddDynamic(this, &AHealthCompCharacter::OnHealthChanged); }
-
AHealthCompCharacter::OnHealthChanged メソッドで次のロジックを実装し、ヘルスが 0 になった場合に Player Character を消去します。
void AHealthCompCharacter::OnHealthChanged(UHealthComponent* HealthComp, float Health, float DamageAmount, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser) { if (Health <= 0.0f) { Destroy(); } UE_LOG(LogTemp, Warning, TEXT("The Player's Current Health is: %f"), Health); }
UE_LOG は、さまざまな
-
次のコードを追加し、 AHealthCompCharacter::DamagePlayerCharacter メソッドを実装します。
void AHealthCompCharacter::DamagePlayerCharacter() { UGameplayStatics::ApplyDamage(this, 20.0f, GetInstigatorController(),this,GenericDamageType); }
-
AHealthCompCharacter::SetupPlayerInputComponent メソッドに移動し、 OnDamagePressed バインディングを DamagePlayerCharacter 関数に割り当てます。
void AHealthCompCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) { //Bind OnDamagePressed Action Event PlayerInputComponent->BindAction("OnDamagePressed", IE_Pressed, this, &AHealthCompCharacter::DamagePlayerCharacter); }
-
コードを コンパイル します。
完成コード
HealthCompCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "HealthCompCharacter.generated.h"
UCLASS(config=Game)
class AHealthCompCharacter : 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:
AHealthCompCharacter();
/** 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 BeginPlay() override;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
class UHealthComponent* HealthComponent;
UFUNCTION()
void OnHealthChanged(UHealthComponent* HealthComp, float Health, float DamageAmount, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
//Function which will call damage to our Player
UFUNCTION()
void DamagePlayerCharacter();
//Container for a Damage Type to inflict on the Player
UPROPERTY(EditDefaultsOnly,BlueprintReadOnly, Category = "Weapon")
TSubclassOf<UDamageType> GenericDamageType;
/** 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; }
};
HealthCompCharacter.cpp
#include "HealthCompCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "HealthComponent.h"
#include "Kismet/GameplayStatics.h"
//////////////////////////////////////////////////////////////////////////
// AHealthCompCharacter
AHealthCompCharacter::AHealthCompCharacter()
{
// 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
// 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++)
HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"));
}
//////////////////////////////////////////////////////////////////////////
// Input
void AHealthCompCharacter::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, &AHealthCompCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AHealthCompCharacter::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, &AHealthCompCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &AHealthCompCharacter::LookUpAtRate);
// handle touch devices
PlayerInputComponent->BindTouch(IE_Pressed, this, &AHealthCompCharacter::TouchStarted);
PlayerInputComponent->BindTouch(IE_Released, this, &AHealthCompCharacter::TouchStopped);
// VR headset functionality
PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AHealthCompCharacter::OnResetVR);
//Bind OnDamagePressed Action Event
PlayerInputComponent->BindAction("OnDamagePressed", IE_Pressed, this, &AHealthCompCharacter::DamagePlayerCharacter);
}
void AHealthCompCharacter::BeginPlay()
{
Super::BeginPlay();
HealthComponent->OnHealthChanged.AddDynamic(this, &AHealthCompCharacter::OnHealthChanged);
}
void AHealthCompCharacter::OnHealthChanged(UHealthComponent* HealthComp, float Health, float DamageAmount, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (Health <= 0.0f)
{
Destroy();
}
UE_LOG(LogTemp, Warning, TEXT("The Player's Current Health is: %f"), Health);
}
void AHealthCompCharacter::DamagePlayerCharacter()
{
UGameplayStatics::ApplyDamage(this, 20.0f, GetInstigatorController(),this,GenericDamageType);
}
void AHealthCompCharacter::OnResetVR()
{
// If HealthComp is added to a project via 'Add Feature' in the Unreal Editor the dependency on HeadMountedDisplay in HealthComp.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 AHealthCompCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
Jump();
}
void AHealthCompCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
StopJumping();
}
void AHealthCompCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AHealthCompCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
void AHealthCompCharacter::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 AHealthCompCharacter::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);
}
}
最終結果
Health Component の機能をテストする準備が整いました。
WASD のキーを使用してキャラクターを動かすこともできます。数字キーの 1 を押すと、キャラクターはヘルスが 0 に達するまでダメージを受け、ヘルスが 0 に達するとワールドから消去されます。
-
ツールバー に移動し、 [Play (プレイ)] を押します。