アクタにコンポーネントを追加する

アクタにコンポーネントを追加するためのチュートリアルです。

コンテンツ

[コンポーネント](programming-and-scripting\programming-language-implementation\cpp-in-unreal-engine\unreal-engine-cpp-tutorials\Components)
アクタ 内でサブオブジェクトとして使用されるように作られた オブジェクト のタイプです。

コンポーネントは、視覚効果の表示、サウンドの再生、動作ロジックの処理など、さまざまなクラス間において共通の動作を共有する上で役立ちます。

独自のコンポーネントの作成する場合に、Actor コンポーネントScene コンポーネント、および Primitive コンポーネント の 3 つのコンポーネントを使用することができます。

  • Actor コンポーネント (クラス UActorComponent) は、移動、インベントリ、または属性管理、およびその他の非物理的概念などの抽象的な動作に役立ちます。Actor コンポーネントにはトランスフォーム プロパティ (ワールドでの物理的な位置や回転) は含まれません。

  • Scene コンポーネント (クラス USceneComponentUActorComponent の子) はジオメトリ表現を必要としない場所ベースのビヘイビアをサポートします。これには、スプリング アーム、カメラ、物理的な力、およびコンストレイント (物理オブジェクトは除く)、音声が含まれます。

  • Primitive コンポーネント (クラス UPrimitiveComponentUSceneComponent の子) は、ジオメトリ表現を備えた Scene コンポーネントです。これは、通常、ビジュアル要素のレンダリング、または物理オブジェクトとのコリジョンまたはオーバーラップに使用されます。これには、スタティック メッシュまたはスケルタル メッシュ、スプライトまたはビルボード、パーティクル システム、ボックス、カプセル、球体のコリジョン ボリュームが含まれます。

実装方法を選んでください。

Blueprints

C++

プロジェクトの設定

このチュートリアルでは、「HealthComponent」という名前のアクタ コンポーネントのゲームプレイ ロジックを作成するタスクに取り組みます。アクタにこのコンポーネントを接続すると、ダメージによってヘルスを減らす機能がアクタに搭載されます。

今回の例では、サードパーソン テンプレート プロジェクトに含まれているサードパーソン キャラクターを使用します。

  1. まず [New (新規)] > [Games (ゲーム)] > [Third Person (サードパーソン)] > [Blueprint (ブループリント)] プロジェクトの順に選択して、「Health Component」という名前のプロジェクトを作成します。

    クリックしてフルサイズで表示

  2. [Edit (編集)] > [Project Settings (プロジェクト設定)] > [Engine (エンジン)] > [Input (入力)] >[Bindings (バインディング)] > [Action Mappings (アクション マッピング)] の順に移動し、 [Add (追加) (+)] をクリックして「OnDamagePressed」という名前の新しいアクション マッピングを作成し、その値を 1 に設定します。

    クリックしてフルサイズで表示

入力値 には、アクション マッピングが含まれており、プレイヤーのキャラクターに対するダメージの発生など、イベント駆動の動作にボタンまたはキーの押下をマッピングすることができます。

アクタ コンポーネントを作成する:Health Component

このセクションでは、任意のアクタに接続できるアクタ コンポーネントである Health Component を作成します。

以下の手順に従って、プレイヤーのヘルスの値を維持および減少させるロジックの作成を開始します。

  1. コンテンツ ブラウザ[Add/New (新規/追加)] > [Blueprint Class (ブループリント クラス)] の順にクリックします。次に、 [Pick Parent Class (親クラスを選択)] メニューから、 [Actor Component (アクタ コンポーネント)] を選択し、「Bp_HealthComponent」という名前の新しいアクタ コンポーネントを作成します。

    03_BP_CreateBPActorComponentClass.png

  2. [Class Defaults (クラスのデフォルト)] で [My Blueprint (マイ ブループリント)] パネルに移動します。[Variables (変数)] カテゴリの [Add (+) (追加)] ボタンをクリックして、「Health」および「MaxHealth」という名前の 2 つの float 変数を作成します。

    04_BP_AddVariables.png

  3. Max Health 変数を選択し、[Details (詳細)] パネルに移動してデフォルトの Max Health の値を 100.0 に設定します。

    05_BP_MaxHealthDetails.png

