// AuraProjectile.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AuraProjectile.generated.h"
class USphereComponent;
class UProjectileMovementComponent;
UCLASS()
class AURA_API AAuraProjectile : public AActor
{
GENERATED_BODY()
public:
AAuraProjectile();
UPROPERTY(VisibleAnywhere)
TObjectPtr<UProjectileMovementComponent> ProjectileMovement;
protected:
virtual void BeginPlay() override;
void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
);
private:
UPROPERTY(VisibleAnywhere)
TObjectPtr<USphereComponent> Sphere;
};
// AuraProjectile.cpp
#include "Actor/AuraProjectile.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
// Sets default values
AAuraProjectile::AAuraProjectile()
{
PrimaryActorTick.bCanEverTick = false;
bReplicates = true;
Sphere = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere"));
SetRootComponent(Sphere);
Sphere->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
Sphere->SetCollisionResponseToAllChannels(ECR_Ignore);
Sphere->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Overlap);
Sphere->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Overlap);
Sphere->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
ProjectileMovement->InitialSpeed = 550.f;
ProjectileMovement->MaxSpeed = 550.f;
ProjectileMovement->ProjectileGravityScale = 0.f;
}
void AAuraProjectile::BeginPlay()
{
Super::BeginPlay();
Sphere->OnComponentBeginOverlap.AddDynamic(this, &AAuraProjectile::OnSphereOverlap);
}
void AAuraProjectile::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
}GameplayAbility도 세부적인 내용까지는 아니더라도 전반적인 구성과 어떤 변수와 함수가 존재하는 지 등의 대략적인 내용을 파악해두는 것이 좋다.
특히, ActivateAbility()와 같이 상속해서 구현해주어야 하는 함수들도 있기 때문이다.
/*
UGameplayAbility
Abilities define custom gameplay logic that can be activated or triggered.
The main features provided by the AbilitySystem for GameplayAbilities are:
-CanUse functionality:
-Cooldowns
-Costs (mana, stamina, etc)
-etc
-Replication support
-Client/Server communication for ability activation
-Client prediction for ability activation
-Instancing support
-Abilities can be non-instanced (native only)
-Instanced per owner
-Instanced per execution (default)
-Basic, extendable support for:
-Input binding
-'Giving' abilities (that can be used) to actors
See GameplayAbility_Montage for an example of a non-instanced ability
-Plays a montage and applies a GameplayEffect to its target while the montage is playing.
-When finished, removes GameplayEffect.
Note on replication support:
-Non instanced abilities have limited replication support.
-Cannot have state (obviously) so no replicated properties
-RPCs on the ability class are not possible either.
To support state or event replication, an ability must be instanced. This can be done with the InstancingPolicy property.
/
GameplayAbility 클래스의 헤더에서 GameplayAbility를 대략적으로 어떻게 정의하고 있는지, 어떤 기능을 제공하는지를 코멘트로 명시해두고 있다.
- 의도적으로 활성화되거나 트리거될 수 있는 커스텀 가능한 게임플레이 관련 로직을 정의하는 것을 GameplayAbility라고 정의하고 있다.
- CanUse 함수: 쿨타임, 코스트(마나/스테미나 같은 개념), 기타
- Replication 지원
- 입력 바인딩, GameplayAbility 부여 등의 기본/확장 가능한 지원
// ----------------------------------------------------------------------------------------------------------------
//
// The important functions:
//
// CanActivateAbility() - const function to see if ability is activatable. Callable by UI etc
//
// TryActivateAbility() - Attempts to activate the ability. Calls CanActivateAbility(). Input events can call this directly.
// - Also handles instancing-per-execution logic and replication/prediction calls.
//
// CallActivateAbility() - Protected, non virtual function. Does some boilerplate 'pre activate' stuff, then calls ActivateAbility()
//
// ActivateAbility() - What the abilities *does*. This is what child classes want to override.
//
// CommitAbility() - Commits reources/cooldowns etc. ActivateAbility() must call this!
//
// CancelAbility() - Interrupts the ability (from an outside source).
//
// EndAbility() - The ability has ended. This is intended to be called by the ability to end itself.
//
// ----------------------------------------------------------------------------------------------------------------
특히, UGameplayAbility 내 중요 함수들을 위의 코멘트와 같이 명시하고 있다.
이 외에도 Input, Animation, Ability Levels and Source Object, Interaction with Ability System Component 등의 코멘트가 있으니 참고하면 좋다.
// AuraProjectileSpell.h
#include "CoreMinimal.h"
#include "AbilitySystem/Abilities/AuraGameplayAbility.h"
#include "AuraProjectileSpell.generated.h"
class AAuraProjectile;
UCLASS()
class AURA_API UAuraProjectileSpell : public UAuraGameplayAbility
{
GENERATED_BODY()
protected:
// GameplayAbility 클래스에 대한 설명 참조
// GameplayAbility가 하는 일을 직접 구현하기 위한 오버라이드 함수임.
virtual void ActivateAbility(
const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo,
const FGameplayEventData* TriggerEventData) override;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<AAuraProjectile> ProjectileClass;
};
// AuraProjectileSpell.cpp
#include "AbilitySystem/Abilities/AuraProjectileSpell.h"
#include "Actor/AuraProjectile.h"
#include "Interaction/CombatInterface.h"
void UAuraProjectileSpell::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
const bool bIsServer = HasAuthority(&ActivationInfo);
if (!bIsServer) return;
ICombatInterface* CombatInterface = Cast<ICombatInterface>(GetAvatarActorFromActorInfo());
if (CombatInterface)
{
const FVector SocketLocation = CombatInterface->GetCombatSocketLocation();
FTransform SpawnTransform;
SpawnTransform.SetLocation(SocketLocation);
// TODO: Set Projectile Rotation
AAuraProjectile* Projectile = GetWorld()->SpawnActorDeferred<AAuraProjectile>(
ProjectileClass,
SpawnTransform,
GetOwningActorFromActorInfo(),
Cast<APawn>(GetOwningActorFromActorInfo()),
ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
// TODO: Set Projectile Properties (Give Projectile a GameplayEffectSped for causing Damage)
Projectile->FinishSpawning(SpawnTransform);
}
}