UE4

UE4/C++ 弾丸を発射する 5 : ヒット処理とダメージ通知

ダメージ処理の実装です。
ダメージを通知するコンポーネントとそれを受け取るコンポーネントを作成します。

AttakDamageBase.hの追加

全文。

/**
 * @file AttakDamageBase.h
 * @brief 攻撃とダメージ
 *  - UDamageComponent ダメージを受信するコンポーネントるコンポーネント
 *  - UHitComponent ダメージを通知す
 * @author     inuvatico
 * @date        2020/11/21
 * @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 "Components/SphereComponent.h"
#include "AttakDamageBase.generated.h"

//---------------------------------------------------------------------
//! グループ
//---------------------------------------------------------------------
UENUM(BlueprintType)
enum class UAttackGroup : uint8 {
    GP_Non      UMETA(DisplayName = "GP_Non"),
    GP_A        UMETA(DisplayName = "GP_A"),
    GP_B        UMETA(DisplayName = "GP_B"),
    GP_C        UMETA(DisplayName = "GP_C"),
    GP_D        UMETA(DisplayName = "GP_D"),
};

//---------------------------------------------------------------------
//! ダメージを受信するコンポーネント
//---------------------------------------------------------------------
UCLASS(ClassGroup = "Collision", editinlinenew, hidecategories = (Object, LOD, Lighting, TextureStreaming), meta = (DisplayName = "UDamageComponent", BlueprintSpawnableComponent))
class UDamageComponent : public UActorComponent {

    GENERATED_BODY()

public:
    //-------------------------------------------
    //! コンストラクタ
    //-------------------------------------------
    UDamageComponent() {
        RcvDamage = 0;
        Group = 0;
    }

    //-------------------------------------------
    //! ダメージの設定
    //-------------------------------------------
    virtual int SetDamage(
        const AActor* Sender,       //!< 攻撃するActor
        AActor* Receiver,   //!< 攻撃を受けるActor
        int AttackType,     //!< 攻撃種別
        int Dmg,            //!< ダメージ量
        const FHitResult* SweepResult //! ヒット情報
    ) {
        RcvDamage += Dmg;
        return 0; 
    }

public:
    int RcvDamage;
    uint32 Group;
protected:

private:
};

//---------------------------------------------------------------------
//! ヒット属性
//---------------------------------------------------------------------
enum {
    HITATB_NON = 0x0000, //!< 当たってない
    HITATB_HIT = 0x0001, //!< ぶつかった
    HITATB_OVR = 0x0002, //!< オーバーラップした(HITATB_HITもOnになる)
    HITATB_DMG = 0x0004, //!< ダメージ入った
};

//----------------------------------------------------------------------
//! 衝突通知を受け取りダメージを通知します
//----------------------------------------------------------------------
UCLASS(ClassGroup = "Hit", editinlinenew, hidecategories = (Object, LOD, Lighting, TextureStreaming), meta = (DisplayName = "UHitComponent", BlueprintSpawnableComponent))
class UHitComponent : public UActorComponent
{
    GENERATED_BODY()

public:

    //-------------------------------------------
    //! コンストラクタ
    //-------------------------------------------
    UHitComponent() {
        Attacker = nullptr;
        SetGroup((int)UAttackGroup::GP_Non);
    }

    void BeginPlay() {
        Super::BeginPlay();
        HitAtb = 0;
        //------------------------------------------------------------------------
        // Tagから衝突通知用のコンポーネントを探して通知用のコールバックを設定する
        //------------------------------------------------------------------------
        auto cmps = GetOwner()->GetComponents();
        for (auto cmp : cmps) {
            if (cmp->ComponentHasTag("NotifiyCollision")) {
                auto obj = Cast<UPrimitiveComponent>(cmp);
                if (obj) {
                    //オーバーラップのコールバックを設定
                    obj->OnComponentBeginOverlap.AddDynamic(this, &UHitComponent::OnOverlapBegin);
                    obj->OnComponentHit.AddDynamic(this, &UHitComponent::OnHit);
                }
            }
        }
    }

    //! 攻撃するアクター
    void SetAttacker(AActor* act) {
        Attacker = act;
    }

    //! オーバーラップのコールバック
    void SetGroup(int32 gp) {
        Group = gp;
    }

    UFUNCTION()
    void OnHit(
        UPrimitiveComponent* HitComp,
        AActor* OtherActor,
        UPrimitiveComponent* OtherComp,
        FVector NormalImpulse,
        const FHitResult& Hit
    ){
        Rcv(false, HitComp, OtherActor, OtherComp, Hit);
    }

    //-------------------------------------------
    //! オーバーラップのコールバック
    //-------------------------------------------
    UFUNCTION()
    void OnOverlapBegin(
        UPrimitiveComponent* OverlappedComp,
        AActor* OtherActor,
        UPrimitiveComponent* OtherComp,
        int32 OtherBodyIndex,
        bool bFromSweep, 
        const FHitResult& SweepResult)
    {
        Rcv(true, OverlappedComp, OtherActor, OtherComp, SweepResult);
    }

protected:
    int  Rcv(
        bool IsOverlap,
        UPrimitiveComponent* OverlappedComp,
        AActor* OtherActor,
        UPrimitiveComponent* OtherComp,
        const FHitResult& Hit)
    {
        if (Attacker) {
            if (!IsValid(Attacker)) {
                Attacker = nullptr; //攻撃側Actorはいなくなった
            }
        }

        if (Attacker == OtherActor) {
            return 0; //自分の攻撃は当たらない
        }

        {
            FString str;
            if (IsOverlap)  str = "OnOverlapBegin :" + OtherActor->GetName();
            else            str = "OnHitBegin : " + OtherActor->GetName();
            GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Orange, str);
        }

        HitAtb = HITATB_HIT;
        if (IsOverlap) HitAtb |= HITATB_OVR;

        auto DamageComponent = OtherActor->FindComponentByClass<UDamageComponent>();
        if (DamageComponent) {
            HitAtb |= HITATB_OVR;
            if ((DamageComponent->Group == 0) || (DamageComponent->Group != Group)) {// 仲間以外に攻撃を充てる
                DamageComponent->SetDamage(
                    Attacker,
                    OtherActor,     //!< 攻撃を受けるActor
                    0,              //!< 攻撃種別
                    100,            //!< ダメージ量
                    &Hit            //! ヒット情報
                );
            }
        }
        return 0;
    }

public:
    uint32 HitAtb;
protected:
    uint32 Group;
    UPROPERTY()
        const AActor* Attacker;
};

cpp:

// Fill out your copyright notice in the Description page of Project Settings.
#include "AttakDamageBase.h"

定義されているクラスは2つです。

ダメージの受信 : UDamageComponent

UDamageComponentはSetDamage()を通してダメージを受信します。

ダメージの通知 : UHitComponent

UHitComponentは衝突イベントを受け取りダメージを通知します。

衝突イベントの設定 OnComponentBeginOverlap/OnComponentBeginHit

衝突イベントのコールバック設定をBeginPlay()で行っています。
オーナーのアクターからTag"NotifiyCollision"を持つコンポーネントを見つけコールバックを設定します。

ダメージの通知:Rcv()

UHitComponentは衝突イベントを受けとると衝突したActorからUDamageComponentを見つけダメージを通知します。










digitalize
  始めました。