イベント ディスパッチャー Handle Damage を作成する

イベント ディスパッチャー を使用すると、アクタでイベント ディスパッチャーにバインドする (イベント ディスパッチャーをリッスンする) メソッドを呼び出すことができます。続いて、イベント ディスパッチャーがイベントを呼び出すと、イベント ディスパッチャーをリッスンしているアクタがそのイベントに応答します。

以下の手順に従って、Handle Take Damage イベント ディスパッチャーの作成を開始します。

  1. [My Blueprint] パネルから Event Dispatchers カテゴリへ移動し [Add (+)] ボタンをクリックして「HandleTakeDamage」という名前の イベント ディスパッチャー を作成します。

    06_BP_AddEventDispatcher.png

  2. [Details] パネルの Inputs カテゴリに移動して [Add (+)] ボタンをクリックし、次の変数を作成します。

    変数名

    説明

    DamageActor

    Bp_HealthComponent

    ダメージを受けるアクタ。

    Health

    Float

    アクタのベース ヘルス。

    Damage

    Float

    適用するベース ダメージ。

    DamageType

    Damage Type

    処理するダメージを説明するクラス。

    InstigatedBy

    Controller

    ダメージの原因となったコントローラー。手りゅう弾を投げたプレイヤーなどがその例です。

    DamageCauser

    Actor

    ダメージの原因を表すアクタ。爆発した手りゅう弾などがその例です。

    07_BP_AddDispatcherInputs.png

  3. [Compile (コンパイル)][Save (保存)] を順にクリックします。

    08_BP_CompileSaveButtons.png

Health コンポーネントのイベント開始スクリプトを作成する

  1. Max Health float のコピーを イベント グラフ にドラッグします。

    Copy Node Graph

    09_BPScript_01_01.png

  2. Health float を イベント グラフ にドラッグし、 [Set Health (Health を設定)] を選択して Set Health ノードを作成します。

    Copy Node Graph

    10_BPScript_01_02.png

  3. Max Health float の 出力 ピンを、 Set Health ノードの Health入力 ピンに接続します。次に、 Event Begin Play ノードの出力実行ピンを Set Health ノードの入力実行ピンに接続します。

    Copy Node Graph

    11_BPScript_01_03.png

  4. Set Health ノードから出力実行ピンを引き出します。[Actions] メニューから [Context Sensitive (状況依存)] ボックスをオフにし、 Bind Event to On Take Any Damage を検索して選択します。

    Copy Node Graph

    12_BPScript_01_04.png

  5. [Bind Event to On Take Any Damage] ノードから Target ピンを引き出し、 [Actions] メニューから Components カテゴリの Get Owner を検索して選択します。

    Copy Node Graph

    13_BPScript_01_05.png

  6. [Bind Event to On Take Any Damage] ノードから Target ピンを引き出し、 [Actions] メニューから Components カテゴリの Get Owner を検索して選択します。

    Copy Node Graph

    14_BPScript_01_06.png

  7. Create Event ノードで、 [Select Function (関数を選択)] ドロップダウン メニューをクリックし、関数 Create matching function を選択します。

    15_BPScript_01_07.png

関数 Take Damage を作成する

Health コンポーネントのイベント開始スクリプトの作成が完了すると、最後に作成した関数が自動的に開かれます。

Player Character がダメージを受けたときにヘルスを減らすロジックを処理するための関数スクリプトを作成する必要があります。

