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

  • 장식 이펙트를 담당하는 게임플레이 큐의 활용방법의 학습
  • 기간형 게임플레이 이펙트를 활용한 다양한 아이템 상자의 구현
  • 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)


     

     

     

    실행화면

     


     

     

    정리

     

    아이템 상자 구현

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