ue4

Playerの作成


ダメージとダメージ処理

ダメージ処理の作成です。 以下の手順で作成します。

ダメージアニメションの作成

アニメーションはアニメーションモンタージュで作成します。
このアニメーションは連続再生が可能です。
アニメーション→モンタージュを選び、対応するスケルトンを選びます。

<モンタージュを選択>
vatico

<スケルトンを選択>
vatico

名前はMAnm_Damageにしました。ダブルクリックで編集を開始します。

再生アニメーションの選択と調整

再生したいアニメーションを選択してドロップします。 vatico

詳細パネルから開始時間と終了時間を設定します。
vatico

次に、アニメーション通知からAnm State NotifyでStartPackとEndを設定します。
<通知の設定>
vatico

<通知内容の設定:画像ではendを設定> vatico
アニメーションの作成はひとまず終了です。

アニメーションブループリントの修正

アニメーションモンタージュの再生を行うためにアニメーションブループリントにslotノードを追加します。
vatico

AnmStateComponent.hの修正

AnmStateComponent.hにアニメーションモンタージュの読み込みと再生処理を追加します。

追加部分

protected:
    UPROPERTY()
    UAnimInstance* AnimInst; //!< アニメーションインスタンス

    UPROPERTY()
    TMap<FString, UAnimMontage*> MontagesAnims; //!< アニメーションモンタージュのリスト

    //-------------------------------------------
    /// アニメーションインスタンスの取得
    //-------------------------------------------
    void InitAnimInst() {
        if (AnimInst) return;

        AActor* ac = GetOwner();
        if (!ac) return;

        auto Mesh = ac->FindComponentByClass<USkeletalMeshComponent>();
        if (!Mesh) return;

        AnimInst = Mesh->GetAnimInstance();
    }
public:
    //-------------------------------------------
    /// アニメーションインスタンスを取得する
    //-------------------------------------------
    UAnimInstance* GetAnmInst(void) {
        return AnimInst;
    }

    //-------------------------------------------
    /// アニメーションモンタージュの登録
    //-------------------------------------------
    void EntryMontageAnim(FString AnimName, FString AssetPath) {
        UAnimMontage** anm = MontagesAnims.Find(AnimName);
        if (anm == nullptr) {
            UAnimMontage* Obj = LoadObject<UAnimMontage>(NULL, *AssetPath, NULL, LOAD_None, NULL);
            if (Obj) {
                MontagesAnims.Add(AnimName, Obj);
            }
        }
    }

    //-------------------------------------------
    /// アニメーションモンタージュの取得
    //-------------------------------------------
    UAnimMontage* GetMontagesAnim(FString AnimName) {
        return MontagesAnims[AnimName];
    }

    //-------------------------------------------
    /// アニメーションモンタージュの再生
    //-------------------------------------------
    float PlayMontage(FString AnimName, 
                    float InPlayRate = 1.f, 
                    EMontagePlayReturnType ReturnValueType = EMontagePlayReturnType::MontageLength, 
                    float InTimeToStartMontageAt = 0.f, 
                    bool bStopAllMontages = true
    ) {
        return AnimInst->Montage_Play(GetMontagesAnim(AnimName), 
                                        InPlayRate,
                                        ReturnValueType,
                                        InTimeToStartMontageAt,
                                        bStopAllMontages);
    }

EntryMontageAnim()を使ってアニメーションを読み込みます。
UAnimInstanceが必要になるのでOwnerのMeshから取得します。
再生はPlayMontage()で行います。

全文

/**
 * @file AnmStateComponent.h
 * @brief  アニメーション状態
 * @author     inuvatico
 * @date        2020/11/08
 * @version     1.0
 * @copyright   2018 inuvatico
 * Released under the MIT license.
 *   see https://opensource.org/licenses/MIT
 * @par (new/Add/Change : 2020/10/17)
 *
*/
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "AnmStateComponent.generated.h"

//---------------------------------------------------------------------
// アニメーションモンタージュインデックス
//---------------------------------------------------------------------
UENUM(BlueprintType)
enum class UAnmMtIndex : uint8 {
    AMTID_NONE          UMETA(DisplayName = "None"),
    AMTID_DAMAGE        UMETA(DisplayName = "Damage"),
};