これを行うには、以下の手順に従います。

  1. 作成した関数の名前を「Take Damage」に変更します。

    16_RenameFunction.png

  2. [My Blueprint] パネルで Variables カテゴリに移動し、 Health 変数を イベント グラフ にドラッグします。

    Copy Node Graph

    17_BPScript_02_01.png

  3. Take Damage ノードから Damage 変数を引き出します。次に、 [Actions] メニューで Subtract を検索して選択します。

    Copy Node Graph

    18_BPScript_02_02.png

  4. Health ピンからドラッグし、Subtract ノードの 減算浮動小数 に接続します。

    Copy Node Graph

    19_BPScript_02_03.png

  5. Subtract ノードから結果を引き出し、 [Actions] メニューで Clamp (float) を検索して選択します。

    Copy Node Graph

    20_BPScript_02_04.png

  6. [My Blueprint] パネルで Variables カテゴリに移動し、 Max Health 変数を Clamp (float) ノードの Max float 変数にドラッグします。

    Copy Node Graph

    21_BPScript_02_05.png

  7. Take Damage ノードから実行ピンを引き出し、 Actions メニューから Branch を検索して選択します。

    Copy Node Graph

    22_BPScript_02_06.png

  8. Take Damage ノードから Damage 変数を引き出します。次に、 [Actions] メニューで <= (Less Equal) を検索して選択します。

    Copy Node Graph

    23_BPScript_02_07.png

  9. Less Equal 演算子ノードから boolean リターン ピンを引き出し、 Branch ノードの Condition ピンに接続します。

    Copy Node Graph

    24_BPScript_02_08.png

  10. Branch ノードから False 実行ピンを引き出します。次に、 [Actions] メニューで Set Health を検索して選択します。

    Copy Node Graph

    25_BPScript_02_09.png

  11. Clamp (float) ノードに移動し、 Return Value からドラッグします。それを Set Health ノードの Health ピンに接続します。

    Copy Node Graph

    26_BPScript_02_10.png

  12. Set Health ノードから出力実行ピンを引き出し、 [Actions] メニューで Call Handle Take Damage を検索して選択します。

    Copy Node Graph

    27_BPScript_02_11.png

  13. Set Health ノードで、 Health 出力ピンを Call Handle Take Damage ノードの Health 入力ピンに接続します。

    Copy Node Graph

    28_BPScript_02_12.png

  14. Call Handle Take Damage ノードから Damaged Actor ピンを引き出し、 [Actions] メニューから Get a reference to self を検索して選択します。

    Copy Node Graph

    29_BPScript_02_13.png

  15. Take Damage ノードに移動し、 Damage ピン、 Damage Type ピン、 Instigated By ピン、および Damage Causer ピンを、Call Handle Take Damage ノードのそれぞれのピンに接続します。

    Copy Node Graph

    30_BPScript_02_14.png

  16. [Compile (コンパイル)][Save (保存)] を順にクリックします。

    31_CompileSaveButton.png

    完成したブループリント

Take Damage 関数

Copy Node Graph

32_BPFinalScript1.png

Begin Play

Copy Node Graph

33_BPFinalScript2.png

Third Person Character に Health Component を追加する

