1. GameplayCueManager
OnRangeWeaponTargetDataReady 처리는 GameplayCue Notify 형식으로 진행
- 이는 Blueprint로 구성됨
- 초기 코드 구축 과정 외에는, 거의 대부분을 Blueprint로 작업을 진행함

- GA_Weapon_Fire에 2가지 변수 추가
- GCN Parameters: 전달받은 FGameplayAbilityTargetDataHandle을 GameplayCue Notify에서 Gameplay Cue Parameters로 한 번 더 래핑
- Effect 정보를 담고 있는 객체
- 다방면에서 자주 발생하는 Effect를 통일된 규격으로 다루게 위함
- 즉 Tag를 이용해 Effect를 발생시키는 방식으로 사용

- C++에서 선언한 함수를 override한 블루프린트 이벤트 노드가 시작점

- TargetData로부터 피격 대상을 GameplayCue Parameter로 가져와 GCN Parameters로 캐싱
- 그 후 캐싱한 GCN Parameters 정보를 이용해(위치, 회전값 등) Tag에 맞는 GameplayCue가 발동
- 이 때 내부적으로 Owner로부터 ASC를 가져와 CueManager를 발동시킴
- 이러한 CueManager는 내부적으로 Map을 들고 있어, 해당 Map에 Tag가 등록된 후 필요할 때 여기서 찾아 발동시킴


- 상속받을 수 있는 CueNotify 종류가 다양함
- Looping: 지속 데미지
- Burst: 단발성 데미지

- CueNotify를 생성하면 반드시 GameplayCueManager에 스캔 대상으로 지정해줘야 함
- 이는 수동으로도 가능하나 번거러움
- 대신 Action을 이용함
- 이를 위해 2가지 클래스를 생성함
- GameFeatureAction_AddGameplayCuePath
- LyraGameFeaturePolicy
#pragma once
#include "CoreMinimal.h"
#include "GameFeatureAction.h"
#include "GameFeatureAction_AddGameplayCuePath.generated.h"
// WorldActionBase를 상속받지 않음 -> 모든 월드에서 알고 있어야 하기 때문
UCLASS()
class UGameFeatureAction_AddGameplayCuePath : public UGameFeatureAction
{
GENERATED_BODY()
public:
UGameFeatureAction_AddGameplayCuePath();
// GameplayCueNotify 에셋 경로 추가
UPROPERTY(EditAnywhere, Category="GameFeature|GameplayCue")
TArray<FDirectoryPath> DirectoryPathsToAdd;
};
#include "GameFeatureAction_AddGameplayCuePath.h"
UGameFeatureAction_AddGameplayCuePath::UGameFeatureAction_AddGameplayCuePath()
: Super()
{
// Lyra에서는 해당 폴더에서 모든 GameplayCue를 관리함
// 우리의 경우 Weapon 폴더를 등록시키면 됨
DirectoryPathsToAdd.Add(FDirectoryPath{TEXT("/GameplayCues")});
}
- 현재 우리의 GameFeatureAction은 ExperienceManager의
OnExperienceFullLoadCompleted() 함수에서 Registering/Loading/Activating을 수행함
- GameplayCue의 경우 등록/활성화를 누가 수행하는가?
- 이전까지 정의한 Action은 해당 게임 Experience에 종속적
- 다만 GameplayCue는 Experience와 관계가 없으며, 등록/탐색 속도가 매우 느린 편에 속함
- 즉 기존 방식대로 ExperienceManager에서 수행하면 안됨
- Lyra에서는 Observer 패턴을 사용함
- 게임 Feature 등록(스캔) 시, 앞서 등록한 경로의 에셋을 자동으로 로드함
#pragma once
#include "GameFeatureStateChangeObserver.h"
#include "GameFeaturesProjectPolicies.h"
#include "LyraGameFeaturePolicy.generated.h"
UCLASS()
class ULyraGameplayFeaturePolicy : public UDefaultGameFeaturesProjectPolicies
{
GENERATED_BODY()
public:
ULyraGameplayFeaturePolicy(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/**
* GameFeaturesProjectPolicies Interfaces
*/
virtual void InitGameFeatureManager() override;
virtual void ShutdownGameFeatureManager() override;
// Observer로 등록한 객체 관리
// 아래 AddGameplayCuePaths가 한 예시
UPROPERTY(Transient)
TArray<TObjectPtr<UObject>> Observers;
};
UCLASS()
class ULyraGameFeature_AddGameplayCuePaths : public UObject, public IGameFeatureStateChangeObserver
{
GENERATED_BODY()
public:
/**
* IGameFeatureStateChangeObserver Interfaces
*/
virtual void OnGameFeatureRegistering(const UGameFeatureData* GameFeatureData, const FString& PluginName, const FString& PluginURL) override;
virtual void OnGameFeatureUnregistering(const UGameFeatureData* GameFeatureData, const FString& PluginName, const FString& PluginURL) override;
};
- 우리만의 GameFeature 서브 시스템 규칙을 만들고 싶다면
UDefaultGameFeatureProjectPolicies를 상속받아 구현하면 됨
- GameFeatureManager 초기화/삭제 시 수행할 기능, 플러그인 Cook 기능 등 다양한 함수를 overriding 받을 수 있음
- 추가로 Observer 패턴을 적용함
IGameFeatureStateChangeObserver 라는 인터페이스를 상속받아 구현
- GameFeature 등록/로딩/해제/활성화/비활성화 시 등 다양한 시점에서의 기능을 정의할 수 있음
- AddGameplayCuePaths는 게임 피쳐가 등록/등록 해제가 될 때마다 알림을 받게 설정
- 알림을 받도록 등록시켜주는 역할이 Policy
ULyraGameplayFeaturePolicy::ULyraGameplayFeaturePolicy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void ULyraGameplayFeaturePolicy::InitGameFeatureManager()
{
Observers.Add(NewObject<ULyraGameFeature_AddGameplayCuePaths>());
UGameFeaturesSubsystem& Subsystem = UGameFeaturesSubsystem::Get();
for(UObject* Observer : Observers)
{
Subsystem.AddObserver(Observer);
}
// 위에서 Observer를 등록했으므로, GameFeatureManager가 초기화되는 과정에 반영됨
Super::InitGameFeatureManager();
}
void ULyraGameplayFeaturePolicy::ShutdownGameFeatureManager()
{
Super::ShutdownGameFeatureManager();
UGameFeaturesSubsystem& Subsystem = UGameFeaturesSubsystem::Get();
for(UObject* Observer : Observers)
{
Subsystem.RemoveObserver(Observer);
}
}
- Policy가
AddGameplayCuePaths를 옵저버 리스트에 추가한 뒤, 리스트 내 옵저버들을 모두 GameFeatures 서브 시스템에 등록시키는 과정을 수행함
- UGameFeaturesSubsystem은 UEngineSubsystem을 상속받으므로 엔진이 켜지기 전, 즉 매우 이른 시점에 해당 과정을 수행함
- GameplayCue는 AssetManager와 GameplayCueManager 2군데에 등록돼야 함
- CueManager: 게임 플레이 태그를 이용해 적절한 Cue를 가져오는 용도
- AssetManager: Cue는 결국 UObject를 상속받는, 메모리에 올라가는 객체이므로 AssetManager에 등록해야 함
- 이 둘에 한 번에 등록시키는 시스템을 제작함

- 우선 GameplayCueManager를 생성