//---------------------------------------------------------------------
// アニメーションインデックス
//---------------------------------------------------------------------
UENUM(BlueprintType)
enum class UAnmIndex : uint8 {
    ANMID_NONE          UMETA(DisplayName = "None"),
    ANMID_IDLE_RUN      UMETA(DisplayName = "IdleRun"),
    ANMID_ATTACK1       UMETA(DisplayName = "Attack1"),
    ANMID_ATTACK2       UMETA(DisplayName = "Attack2"),
    ANMID_DAMAGE        UMETA(DisplayName = "Damage"),
    ANMID_DEAT          UMETA(DisplayName = "Death"),
    ANMID_TESTANM1      UMETA(DisplayName = "Test1"),
    ANMID_TESTANM2      UMETA(DisplayName = "Test2"),
    ANMID_TESTANM3      UMETA(DisplayName = "Test3"),
    //Num               UMETA(Hidden)
};

//---------------------------------------------------------------------
/// アニメーションイベント
//---------------------------------------------------------------------
UENUM(BlueprintType)
enum class UAnmEvent : uint8 {
    ST_START        UMETA(DisplayName = "Start"),
    ST_END          UMETA(DisplayName = "End"),

    ST_SHOOT1       UMETA(DisplayName = "Shoot1"),
    ST_SHOOT2       UMETA(DisplayName = "Shoot2"),
};

//---------------------------------------------------------------------
/// アニメーションの状態
//---------------------------------------------------------------------
USTRUCT(BlueprintType)
struct FBPAnmState
{
    GENERATED_USTRUCT_BODY();

    UPROPERTY(BlueprintReadOnly, Category = "BPA") 
    UAnmIndex AnmIndex = UAnmIndex::ANMID_NONE; //!< 実行中のアニメーション

    UPROPERTY(BlueprintReadOnly, Category = "BPA")
    float Speed = 0; //!< アニメーションスペースのレート 0~1

    UPROPERTY(BlueprintReadOnly, Category = "BPA")
    UAnmMtIndex AnmMtIndex = UAnmMtIndex::AMTID_DAMAGE; //!< 実行中のアニメーションモンタージュ

    FBPAnmState() {
        AnmIndex = UAnmIndex::ANMID_NONE;
        Speed = 0;
    }
};

//---------------------------------------------------------------------
/// アニメーション管理用コンポーネント
//---------------------------------------------------------------------
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UAnmStateComponent : public UActorComponent
{
    GENERATED_BODY()

    //-------------------------------------------
    /// コンストラクタ
    //-------------------------------------------
    UAnmStateComponent()
    :AnimInst(nullptr)
    {}
public:

    UPROPERTY(BlueprintReadOnly, Category = "Variable|Blueprint")
    FBPAnmState BPAnmState; //!< アニメーションの状態

    //-------------------------------------------
    /// BeginPlay
    //-------------------------------------------
    void BeginPlay()
    {
        Super::BeginPlay();
        InitAnimInst();
    }

public:
    //-------------------------------------------
    /// 指定のイベントをクリア
    //-------------------------------------------
    void ClearEventNo(int No) {
        AnmEventBit &= ~(1 << No);
    }

    //-------------------------------------------
    /// アニメーションイベントを受信
    //-------------------------------------------
    void ReceiveEvent(UAnmEvent AnmEvent) {
        if (AnmEvent == UAnmEvent::ST_START) {
            if (Started == 0) {
                Started = 1;
                Ended = 0;
                AnmEventBit = 0;
            }
        }

        if (AnmEvent == UAnmEvent::ST_END) {
            Started = 0;
            Ended = 1;
        }

        AnmEventBit |= (1 << (uint32)AnmEvent); //受信イベントを保存

        //if (GEngine) {
        //  GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Orange, FString::Printf(TEXT("AnmEvent=%d"), (int)AnmEvent));
        //}
    }

    //-------------------------------------------
    /// アニメーションの変更要求
    //-------------------------------------------
    void SetAnmIndex(UAnmIndex AnmIndex) {
        AnmEventBit = 0;
        BPAnmState.AnmIndex = AnmIndex;

        //if (GEngine) {
        //  GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Orange, FString::Printf(TEXT("ANMIDX=%d"), (int)BPAnmState.AnmIndex));
        //}

    }

    uint32 AnmEventBit = 0; //!< 通知されたイベント

protected:
    UPROPERTY()
    UAnimInstance* AnimInst; //!< アニメーションインスタンス

    UPROPERTY()
    TMap<FString, UAnimMontage*> MontagesAnims; //!< アニメーションモンタージュのリスト

    //-------------------------------------------
    /// アニメーションインスタンスの取得
    //-------------------------------------------
    void InitAnimInst() {
        if (AnimInst) return;

        AActor* ac = GetOwner();
        if (!ac) return;

        auto Mesh = ac->FindComponentByClass<USkeletalMeshComponent>();
        if (!Mesh) return;

        AnimInst = Mesh->GetAnimInstance();
    }
public:
    //-------------------------------------------
    /// アニメーションインスタンスを取得する
    //-------------------------------------------
    UAnimInstance* GetAnmInst(void) {
        return AnimInst;
    }

    //-------------------------------------------
    /// アニメーションモンタージュの登録
    //-------------------------------------------
    void EntryMontageAnim(FString AnimName, FString AssetPath) {
        UAnimMontage** anm = MontagesAnims.Find(AnimName);
        if (anm == nullptr) {
            UAnimMontage* Obj = LoadObject<UAnimMontage>(NULL, *AssetPath, NULL, LOAD_None, NULL);
            if (Obj) {
                MontagesAnims.Add(AnimName, Obj);
            }
        }
    }

    //-------------------------------------------
    /// アニメーションモンタージュの取得
    //-------------------------------------------
    UAnimMontage* GetMontagesAnim(FString AnimName) {
        return MontagesAnims[AnimName];
    }

    //-------------------------------------------
    /// アニメーションモンタージュの再生
    //-------------------------------------------
    float PlayMontage(FString AnimName, 
                    float InPlayRate = 1.f, 
                    EMontagePlayReturnType ReturnValueType = EMontagePlayReturnType::MontageLength, 
                    float InTimeToStartMontageAt = 0.f, 
                    bool bStopAllMontages = true
    ) {
        return AnimInst->Montage_Play(GetMontagesAnim(AnimName), 
                                        InPlayRate,
                                        ReturnValueType,
                                        InTimeToStartMontageAt,
                                        bStopAllMontages);
    }

private:
    int Started;
    int Ended;

};