Health Component クラスの作成が完了したら、そのアクタ コンポーネントを Third Person Character クラスに追加し、OnDamagePressed アクション マッピングをバインドしてプレイヤーがダメージを受けるようにする必要があります。

  1. Content」 > 「ThirdPersonBp」 > 「Blueprints」フォルダの順に移動し、 ThirdPersonCharacter をダブルクリックしてその [Class Defaults (クラスのデフォルト)] を開きます。

    34_OpenThirdPerCharacter.png

  2. [Components (コンポーネント)] パネルで [Add Component (コンポーネントを追加)] ボタンをクリックし、 Bp Health Component を検索して選択します。

    35_AddBPHealthComponent.png

  3. イベント グラフ を右クリックし、 [Actions] メニューから OnDamagePressed を検索して選択します。

    Copy Node Graph

    36_BPScript_03_01.png

  4. InputAction OnDamagePressed ノードから Pressed 実行ピンを引き出し、 [Actions] メニューから Apply Damage を検索して選択します。

    Copy Node Graph

    37_BPScript_03_02.png

  5. Apply Damage ノードから Damaged Actor ピンを引き出し、 [Actions] メニューから Get a reference to self を検索して選択します。Base Damage 浮動小数値を「20.0」に設定します。

    Copy Node Graph

    38_BPScript_03_03.png

    Apply Damage 関数は、ゲームプレイを目的とする便利な汎用関数を含む Gameplay Statics Library の一部です。

  6. イベント グラフ を右クリックし、 [Actions] メニューから Event Begin Play を検索して選択します。

    Copy Node Graph

    39_BPScript_03_04.png

  7. [Components (コンポーネント)] パネルから、 BP_HealthComponent のコピーを イベント グラフ にドラッグします。

    Copy Node Graph

    40_BPScript_03_05.png

  8. .Bp Health Component ピンを引き出し、 [Actions] メニューで Bind Event to Handle Take Damage を検索して選択します。

    Copy Node Graph

    41_BPScript_03_06.png

  9. Event BeginPlay ノードの出力実行ピンを、Bind Event to Handle Take Damage の入力実行ピンに接続します。

    Copy Node Graph

    42_BPScript_03_07.png

  10. Bind Event to Handle Take Damage から Event ピンを引き出し、 [Actions] メニューから Add Custom Event を検索して選択し、 OnHealthChanged という名前のカスタム イベントを作成します。

    Copy Node Graph

    43_BPScript_03_08.png

  11. OnHealthChanged ノードから Health ピンを引き出し、[Actions] メニューで <= (Less Equal) 演算子を検索して選択します。

    Copy Node Graph

    44_BPScript_03_09.png

  12. OnHealthChanged ノードから Execution ピンを引き出し、[Actions] メニューで Branch** を検索して選択します。

    Copy Node Graph

    45_BPScript_03_10.png

  13. <= (Less Equal) 演算子ノードから boolean リターン ピンを引き出し、 Branch ノードの Condition ピンに接続します。

    Copy Node Graph

    46_BPScript_03_11.png

  14. Branch ノードの True 実行ピンを引き出して、 [Actions] メニューで DestroyActor を検索して選択します。

    Copy Node Graph

    47_BPScript_03_12.png

    プレイヤーのヘルス値がゼロに達すると、プレイヤーはワールドから除去されます。

  15. OnHealthChanged ノードから Execution ピンを引き出し、 [Actions] メニューで Print String を検索して選択します。

    Copy Node Graph

    48_BPScript_03_13.png

    Print String は、プレイヤーのキャラクターがダメージを受けるたびに、ヘルスの現在値を画面に表示するために使用します。

  16. OnHealthChanged ノードの Health ピンを Print StringIn String ピンに接続します。

    49_BPScript_03_14.png

  17. [Compile (コンパイル)][Save (保存)] を順にクリックします。

    50_CompileSaveButton.png

完成したブループリント

Copy Node Graph

51_BPFinalScript3.png

最終結果

Health Component の機能をテストする準備が整いました。

WASD のキーを使用してキャラクターを動かすこともできます。数字キーの 1 を押すと、キャラクターはヘルスが 0 に達するまでダメージを受け、ヘルスが 0 に達するとワールドから消去されます。

ツールバー に移動し、 [Play (プレイ)] をクリックします。

52_PlayButton.png

プロジェクトの設定

このチュートリアルでは、「HealthComponent」という名前のアクタ コンポーネントのゲームプレイ ロジックを作成するタスクに取り組みます。アクタにこのコンポーネントを接続すると、ダメージによってヘルスを減らす機能がアクタに搭載されます。

