장식이펙트와 아이템 상자의 구현 

  • 장식 이펙트를 담당하는 게임플레이 큐의 활용방법의 학습
  • 기간형 게임플레이 이펙트를 활용한 다양한 아이템 상자의 구현
  • GAS의다양한 API를 활용한 무기에 관련된 게임플레이 이벤트와 어트리뷰트의 설정   

 

 

 

목차

     

     


     

     

    기획


     

    게임플레이 이펙트를 활용한 아이템과 무기 기획

     

    • 데미지, 회복상자의 구현
    • 체력 레젠(Regeneration) 상자, DOT(Damage over time) 상자의 구현
    • 캐릭터에 무기 추가 
      • 무기로 인한 공격 범위 증가: 75
      • 무기로 인한 공격력 증가: 100

     

    핵심 컴포넌트

     

     


     

     

     

    Gameplay Cue를 활용한 캐릭터 피격 이펙트 구현  


     

     

    게임플레이 큐 Gameplay Cue

     

    Gameplay Cue

    • 시각 이펙트나 사운드와 같은 게임 로직과 무관한 시각적, 청각적 기능을 담당함
    • Client부분에만 사용하고 Dedicated Server에서는 사용할 필요가 없음
    • 두 종류의 Gameplay Queue가 있음
      • Static Gameplay Cue일시적으로 발생하는 특수효과 사용. Execute 이벤트 발동.
      • Actor Gameplay Queue일정 기간동안 발생하는 특수효과에 사용. Add/Remove 이벤트 발동.
    • C++로도 구현할수 있지만, Blueprint로 제작하는 것이 더 생산적임.
    • GameplayEffect(GE)에서 자동으로 GamplayCue(GC)와 연동할수 있도록 기능을 제공하고 있음.
    • Gameplay Queue의 재생 GameplayCueManager 관리. ( 다른 시스템과 분리된 구조)
    • Gameplay Tag를 사용해 쉽게 발동시킬 수 있음
      • 이 때 반드시 GameplayCue로시작하는 Gameplay Tag를 사용해야 함.   

     

     

    GameplayCue에 쓸 GameplayTags 지정하기

     

    Project Settings - GameplayTags - Gameplay Tag List

    • GameplayCue.Character.AttackHit :  캐릭터에 맞았을 때 발동하는 특수효과를 의미하는 태그
    • GameplayCue.Character.Damage  캐릭터가 맞았을 때 들어가는 데미지를 의미하는 태그  
    • GameplayCue.Chest.Open  를 의미하는 태그  


     

     

    ABGC_AttackHit 생성

     

    캐릭터를 공격해서 명중했을때 이펙트가 터지는 이펙트

     

    GameplayCueNotify_Static - GC_ABGC_AttackHit 생성

     

     

    ABGC_AttackHit.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "GameplayCueNotify_Static.h"
    #include "ABGC_AttackHit.generated.h"
    UCLASS()
    class ARENABATTLEGAS_API UABGC_AttackHit : public UGameplayCueNotify_Static
    {
    GENERATED_BODY()
    public:
    UABGC_AttackHit();
    virtual bool OnExecute_Implementation(AActor* Target, const FGameplayCueParameters& Parameters) const override;
    protected:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=GameplayCue)
    TObjectPtr<class UParticleSystem> ParticleSystem;
    };

     

     

    ABGC_AttackHit.cpp

    더보기
    #include "GC/ABGC_AttackHit.h"
    #include "Particles/ParticleSystem.h"
    #include "Kismet/GameplayStatics.h"
    UABGC_AttackHit::UABGC_AttackHit()
    {
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ExplosionRef(TEXT("/Script/Engine.ParticleSystem'/Game/StarterContent/Particles/P_Explosion.P_Explosion'"));
    if (ExplosionRef.Object)
    {
    ParticleSystem = ExplosionRef.Object; // 파티클 이펙트 지정
    }
    }
    bool UABGC_AttackHit::OnExecute_Implementation(AActor* Target, const FGameplayCueParameters& Parameters) const
    {
    const FHitResult* HitResult = Parameters.EffectContext.GetHitResult();
    if (HitResult)
    {
    UGameplayStatics::SpawnEmitterAtLocation(Target, ParticleSystem, HitResult->ImpactPoint, FRotator::ZeroRotator, true); // 충돌 위치에 스폰
    }
    return false;
    }

     

     

     

    ※ GameplayEffectTypes.h

    HitResult는 FGameplayEffectContextHandle에 담겨 전달된다.

     

     

     

    ABGC_AttackHit 클래스를 상속받는 BPGC_AttackHit 생성


     

     

    ABGA_AttackHitCheck

     

    ABGA_AttackHitCheck.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Abilities/GameplayAbility.h"
    #include "ABGA_AttackHitCheck.generated.h"
    UCLASS()
    class ARENABATTLEGAS_API UABGA_AttackHitCheck : public UGameplayAbility
    {
    GENERATED_BODY()
    public:
    UABGA_AttackHitCheck();
    virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
    protected:
    UFUNCTION()
    void OnTraceResultCallback(const FGameplayAbilityTargetDataHandle& TargetDataHandle);
    UPROPERTY(EditAnywhere, Category = "GAS")
    TSubclassOf<class UGameplayEffect> AttackDamageEffect;
    UPROPERTY(EditAnywhere, Category = "GAS")
    TSubclassOf<class UGameplayEffect> AttackBuffEffect;
    float CurrentLevel; // CurveTable의 현재레벨 변수
    };

     

     

    ABGA_AttackHitCheck.cpp

    더보기
    #include "GA/ABGA_AttackHitCheck.h"
    #include "ArenaBattleGAS.h"
    #include "AbilitySystemBlueprintLibrary.h"
    #include "GA/AT/ABAT_Trace.h"
    #include "GA/TA/ABTA_Trace.h"
    #include "Attribute/ABCharacterAttributeSet.h"
    #include "ArenaBattleGAS.h"
    #include "Tag/ABGameplayTag.h"
    UABGA_AttackHitCheck::UABGA_AttackHitCheck()
    {
    InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
    }
    void UABGA_AttackHitCheck::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
    {
    Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
    CurrentLevel = TriggerEventData->EventMagnitude;
    UABAT_Trace* AttackTraceTask = UABAT_Trace::CreateTask(this, AABTA_Trace::StaticClass());
    AttackTraceTask->OnComplete.AddDynamic(this, &UABGA_AttackHitCheck::OnTraceResultCallback);
    AttackTraceTask->ReadyForActivation();
    }
    void UABGA_AttackHitCheck::OnTraceResultCallback(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
    {
    if (UAbilitySystemBlueprintLibrary::TargetDataHasHitResult(TargetDataHandle, 0))
    {
    FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(TargetDataHandle, 0);
    ABGAS_LOG(LogABGAS, Log, TEXT("Target %s Detected"), *(HitResult.GetActor()->GetName()));
    UAbilitySystemComponent* SourceASC = GetAbilitySystemComponentFromActorInfo_Checked();
    UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(HitResult.GetActor());
    const UABCharacterAttributeSet* SourceAttribute = SourceASC->GetSet<UABCharacterAttributeSet>();
    FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingGameplayEffectSpec(AttackDamageEffect, CurrentLevel);
    if (EffectSpecHandle.IsValid())
    {
    ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, EffectSpecHandle, TargetDataHandle);
    FGameplayEffectContextHandle CueContextHandle = UAbilitySystemBlueprintLibrary::GetEffectContext(EffectSpecHandle);
    CueContextHandle.AddHitResult(HitResult);
    FGameplayCueParameters CueParam;
    CueParam.EffectContext = CueContextHandle;
    TargetASC->ExecuteGameplayCue(ABTAG_GAMEPLAYCUE_CHARACTER_ATTACKHIT, CueParam);
    }
    FGameplayEffectSpecHandle BuffEffectSpecHandle = MakeOutgoingGameplayEffectSpec(AttackBuffEffect);
    if (BuffEffectSpecHandle.IsValid())
    {
    ApplyGameplayEffectSpecToOwner(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, BuffEffectSpecHandle);
    }
    }
    bool bReplicatedEndAbility = true;
    bool bWasCancelled = false;
    EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled);
    }

    void UABGA_AttackHitCheck::OnTraceResultCallback(const FGameplayAbilityTargetDataHandle& TargetDataHandle)

    • if (UAbilitySystemBlueprintLibrary::TargetDataHasHitResult(TargetDataHandle, 0))
      • if (EffectSpecHandle.IsValid())
        • ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, EffectSpecHandle, TargetDataHandle);

        • FGameplayEffectContextHandle CueContextHandle = UAbilitySystemBlueprintLibrary::GetEffectContext(EffectSpecHandle);
          CueContextHandle.AddHitResult(HitResult);
          FGameplayCueParameters CueParam;
          CueParam.EffectContext = CueContextHandle;

        • TargetASC->ExecuteGameplayCue(ABTAG_GAMEPLAYCUE_CHARACTER_ATTACKHIT, CueParam);

     

     

    실행화면

     

     

     


     

     

     

     

    아이템 상자의 구현하기


     

     

    ABGASItemBox 생성

     

    Actor - ABGASItemBox 생성

     

     

    ABGASItemBox.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "AbilitySystemInterface.h"
    #include "GameplayTagContainer.h"
    #include "ABGASItemBox.generated.h"
    UCLASS()
    class ARENABATTLEGAS_API AABGASItemBox : public AActor, public IAbilitySystemInterface
    {
    GENERATED_BODY()
    public:
    AABGASItemBox();
    virtual class UAbilitySystemComponent* GetAbilitySystemComponent() const override;
    virtual void NotifyActorBeginOverlap(class AActor* Other) override;
    protected:
    virtual void PostInitializeComponents() override;
    void ApplyEffectToTarget(AActor* Target); // ItemBox을 먹은 대상에게 GE를 적용
    void InvokeGameplayCue(AActor* Target); // ItemBox 자체의 이펙트를 재생하기 위한 Cue
    protected:
    UPROPERTY()
    TObjectPtr<class UAbilitySystemComponent> ASC;
    UPROPERTY(VisibleAnywhere, Category = Box)
    TObjectPtr<class UBoxComponent> Trigger;
    UPROPERTY(VisibleAnywhere, Category = Box)
    TObjectPtr<class UStaticMeshComponent> Mesh;
    UPROPERTY(EditAnywhere, Category = GAS)
    TSubclassOf<class UGameplayEffect> GameplayEffectClass;
    UPROPERTY(EditAnywhere, Category = GAS, Meta = (Categories=GameplayCue))
    FGameplayTag GameplayCueTag;
    };

     

     

     

     

    ABGASItemBox.cpp

    더보기
    #include "Item/ABGASItemBox.h"
    #include "AbilitySystemComponent.h"
    #include "Components/BoxComponent.h"
    #include "Components/StaticMeshComponent.h"
    #include "Physics/ABCollision.h"
    #include "AbilitySystemBlueprintLibrary.h"
    AABGASItemBox::AABGASItemBox()
    {
    ASC = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("ASC"));
    Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("Trigger"));
    Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    RootComponent = Trigger;
    Mesh->SetupAttachment(Trigger);
    Trigger->SetCollisionProfileName(CPROFILE_ABTRIGGER);
    Trigger->SetBoxExtent(FVector(40.0f, 42.0f, 30.0f));
    static ConstructorHelpers::FObjectFinder<UStaticMesh> BoxMeshRef(TEXT("/Script/Engine.StaticMesh'/Game/ArenaBattle/Environment/Props/SM_Env_Breakables_Box1.SM_Env_Breakables_Box1'"));
    if (BoxMeshRef.Object)
    {
    Mesh->SetStaticMesh(BoxMeshRef.Object);
    }
    Mesh->SetRelativeLocation(FVector(0.0f, -3.5f, -30.0f));
    Mesh->SetCollisionProfileName(TEXT("NoCollision"));
    }
    UAbilitySystemComponent* AABGASItemBox::GetAbilitySystemComponent() const
    {
    return ASC;
    }
    void AABGASItemBox::NotifyActorBeginOverlap(AActor* Other)
    {
    Super::NotifyActorBeginOverlap(Other);
    InvokeGameplayCue(Other); // ItemBox 자체의 이펙트를 재생
    ApplyEffectToTarget(Other); // ItemBox을 먹은 Target에서 이펙트를 재생
    Mesh->SetHiddenInGame(true); // 매쉬를 숨김
    SetActorEnableCollision(false); // 충돌x
    SetLifeSpan(2.0f);
    }
    void AABGASItemBox::PostInitializeComponents()
    {
    Super::PostInitializeComponents();
    ASC->InitAbilityActorInfo(this, this); // ItemBox 초기화
    }
    void AABGASItemBox::ApplyEffectToTarget(AActor* Target)
    {
    UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Target);
    if (TargetASC)
    {
    FGameplayEffectContextHandle EffectContext = TargetASC->MakeEffectContext();
    EffectContext.AddSourceObject(this);
    FGameplayEffectSpecHandle EffectSpecHandle = TargetASC->MakeOutgoingSpec(GameplayEffectClass, 1, EffectContext);
    if (EffectSpecHandle.IsValid())
    {
    //TargetASC->ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get()); // C++ Ver.
    TargetASC->BP_ApplyGameplayEffectSpecToSelf(EffectSpecHandle); // BP Ver.
    }
    }
    }
    void AABGASItemBox::InvokeGameplayCue(AActor* Target) // 자기 자신(=ItemBox)에게 특수효과를 재생
    {
    FGameplayCueParameters Param;
    Param.SourceObject = this;
    Param.Instigator = Target;
    Param.Location = GetActorLocation();
    ASC->ExecuteGameplayCue(GameplayCueTag, Param);
    }

     

     

    일반 데미지 버젼 - BPGE_Damage

     

    Dot 데미지 버젼 - BPGE_Dot


     

    BPGE_CharaterStat 생성

     


     

     

    BPGC_ChestOpen 생성

     

    GameplayCueNotify_Static - BPGC_ChestOpen 생성


     

     

    BPGE_Damage 생성 + BPGC_Damage 생성

     

    BPGE_Damage

    GameplayEffect - BPGE_Damage 생성

     

    BPGC_Damage


     

     

    BPGE_Heal 생성 + BPGE_Regen 생성

     

    BPGE_Heal

     

     

    BPGE_Regen 


     

     

    실행화면

     

     


     

     

     

    무적상태로 만드는 아이템 상자


     

     

    BPGE_Invincible + BPGE_InvincibleInf + BPGE_InvincibleRemove 생성

     


     

     

    무기를 주는 아이템 상자


     

     

    ABGASWeaponBox 생성

     

    ABGASItemBox - ABGASWeaponBox 생성

     

     

    ABGASWeaponBox.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Item/ABGASItemBox.h"
    #include "ABGASWeaponBox.generated.h"
    UCLASS()
    class ARENABATTLEGAS_API AABGASWeaponBox : public AABGASItemBox
    {
    GENERATED_BODY()
    protected:
    virtual void NotifyActorBeginOverlap(AActor* Other) override;
    protected:
    UPROPERTY(EditAnywhere, Category = GAS, Meta=(Categories=Event))
    FGameplayTag WeaponEventTag;
    };

     

     

     

    ABGASWeaponBox.cpp

    더보기
    #include "Item/ABGASWeaponBox.h"
    #include "AbilitySystemBlueprintLibrary.h"
    void AABGASWeaponBox::NotifyActorBeginOverlap(AActor* Other)
    {
    Super::NotifyActorBeginOverlap(Other);
    UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(Other, WeaponEventTag, FGameplayEventData());
    }

    이 이벤트를 캐릭터(=ABGASCharacterPlayer)가 직접 수신을 하게 된다.


     

     

     

    ABGASCharacterPlayer

     

    ABGASCharacterPlayer.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Character/ABCharacterPlayer.h"
    #include "AbilitySystemInterface.h"
    #include "Abilities/GameplayAbilityTypes.h"
    #include "ABGASCharacterPlayer.generated.h"
    UCLASS()
    class ARENABATTLEGAS_API AABGASCharacterPlayer : public AABCharacterPlayer, public IAbilitySystemInterface
    {
    GENERATED_BODY()
    public:
    AABGASCharacterPlayer();
    virtual class UAbilitySystemComponent* GetAbilitySystemComponent() const override;
    virtual void PossessedBy(AController* NewController) override;
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    protected:
    void SetupGASInputComponent();
    void GASInputPressed(int32 InputId);
    void GASInputReleased(int32 InputId);
    UFUNCTION()
    virtual void OnOutOfHealth();
    void EquipWeapon(const FGameplayEventData* EventData);
    void UnequipWeapon(const FGameplayEventData* EventData);
    protected:
    UPROPERTY(EditAnywhere, Category = GAS)
    TObjectPtr<class UAbilitySystemComponent> ASC;
    UPROPERTY(EditAnywhere, Category = GAS)
    TArray<TSubclassOf<class UGameplayAbility>> StartAbilities;
    UPROPERTY(EditAnywhere, Category = GAS)
    TMap<int32, TSubclassOf<class UGameplayAbility>> StartInputAbilities;
    UPROPERTY(VisibleAnywhere)
    TObjectPtr<class UABGASWidgetComponent> HpBar;
    UPROPERTY(EditAnywhere, Category = Weapon)
    TObjectPtr<class USkeletalMesh> WeaponMesh;
    UPROPERTY(EditAnywhere, Category = Weapon)
    float WeaponRange;
    UPROPERTY(EditAnywhere, Category = Weapon)
    float WeaponAttackRate;
    };

    변경사항 없음

     

     

    ABGASCharacterPlayer.cpp

    더보기
    #include "Character/ABGASCharacterPlayer.h"
    #include "AbilitySystemComponent.h"
    #include "Player/ABGASPlayerState.h"
    #include "EnhancedInputComponent.h"
    #include "UI/ABGASWidgetComponent.h"
    #include "UI/ABGASUserWidget.h"
    #include "Attribute/ABCharacterAttributeSet.h"
    #include "Tag/ABGameplayTag.h"
    AABGASCharacterPlayer::AABGASCharacterPlayer()
    {
    ASC = nullptr;
    static ConstructorHelpers::FObjectFinder<UAnimMontage> ComboActionMontageRef(TEXT("/Script/Engine.AnimMontage'/Game/ArenaBattleGAS/Animation/AM_ComboAttack.AM_ComboAttack'"));
    if (ComboActionMontageRef.Object)
    {
    ComboActionMontage = ComboActionMontageRef.Object;
    }
    HpBar = CreateDefaultSubobject<UABGASWidgetComponent>(TEXT("Widget"));
    HpBar->SetupAttachment(GetMesh());
    HpBar->SetRelativeLocation(FVector(0.0f, 0.0f, 180.0f));
    static ConstructorHelpers::FClassFinder<UUserWidget> HpBarWidgetRef(TEXT("/Game/ArenaBattle/UI/WBP_HpBar.WBP_HpBar_C"));
    if (HpBarWidgetRef.Class)
    {
    HpBar->SetWidgetClass(HpBarWidgetRef.Class);
    HpBar->SetWidgetSpace(EWidgetSpace::Screen);
    HpBar->SetDrawSize(FVector2D(200.0f, 20.f));
    HpBar->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    }
    static ConstructorHelpers::FObjectFinder<USkeletalMesh> WeaponMeshRef(TEXT("/Script/Engine.SkeletalMesh'/Game/InfinityBladeWeapons/Weapons/Blunt/Blunt_Hellhammer/SK_Blunt_HellHammer.SK_Blunt_HellHammer'"));
    if (WeaponMeshRef.Object)
    {
    WeaponMesh = WeaponMeshRef.Object;
    }
    WeaponRange = 75.f;
    WeaponAttackRate = 100.0f;
    }
    UAbilitySystemComponent* AABGASCharacterPlayer::GetAbilitySystemComponent() const
    {
    return ASC;
    }
    void AABGASCharacterPlayer::PossessedBy(AController* NewController)
    {
    Super::PossessedBy(NewController);
    AABGASPlayerState* GASPS = GetPlayerState<AABGASPlayerState>();
    if (GASPS)
    {
    ASC = GASPS->GetAbilitySystemComponent();
    ASC->InitAbilityActorInfo(GASPS, this);
    ASC->GenericGameplayEventCallbacks.FindOrAdd(ABTAG_EVENT_CHARACTER_WEAPONEQUIP).AddUObject(this, &AABGASCharacterPlayer::EquipWeapon);
    ASC->GenericGameplayEventCallbacks.FindOrAdd(ABTAG_EVENT_CHARACTER_WEAPONUNEQUIP).AddUObject(this, &AABGASCharacterPlayer::UnequipWeapon);
    const UABCharacterAttributeSet* CurrentAttributeSet = ASC->GetSet<UABCharacterAttributeSet>();
    if (CurrentAttributeSet)
    {
    CurrentAttributeSet->OnOutOfHealth.AddDynamic(this, &ThisClass::OnOutOfHealth);
    }
    for (const auto& StartAbility : StartAbilities)
    {
    FGameplayAbilitySpec StartSpec(StartAbility);
    ASC->GiveAbility(StartSpec);
    }
    for (const auto& StartInputAbility : StartInputAbilities)
    {
    FGameplayAbilitySpec StartSpec(StartInputAbility.Value);
    StartSpec.InputID = StartInputAbility.Key;
    ASC->GiveAbility(StartSpec);
    }
    SetupGASInputComponent();
    APlayerController* PlayerController = CastChecked<APlayerController>(NewController);
    PlayerController->ConsoleCommand(TEXT("showdebug abilitysystem"));
    }
    }
    void AABGASCharacterPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
    {
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    SetupGASInputComponent();
    }
    void AABGASCharacterPlayer::SetupGASInputComponent()
    {
    if (IsValid(ASC) && IsValid(InputComponent))
    {
    UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(InputComponent);
    EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &AABGASCharacterPlayer::GASInputPressed, 0);
    EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &AABGASCharacterPlayer::GASInputReleased, 0);
    EnhancedInputComponent->BindAction(AttackAction, ETriggerEvent::Triggered, this, &AABGASCharacterPlayer::GASInputPressed, 1);
    }
    }
    void AABGASCharacterPlayer::GASInputPressed(int32 InputId)
    {
    FGameplayAbilitySpec* Spec = ASC->FindAbilitySpecFromInputID(InputId);
    if (Spec)
    {
    Spec->InputPressed = true;
    if (Spec->IsActive())
    {
    ASC->AbilitySpecInputPressed(*Spec);
    }
    else
    {
    ASC->TryActivateAbility(Spec->Handle);
    }
    }
    }
    void AABGASCharacterPlayer::GASInputReleased(int32 InputId)
    {
    FGameplayAbilitySpec* Spec = ASC->FindAbilitySpecFromInputID(InputId);
    if (Spec)
    {
    Spec->InputPressed = false;
    if (Spec->IsActive())
    {
    ASC->AbilitySpecInputReleased(*Spec);
    }
    }
    }
    void AABGASCharacterPlayer::OnOutOfHealth()
    {
    SetDead();
    }
    void AABGASCharacterPlayer::EquipWeapon(const FGameplayEventData* EventData)
    {
    if (Weapon)
    {
    Weapon->SetSkeletalMesh(WeaponMesh);
    const float CurrentAttackRange = ASC->GetNumericAttributeBase(UABCharacterAttributeSet::GetAttackRangeAttribute());
    const float CurrentAttackRate = ASC->GetNumericAttributeBase(UABCharacterAttributeSet::GetAttackRateAttribute());
    ASC->SetNumericAttributeBase(UABCharacterAttributeSet::GetAttackRangeAttribute(), CurrentAttackRange + WeaponRange);
    ASC->SetNumericAttributeBase(UABCharacterAttributeSet::GetAttackRateAttribute(), CurrentAttackRate + WeaponAttackRate);
    }
    }
    void AABGASCharacterPlayer::UnequipWeapon(const FGameplayEventData* EventData)
    {
    if (Weapon)
    {
    const float CurrentAttackRange = ASC->GetNumericAttributeBase(UABCharacterAttributeSet::GetAttackRangeAttribute());
    const float CurrentAttackRate = ASC->GetNumericAttributeBase(UABCharacterAttributeSet::GetAttackRateAttribute());
    ASC->SetNumericAttributeBase(UABCharacterAttributeSet::GetAttackRangeAttribute(), CurrentAttackRange - WeaponRange);
    ASC->SetNumericAttributeBase(UABCharacterAttributeSet::GetAttackRateAttribute(), CurrentAttackRate - WeaponAttackRate);
    Weapon->SetSkeletalMesh(nullptr);
    }
    }

    void AABGASCharacterPlayer::PossessedBy(AController* NewController)

    • if (GASPS)
      • ASC->GenericGameplayEventCallbacks.FindOrAdd(ABTAG_EVENT_CHARACTER_WEAPONEQUIP).AddUObject(this, &AABGASCharacterPlayer::EquipWeapon);
      • ASC->GenericGameplayEventCallbacks.FindOrAdd(ABTAG_EVENT_CHARACTER_WEAPONUNEQUIP).AddUObject(this, &AABGASCharacterPlayer::UnequipWeapon);
      • GenericGameplayEventCallbacks는 FGameplayTag와 FGameplayEventMulticastDelegate를 받는다.

     

    void AABGASCharacterPlayer::EquipWeapon(const FGameplayEventData* EventData)

     

    void AABGASCharacterPlayer::UnequipWeapon(const FGameplayEventData* EventData)


     

     

     

    실행화면

     


     

     

    정리

     

    아이템 상자 구현

    • 공격 명중시 장식 이펙트를 추가로 발동시키기
    • 기간형 게임플레이 이펙트의 주기 설정이 가지는 특징의 학습 
    • 게임 이펙트 설정으로 장식 이펙트를 발동시키는 방법의 학습
    • 태그 설정을 활용한 게임플레이 이펙트 및 태그의 제거
    • 게임이벤트 발동과 어트리뷰트 직접 접근을 통한 무기추가와 제거 구현