//-------------------------------------------
/// アニメーション管理用コンポーネント
//-------------------------------------------
UCLASS()
class  UAnmStateNotify : public UAnimNotify {
    GENERATED_BODY()

public:

    UPROPERTY(EditAnywhere)
    UAnmEvent AnmEvent;

    void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override {
        if (AActor* Actor = MeshComp->GetOwner()) {
            auto AnmStateComponent = Actor->FindComponentByClass<UAnmStateComponent>();
            if (AnmStateComponent) {
                AnmStateComponent->ReceiveEvent(AnmEvent);
            }
        }
    }
};

ダメージを受信しダメージアニメーションを再生するキャラクタを作成する

プレイヤーとは別にダメージを受信しダメージアニメーションを再生するキャラクタを作成します。

エネミーコントローラーの作成

エネミーコントローラー(EnemyIController01.h)はプレイヤーコントローラーに似ています。
実装するアクションは
- Init
- IdleRun
- Damage
の3つです。
アクターはACppPlyBaseを流用します。

EnemyIController01.h

/**
 * @file EnemyIController01.h
 * @brief エネミーコントローラー
 * @author     inuvatico
 * @date        2021/01/17
 * @version     1.0
 * @copyright   2020 inuvatico
 * Released under the MIT license.
 *   see https://opensource.org/licenses/MIT
 * @par (new/Add/Change : 2020/10/17)
 *
*/
#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "EnemyIController01.generated.h"

class ACppPlyBase;
class UDamageComponent;

using AEnemy01 = ACppPlyBase;

/**
 * エネミーコントローラー
 */
UCLASS()
class AEnemyIController01 : public AAIController
{
    GENERATED_BODY()

public:

    AEnemyIController01();
    virtual void BeginPlay() override;
    virtual void Tick(float DeltaTime) override;

public:
    enum class CallType {
        INIT,
        ACT,
        TERM,
        MAX
    };

    //-------------------------------------------------------------
    /// Actionの定義
    //-------------------------------------------------------------
    enum class ActType : int {
        Init,
        IdleRun,
        Damage,  
        MAX,
    };

    //-------------------------------------------------------------
    /// Actionメンバーの定義
    //-------------------------------------------------------------
    void ActInit(CallType type);
    void ActIdleRun(CallType type);
    void ActDamage(CallType type);