今回の例では、サードパーソン テンプレート プロジェクトに含まれているサードパーソン キャラクターを使用します。

  1. まず [New (新規)] > [Games (ゲーム)] > [Third Person (サードパーソン)] > [CPP] プロジェクトの順に選択して、「Health Comp」という名前のプロジェクトを作成します。

    クリックしてフルサイズで表示

  2. [Edit (編集)] > [Project Settings (プロジェクト設定)] > [Engine (エンジン)] > [Input (入力)] >[Bindings (バインディング)] > [Action Mappings (アクション マッピング)] の順に移動し、 [Add (追加) (+)] をクリックして「OnDamagePressed」という名前の新しいアクション マッピングを作成し、その値を 1 に設定します。

    クリックしてフルサイズで表示

    入力値 には、アクション マッピングが含まれており、プレイヤーのキャラクターに対するダメージの発生など、イベント駆動の動作にボタンまたはキーの押下をマッピングすることができます。

アクタ コンポーネント Health Component を作成する

デリゲート は、汎用的かつ型安全な方法で C++ オブジェクト上でメンバ関数の呼び出しを可能にします。アクタはデリゲートにバインドされると、デリゲートのメンバー関数イベントに応答するようになります。

以下の手順に従って、On Health Changed デリゲートの作成を開始します。

  1. [Tools] > [New C++ Class] ボタンをクリックし、[Choose a Parent Class (親クラスを選択)] メニューから [Actor Component (アクタ コンポーネント)] を選択して [Next] をクリックします。

    クリックしてフルサイズで表示

  2. Actor Component クラスに「HealthComponent」と名前をつけて [Create] をクリックします。

    クリックしてフルサイズで表示

  3. HealthComponent.h」ファイルで、次のデリゲートを宣言します。

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComponent, float, Health, float, DamageAmount, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
  4. [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);
  5. HealthComponent.cpp」ファイルに移動し、 HealthComponent コンストラクタ 内で次のクラスの変数を初期化します。

    UHealthComponent::UHealthComponent()
    {
        MaxHealth = 100.0f;
    }
  6. Implement the following definition for the BeginPlay class method:

    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->OnTakeAnyDamage.AddDynamic(this,&UHealthComponent::HandleTakeDamage);
        }
        //Set Health Equal to Max Health.           
        Health = MaxHealth;
    }
  7. Declare the following code for the HandleTakeDamage method:

    void UHealthComponent::HandleTakeDamage(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); 
    }
  8. コードを コンパイル します。

完成コード

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();

    protected:

        // Called when the game starts
        virtual void BeginPlay() override;

    public: 

        // Called every frame
        virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

    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);
    };

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)
        {
            //The Owner Object is now bound to respond to the OnTakeAnyDamage Function.        
            MyOwner->OnTakeAnyDamage.AddDynamic(this,&UHealthComponent::HandleTakeDamage);
        }
        //Set Health Equal to Max Health.
        Health = MaxHealth;
    }

    // Called every frame
    void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
        Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    }

    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);
    }

HealthComp Character に Health Component を追加する

