Unreal GAS (24) - First Gameplay Ability

wnsduf0000·2025년 12월 1일

Unreal_GAS

목록 보기
25/34
  • First Gameplay Ability - Firebolt
    • AuraProjectile
      • 투사체들의 기본 클래스
        // 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)
        {
        
        }
      • 투사체들은 전부 구 형태의 Collision 감지를 할 예정이므로, USphereComponent를 추가.
        • USphereComponent의 OnComponentBeginOverlap 델리게이트에 반응하기 위한 함수 OnSphereOverlap()도 추가함.
      • ProjectileMovement 컴포넌트를 사용함.
        투사체의 기본 이동, 타겟을 향해 Homing, 중력 영향 등의 기능을 이용하기 위함.
    • GameplayAbility 클래스
      • 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 내 중요 함수들을 위의 코멘트와 같이 명시하고 있다.

        • CanActivateAbility(): GameplayAbility가 사용 가능한 상태인지를 bool로 반환한다.
        • TryActiateAbility(): AbilitySystemComponent 클래스의 함수이다.
          유효성 체크와 서버/클라이언트 관련 판별 후 InternalTryActivateAbility()를 호출한다.
          이를 통해 CanActivateAbility()를 호출하여 GameplayAbility가 사용 가능한 지 판별하고, 인스턴싱과 네트워크/예측(Prediction) 처리 후 모든 것이 잘 통과되면 CallActivateAbility()를 호출한다.
        • CallActivateAbility(): PreActivate() 호출 후 ActivateAbility()를 호출한다.
          PreActivate()는 ActivateAbility() 호출 이전 관용구(Boilerplate)스러운 처리를 하는 함수라고 한다.
        • ActivateAbility(): GameplayAbility가 실제로 적용할 효과를 정의하는 함수이다.
          GameplayAbility 자식 클래스에서 오버라이드하여 작성하면 된다.
          직접적으로 호출하는 것은 피하라고 되어 있으며, TryActivateAbility()와 같은 함수를 통해서 간접적으로 호출되어야 한다.
        • CommitAbility(): ActivateAbility()가 반드시 호출해야 하는 함수이며, GameplayAbility 사용에 소모된 자원, 쿨타임 등을 커밋하는 것으로 보인다.
        • CancelAbility(): 외부에 의해서 GameplayAbility가 취소되는 경우를 위한 함수이다.
          Instanced-per-execution인 경우는 GameplayAbility 오브젝트가 파괴되며, Instance-per-actor인 경우는 GameplayAbility를 리셋해주어야 한다.
        • EndAbility(): GameplayAbility의 지속 시간이 다 되거나, 효과 적용을 마친 등 스스로를 종료해야 할 때 호출해야 하는 함수이다.
      • 이 외에도 Input, Animation, Ability Levels and Source Object, Interaction with Ability System Component 등의 코멘트가 있으니 참고하면 좋다.

    • AuraProjectileSpell - Spawn Projectile
      • 모든 투사체 발사 GameplayAbility들의 기본형이 될 클래스.
        // 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);
        	}
        }
        • ICombatInterface의 활용을 눈여겨 보면 된다.
          • 액터는 레벨 상에 위치하는 것으로, 스폰하려면 항상 Transform을 제공해야 한다.
            이 때 투사체는 캐릭터의 손이나 무기에서 발사되는 것이 일반적이다.
            따라서 캐릭터 무기의 끝에서 투사체가 발사되도록 일단 구현해 볼 수 있겠다.
            • 모든 전투 가능한 캐릭터들은 ICombatInterface를 상속하므로, ICombatInterface에 GetCombatSocketLocation() 함수를 정의하고,
              AuraCharacterBase에서 해당 함수를 상속하여 무기 스켈레탈 메시에서 주어진 이름에 해당하는 소켓 위치를 반환하도록 했다.
          • SpawnActorDeferred()
            • 액터를 스폰하기 위한 함수로, SpawnActor()와 비슷하지만, 별도로 FinishSpawn()을 호출해주어야만 완전히 스폰이 끝난다는 차이가 있다.
            • 이를 통해, 예를 들어 투사체를 호출하는 경우, 해당 투사체의 GameplayEffectSpec을 설정하거나, 아니면 투사체의 ProjectileMovementComponent에 Homing 기능을 사용하기 위해 유도 대상을 설정하는 등의 별도의 처리를 해 줄 수 있을 것이다.
profile
저는 게임 개발자로 일하고 싶어요

0개의 댓글