    //-------------------------------------------------------------
    /// Actionの切り替え
    //-------------------------------------------------------------
    using AThisController = AEnemyIController01;
    void SetAction(ActType actionID) {
        using fnc = void (AThisController::*)(CallType type);
        static const fnc fncList[ActType::MAX] = {
            &AThisController::ActInit
            ,&AThisController::ActIdleRun
            ,&AThisController::ActDamage
        };

        if ((int)actionID >= (int)ActType::MAX)
            return;

        fnc NewAction = fncList[(int)actionID];

        if (TermAction) {
            (this->*TermAction)(CallType::TERM);
        }
        TermAction = NewAction;
        (this->*NewAction)(CallType::INIT);
        CurrentAction = NewAction;
    }

private:
    void (AThisController::* CurrentAction)(CallType type) = nullptr;
    void (AThisController::* TermAction)(CallType type) = nullptr;

private:
    AEnemy01* MyActor = nullptr;; //!< 操作対象のActor
    UDamageComponent* DamageComponent = nullptr;//!< ダメージを受信するコンポーネントのポインタ
};

続いて実装です。

EnemyIController01.cpp

/**
 * @file EnemyIController01.cpp
 * @brief エネミーコントローラー
 * @author     inuvatico
 * @date        2021/01/17
 * @version     1.0
 * @copyright   2020 inuvatico
 * Released under the MIT license.
 *   see https://opensource.org/licenses/MIT
 * @par (new/Add/Change : 2020/10/17)
 *
*/

#include "Components/SkeletalMeshComponent.h"
#include "UObject/ConstructorHelpers.h"
#include "../ply/CppPlyBase.h"
#include "EnemyIController01.h"

//-------------------------------------------------
//! コンストラクタ
//-------------------------------------------------
AEnemyIController01::AEnemyIController01() {
}
//-------------------------------------------------
//! BeginPlay()
//-------------------------------------------------
void AEnemyIController01::BeginPlay() {
    Super::BeginPlay();

    MyActor = Cast <ACppPlyBase>(GetCharacter());
    if (!MyActor) return;

    DamageComponent = MyActor->FindComponentByClass<UDamageComponent>();

    //ダメージアニメーションの登録
    {
        #define ANIMMONTAGE_PATH    TEXT("AnimMontage'/Game/app/grp/chr/chr00/AnimMontage_00.AnimMontage_00'")
        #define ANMMON_DAMAGE "DAMAGE" 
        MyActor->AnmStateComponent->EntryMontageAnim(ANMMON_DAMAGE, ANIMMONTAGE_PATH);
    }
    SetAction(ActType::Init);
}

//-------------------------------------------------
//! Tick
//-------------------------------------------------
void AEnemyIController01::Tick(float DeltaTime) 
{
    if (!MyActor) return;

    if (DamageComponent) {
        if (DamageComponent->RcvDamage) {
            DamageComponent->RcvDamage = 0;
            SetAction(ActType::Damage);
        }
    }
    if (CurrentAction) {
        (this->*CurrentAction)(CallType::ACT);
    }
}

//==============================================================
// Actionメンバー
//==============================================================
void AEnemyIController01::ActInit(CallType type){
    if (type == CallType::ACT) {
        SetAction(ActType::IdleRun);
    }
}

void AEnemyIController01::ActIdleRun(CallType type) {
    if (CallType::INIT == type) {
        MyActor->AnmStateComponent->SetAnmIndex(UAnmIndex::ANMID_IDLE_RUN);
    }if (CallType::ACT == type) {
    }
}
void AEnemyIController01::ActDamage(CallType type) {
    if (CallType::INIT == type) {
        MyActor->AnmStateComponent->PlayMontage(ANMMON_DAMAGE);
    }if (CallType::ACT == type) {
        if (MyActor->AnmStateComponent->AnmEventBit & (1 << static_cast<int>(UAnmEvent::ST_END))) {
            SetAction(ActType::IdleRun);
            return;
        }
    }
}

エネミーブループリントの作成

エネミーブループリントはプレイヤーブループリント(BP_CppPlyBase)を流用して作成します。
BP_CppPlyBaseを複製してBP_CppEnemyとします。

vatico
vatico

BP_CppEnemyを開いてコントローラーをAEnemyIController01に変更します。
vatico

最後にマップに配置して実行です!
vatico

prev/--







digitalize
  始めました。