Health Component クラスの作成が完了したら、そのアクタ コンポーネントを Health Comp Character クラスに追加し、OnDamagePressed アクション マッピングをバインドしてプレイヤーがダメージを受けるようにする必要があります。

  1. コンテンツ ブラウザ から、「C++ Classes」 > 「HealthComp」に移動し、 HealthComp Character をダブルクリックして「HealthCompCharacter.h」ファイルを開きます。

  2. [Class Defaults] で、次のコードを宣言します。

    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;
  3. HealthCompCharacter.cpp」ファイルへ移動し、以下のライブラリをインクルードします。

    #include "HealthComponent.h"
    #include "Kismet/GameplayStatics.h"

    Gameplay 統計ライブラリには、多数のユーティリティ ヘルパー関数が用意されており、ゲームプレイを目的とするニーズに対応できます。この例では、 ApplyDamage メソッドを利用します。

  4. AHealthCompCharacter コンストラクタ で、次のようにして Health Component クラスを初期化します。

    AHealthCompCharacter::AHealthCompCharacter()
    {
        HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"));
    }
  5. AHealthCompChatacter::BeginPlay メソッドに移動し、次のコードを追加します。

    void AHealthCompCharacter::BeginPlay()
    {
        Super::BeginPlay();
        HealthComponent->OnHealthChanged.AddDynamic(this, &AHealthCompCharacter::OnHealthChanged);
    }
  6. 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 は、さまざまな 詳細タイプ を含むマクロで、出力ログでデータを視覚化するコマンドとともに使用します。

  7. 次のコードを追加し、 AHealthCompCharacter::DamagePlayerCharacter メソッドを実装します。

    void AHealthCompCharacter::DamagePlayerCharacter()
    {
        UGameplayStatics::ApplyDamage(this, 20.0f, GetInstigatorController(),this,GenericDamageType);
    }
  8. AHealthCompCharacter::SetupPlayerInputComponent メソッドに移動し、 OnDamagePressed バインディングを DamagePlayerCharacter 関数に割り当てます。

    void AHealthCompCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
    {
        //Bind OnDamagePressed Action Event
        PlayerInputComponent->BindAction("OnDamagePressed", IE_Pressed, this, &AHealthCompCharacter::DamagePlayerCharacter);
    }
  9. コードを コンパイル します。

完成コード

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=Input)
        float TurnRateGamepad;

    protected:

        /** 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; }

    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;
    };

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
        TurnRateGamepad = 50.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, 500.0f, 0.0f); // ...at this rotation rate

        // Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
        // instead of recompiling to adjust them
        GetCharacterMovement()->JumpZVelocity = 700.f;
        GetCharacterMovement()->AirControl = 0.35f;
        GetCharacterMovement()->MaxWalkSpeed = 500.f;
        GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
        GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;

        // Create a camera boom (pulls in towards the player if there is a collision)
        CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
        CameraBoom->SetupAttachment(RootComponent);
        CameraBoom->TargetArmLength = 400.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("Move Forward / Backward", this, &AHealthCompCharacter::MoveForward);
        PlayerInputComponent->BindAxis("Move Right / Left", 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 Right / Left Mouse", this, &APawn::AddControllerYawInput);
        PlayerInputComponent->BindAxis("Turn Right / Left Gamepad", this, &AHealthCompCharacter::TurnAtRate);
        PlayerInputComponent->BindAxis("Look Up / Down Mouse", this, &APawn::AddControllerPitchInput);
        PlayerInputComponent->BindAxis("Look Up / Down Gamepad", this, &AHealthCompCharacter::LookUpAtRate);

        // handle touch devices
        PlayerInputComponent->BindTouch(IE_Pressed, this, &AHealthCompCharacter::TouchStarted);
        PlayerInputComponent->BindTouch(IE_Released, this, &AHealthCompCharacter::TouchStopped);

        //Bind OnDamagePressed Action Event
        PlayerInputComponent->BindAction("OnDamagePressed", IE_Pressed, this, &AHealthCompCharacter::DamagePlayerCharacter);
    }

    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 * TurnRateGamepad * GetWorld()->GetDeltaSeconds());
    }

    void AHealthCompCharacter::LookUpAtRate(float Rate)
    {
        // calculate delta for this frame from the rate information
        AddControllerPitchInput(Rate * TurnRateGamepad * 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);
        }
    }

    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);
    }

最終結果

Health Component の機能をテストする準備が整いました。

WASD のキーを使用してキャラクターを動かすこともできます。数字キーの 1 を押すと、キャラクターはヘルスが 0 に達するまでダメージを受け、ヘルスが 0 に達するとワールドから消去されます。

ツールバー に移動し、 [Play (プレイ)] を押します。

05_PlayButton.png

追加リソース

ブループリントにコンポーネントを追加することに関する考え方を、以下のリンクで参照することができます。

Unreal Engine のドキュメントを改善するために協力をお願いします!どのような改善を望んでいるかご意見をお聞かせください。
調査に参加する
キャンセル