[UE GAS] 아이템 상자 구현
장식이펙트와 아이템 상자의 구현
- 장식 이펙트를 담당하는 게임플레이 큐의 활용방법의 학습
- 기간형 게임플레이 이펙트를 활용한 다양한 아이템 상자의 구현
- 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);
- ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, EffectSpecHandle, TargetDataHandle);
- if (EffectSpecHandle.IsValid())
실행화면
아이템 상자의 구현하기
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)
실행화면
정리
아이템 상자 구현
- 공격 명중시 장식 이펙트를 추가로 발동시키기
- 기간형 게임플레이 이펙트의 주기 설정이 가지는 특징의 학습
- 게임 이펙트 설정으로 장식 이펙트를 발동시키는 방법의 학습
- 태그 설정을 활용한 게임플레이 이펙트 및 태그의 제거
- 게임이벤트 발동과 어트리뷰트 직접 접근을 통한 무기추가와 제거 구현
'⭐ Unreal Engine > UE Game Ability System(GAS)' 카테고리의 다른 글
[UE GAS] 캐릭터의 광역 스킬 구현 (0) | 2024.03.10 |
---|---|
[UE GAS] 어트리뷰트와 UI 연동 Integration with Attribute and UI (0) | 2024.03.09 |
[UE GAS] 게임플레이 이펙트의 활용 Applying Gameplay Effect (0) | 2024.03.08 |
[UE GAS] 캐릭터 어트리뷰트 설정 (0) | 2024.03.07 |
[UE GAS] 공격 판정 시스템의 구현 (0) | 2024.03.06 |
댓글
이 글 공유하기
다른 글
-
[UE GAS] 캐릭터의 광역 스킬 구현
[UE GAS] 캐릭터의 광역 스킬 구현
2024.03.10 -
[UE GAS] 어트리뷰트와 UI 연동 Integration with Attribute and UI
[UE GAS] 어트리뷰트와 UI 연동 Integration with Attribute and UI
2024.03.09 -
[UE GAS] 게임플레이 이펙트의 활용 Applying Gameplay Effect
[UE GAS] 게임플레이 이펙트의 활용 Applying Gameplay Effect
2024.03.08 -
[UE GAS] 캐릭터 어트리뷰트 설정
[UE GAS] 캐릭터 어트리뷰트 설정
2024.03.07