https://github.com/tranek/GASDocumentation#concepts-ga
GitHub - tranek/GASDocumentation: My understanding of Unreal Engine 5's GameplayAbilitySystem plugin with a simple multiplayer s
My understanding of Unreal Engine 5's GameplayAbilitySystem plugin with a simple multiplayer sample project. - GitHub - tranek/GASDocumentation: My understanding of Unreal Engine 5's Gamepl...
github.com
단순히 위의 문서를 보고 제멋대로 해석해서 적은 것입니다. 차라리 위의 문서를 봐주세요. 저 영어 못 합니다.
4.11 Targeting
4.11.1 Target Data
FGameplayAbilityTargetData는 네트워크를 통해 전달되는 데이터로 targeting data의 일반적인 구조체이다. TargetData 일반적으로 AActor/UObject의 레퍼런스, FHitResult 그리고 다른 포괄적인 location/direction/origin information을 가지고 있다. 그러나 하위클래스를 만들어서 넣고 싶은 어느것이든 넣으면된다(simple means to pass data between the client and server in GameplayAbilities). 기본 FGameplayAbilityTargetData 구조체는 직접 사용하는 것이 아니라 서브클래스로 사용한다. GAS는 GameplayAbilityTargetTypes.h안에 있는 몇 가지 FGameplayAbilityTargetData 하위 구조체와 함께 제공된다.
TargetData는 일반적으로 Target Actors에 의해 성성되거나 수동으로 생성되며 EffectContext를 통해 어빌리티 태스크와 게임플레이 이펙트에 의해 소비된다. EffectContext에 포함된 결과, Executions, MMCs, GameplayCues 그리고 어트리뷰트 셋의 백엔드에 있는 함수는 TargetData에 액세스할 수 있다.
우리는 일반적으로 FGameplayAbilityTargetData를 직접 전달하지 않는다. 대신, 내부적으로 FGameplayAbilityTargetData의 포인터의 TArray를 가지고 있는 FGameplayAbilityTargetDataHandle을 사용한다. 이 중간 구조체는 TargetData의 다형성을 지원한다.
FGameplayAbilityTargetData에서 상속하는 예:
USTRUCT(BlueprintType)
struct MYGAME_API FGameplayAbilityTargetData_CustomData : public FGameplayAbilityTargetData
{
GENERATED_BODY()
public:
FGameplayAbilityTargetData_CustomData()
{ }
UPROPERTY()
FName CoolName = NAME_None;
UPROPERTY()
FPredictionKey MyCoolPredictionKey;
// This is required for all child structs of FGameplayAbilityTargetData
virtual UScriptStruct* GetScriptStruct() const override
{
return FGameplayAbilityTargetData_CustomData::StaticStruct();
}
// This is required for all child structs of FGameplayAbilityTargetData
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
// The engine already defined NetSerialize for FName & FPredictionKey, thanks Epic!
CoolName.NetSerialize(Ar, Map, bOutSuccess);
MyCoolPredictionKey.NetSerialize(Ar, Map, bOutSuccess);
bOutSuccess = true;
return true;
}
}
template<>
struct TStructOpsTypeTraits<FGameplayAbilityTargetData_CustomData> : public TStructOpsTypeTraitsBase2<FGameplayAbilityTargetData_CustomData>
{
enum
{
WithNetSerializer = true // This is REQUIRED for FGameplayAbilityTargetDataHandle net serialization to work
};
};
핸들에 타겟 데이터를 넣기위해:
UFUNCTION(BlueprintPure)
FGameplayAbilityTargetDataHandle MakeTargetDataFromCustomName(const FName CustomName)
{
// Create our target data type,
// Handle's automatically cleanup and delete this data when the handle is destructed,
// if you don't add this to a handle then be careful because this deals with memory management and memory leaks so its safe to just always add it to a handle at some point in the frame!
FGameplayAbilityTargetData_CustomData* MyCustomData = new FGameplayAbilityTargetData_CustomData();
// Setup the struct's information to use the inputted name and any other changes we may want to do
MyCustomData->CoolName = CustomName;
// Make our handle wrapper for Blueprint usage
FGameplayAbilityTargetDataHandle Handle;
// Add the target data to our handle
Handle.Add(MyCustomData);
// Output our handle to Blueprint
return Handle
}
값을 얻기위해서는 타입 안전을 체크가 필요하다. 왜냐하면 핸들의 타겟 데이터로부터 값을 얻기위한 단 한가지 방법은 일반적인 C/C++을 사용하여 캐스팅을 하는 방법으로 타겟이 일치하는지 에크하여 object slicing과 crash가 발생하지 않도록 해야한다. 타입 확인을 위한 여러개의 방법이 있다:
- Gameplay Tag(s): 특정 코드 아키텍처의 기능이 발생할 때마다 하위 클래스 계층을 사용할 수 있다. 당신은 베이스 부모의 타입에 캐스트할 수 있고, 그것의 게임플레이 태그를 얻을 수 있으며 그런 다음 상속된 클래스에 대한 캐스팅을 위한 것과 비교할 수 있다.
- Script Struct & Static Structs: 태그 대신에 바로 클래스를 비교할 수 있다(IF 문을 많이 포함허가 템플릿 함수를 만들어서), 밑은 이에 대한 예시이지만 기본적으로 당신은 any FGameplayAbilityTargetData(이건 USTRUCT이며 GetScriptStruct에서 구조체 타입을 지정하기 위해 상속된 클래스가 필요하다는 좋은 이점이 있다)로부터 스크립트 구조체를 얻을 수 있다. 그리고 찾고자하는 타입과 비교한다. 밑은 타입 체크를 위한 이 함수를 사용하는 예시이다:
UFUNCTION(BlueprintPure)
FName GetCoolNameFromTargetData(const FGameplayAbilityTargetDataHandle& Handle, const int Index)
{
// NOTE, there is two versions of this '::Get(int32 Index)' function;
// 1) const version that returns 'const FGameplayAbilityTargetData*', good for reading target data values
// 2) non-const version that returns 'FGameplayAbilityTargetData*', good for modifying target data values
FGameplayAbilityTargetData* Data = Handle.Get(Index); // This will valid check the index for you
// Valid check we have something to use, null data means nothing to cast for
if(Data == nullptr)
{
return NAME_None;
}
// This is basically the type checking pass, static_cast does not have type safety, this is why we do this check.
// If we don't do this then it will object slice the struct and thus we have no way of making sure its that type.
if(Data->GetScriptStruct() == FGameplayAbilityTargetData_CustomData::StaticStruct())
{
// Here is when you would do the cast because we know its the correct type already
FGameplayAbilityTargetData_CustomData* CustomData = static_cast<FGameplayAbilityTargetData_CustomData*>(Data);
return CustomData->CoolName;
}
return NAME_None;
}
4.11.2 Target Actors
게임플레이 어빌리티는 타겟 액터를 WaitTargetData Ability Task로 스폰하며, 월드에서 시각화하고 타겟팅 정보를 캡쳐한다. 타겟 액터는 선택적으로 현재 타겟을 보여주기 위해서 GameplayAbilityWorldReticles를 사용할 수도 있다. 확인이 되면 타겟팅 정보는 타겟 데이터로 반환되며, 이 정보는 게임플레이 이펙트로 전달될 수 있다.
타겟 액터는 AActor를 베이스로 하고있다. 그러므로 스태틱 메시나 데칼로 어디에, 어떻게 타겟팅하고 있는지 눈에 보이는 컴포넌트를 가질 수 있다. 스태틱 메시는 캐릭터가 빌드할 오브젝트의 위치를 사각화할 때 사용될 수 있다. 데칼은 땅의 이펙트 영역을 표현하는데 사용될 수 있다.
샘플 프로젝트에선 메테오 어빌리티의 이펙트의 데미지 영역을 땅에 표현하기 위해 AGameplayAbilityTargetActor_GroundTrace를 데칼과 사용한다. 그들은 또한 아무것도 보여줄 필요가 없다. For example it wouldn't make sense to display anything for a hitscan gun that instantly traces a line to its target as used in GASShooter.
타겟 액터는 기본적인 트레이스나 콜리전 오버랩을 이용하여 타겟팅 정보를 캡쳐하고 타겟 액터 구현에 따라 결과를 FHitResult 또는 AActor 배열로 타겟 데이터를 변환한다. aitTargetData Ability Task는 TEnumAsByte<EGameplayTargetingConfirmation::Type> ConfirmationType의 파라미터에 따라 언제 타겟이 확인되는지 결정한다.
TEnumAsByte<EGameplayTargetingConfirmation::Type>::Instant를 쓰지 않는다면, 타겟 데이터는 Tick()에서 트레이스와 오버랩을 기본적으로 수행하고 태겟 액터의 구현에따라 액터의 위치를 FHitResult로 업데이트 한다. 이런 과정이 Tick()의 트레이스와 오버랩에서 일어나지만 일반적으로 끔찍한 정도는 아니다. 왜냐하면 이것은 리플리케이트되지 않으며 당신은 일반적으로 타겟 액터를 한번의 실행에 하나 이상은 사용하지 않기 때문이다(하지만 여러개 있을 수도 있다). 단지 Tick()과 몇가지 복잡한 타겟 액터를 사용한다면 GASShooter의 로켓 런처의 2번째 능력처럼 많은 것을 한다는 것만 알아둬라. Tick()에서의 트레이스는 클라이언트에 아주 빠르게 반응하지만 트레이스 수행의 히트가 너무 많다면 타겟 액터의 tick rate를 줄이는 것도 고려하는게 좋다.
TEnumAsByte<EGameplayTargetingConfirmation::Type>::Instant를 사용하는 케이스에서는, 타겟 액터는 즉시 스폰하고 타겟 데이터를 생성하고 파괴한다. Tick()은 절대로 호출되지 않는다.
EGameplayTargetingConfirmation::Type | When targets are confirmed |
Instant | 타겟팅이 특별한 로직이나 유저 인풋 없이 즉각적으로 실행되며 언제 '발사'하는지를 결정한다. |
UserConfirmed | 타겟팅이 유저의 결정에 따라 실행된다. 어빌리티에 바운딩된 Confim 인풋이나 UAbilitySystemComponent::TargetConfirm()을 호출하여 실행한다. 타겟 액터는 취소 인풋이나 UAbilitySystemComponent::TargetCancle()에 의해 취소할 수 있다. 샘플 프로젝트의 메테오 어빌리티에서 사용한다. |
Custom | 게임플레이 타겟팅 어빌리티는 UGameplayAbility::ConfirmTaskByInstanceName()을 호출하여 타겟팅 데이터가 언제 준비되는지 결정하는 역할을 한다. 타겟 액터도 UGameplayAbility::CancleTaskByInstanceName()에 응답하여 타겟팅을 취소한다. |
CustomMulti | 게임플레이 타겟팅 어빌리티는 UGameplayAbility::ConfirmTaskByInstanceName()을 호출하여 타겟팅 데이터가 언제 준비되는지 결정하는 역할을 한다. 타겟 액터도 UGameplayAbility::CancleTaskByInstanceName()에 응답하여 타겟팅을 취소한다. 데이터 생성 시 어빌리티 태스크를 종료해서는 안 된다. |
모든 EGameplayTargetingConfimation::Type이 모든 타겟 액터에 지원되는 것은 아니다. 예를 들어, AGameplayAbilityTargetActor_GroundTrace는 Instant Confirmation에서는 지원되지 않는다.
WaitTargetData 어빌리티 태스크는 AGameplayAbilityTargetActor 클래스를 파라미터로 받고, 어빌리티 태스크의 각 활성화에 대해 인스턴스를 생성하고 어빌리티 태스크가 종료되면 타겟 액터를 제거한다. WaitTargetDataUsingActor 어빌리티 태스크는 이미 생성된 TargetActor를 사용한다. 그러나 여전히 어빌리티 태스크가 종료되었을 때, 타겟 액터를 제거한다. 이 2가지 어빌리티 태스크 모두 각각의 사용에 대해 타겟 액터를 생성하거나, 새로 생성된 타겟 액터를 필요로 한다는 점에서 비효율적이다. 프로토타이핑에서는 훌륭하나, 포로덕션에서 자동 소총처럼 타겟 데이터를 지속적으로 생성하는 경우에는 최적화를 검토해야 한다. GASShooter는 AGameplayAbilityTargetActor의 커스텀 하위클래스와 새로운 WaitTargetDataWithReusableActor 어빌리티 태스크가 있으며, 타겟 액터를 파괴하지 않고 재사용할 수 있도록 처음부터 작성되었다.
타겟액터는 기본적으로 리플리케이트되지 않는다. 그러나, 만약 당신의 게임에서 로컬 플레이어가 어디를 타겟팅 하고 있는지 다른 플레이어에게 보여줘야 한다면 리플리케이트되도록 만들 수 있다. WaitTargetData 어빌리티 태스크에서 RPCs를 통해 서버와 소통하는 기본적인 기능을 가지고 있다. 만약 타겟 액터의 ShouldProduceTargetDataOnServer 프로퍼티가 false로 되어 있다면, 클라이언트는 UAbilityTask_WaitTargetData::OnTargetDataReadyCallback()안의 CallServerSetReplicatedTargetData()를 통해 확인에서 타겟 데이터를 서버에게 RPC한다. 만약 ShouldProduceTargetDataOnServer가 true라면, 클라이언트는 일반적인 확인 이벤트, EAilityGenericReplicatedEvent::GenericConfirm, RPC를 서버에 보낸다(UAbilityTask_WaitTargetData::OnTargetDataReadyCallback()안에서). 그리고 서버는 서버에서 데이터를 생성하기 위해 RPC를 수신할 때, 트레이스 또는 오버랩 검사를 한다. 만약 클라이언트가 타겟팅을 취소했다면, 일반적인 취소 이벤트, EAbilityGenericReplicatedEvent::GenericCancle, RPC를 서버에 보낸다(UAbilityTask_WaitTargetData::OnTargetDataReadyCallback()안에서).
보다시피, 타겟 액터와 WaitTargetData 어빌리티 태스크에는 많은 델리게이트가 있다. 타겟 액터는 타겟 데이터를 준비하거나 확인하거나 취소하는 델리게이트를 브로드캐스트하고 생성하는 입력에 응답한다. WaitTargetData는 타겟 액터의 타겟 데이터가 준비, 확인그리고 취소되는 델리게이트를 듣고, 그 정보를 다시 게임플레이 어빌리티와 서버에 전달한다. 만약 당신이 타겟 데이터를 서버에 보냈다면, 치트를 방지하기 위해 타겟 데이터가 올바른지 확인하기 위해 서버에서 유효성 검사를 수행할 수 있다. 서버에서 타겟 데이터를 바로 생성하면 이 문제를 완전히 피할 수 있지만, 소유 클라이언트에 대한 잘못된 예측이 생길 수도 있다.
사용하는 AGameplayAbilityTargetActor의 특정 하위 클래스에 따라, 다른 ExposeOnSpawn 파라미터가 WaitTargetData 어빌리티 태스크 노드에 표시된다. Some common parameters include: 메테오의 Wait Target Data
Common TargetActor Parameters | Definition |
Debug | true라면, 타겟 액터가 non-shipping builds에서 트레이스를 수행할 때마다, 트레이스와 오버랩의 디버그 정보를 그린다. 기억해라, non-Instant TargetActors는 Tick()에서 수행하며 디버그 드로우도 Tick()에서 발생한다. |
Filter | [Optional] 트레이스와 오버랩이 발생할 때, 타겟에서 액터를 걸러내기(제거) 위한 특수한 구조이다. 일반적인 사용처는 플레이어 폰을 필터하고 특정한 클래스의 타겟이 필요할 때이다. Target Data Filters를 더 보자. |
Reticle Class | [Optional] 타겟 액터가 생성할 AGameplayAbilityWorldReticle의 하위 클래스 |
Reticle Parameters | [Optional] Reticles를 구성한다. See Reticles |
Start Location | 트레이싱이 어디서 시작될 것인지를 담은 특별한 구조체이다. 일반적으로 이것은 플레이어의 뷰포인트, 무기의 머즐 혹은 폰의 위치이다. |
디폴트 타겟 액터 클래스의 경우, 트레이스나 오버랩에 직접 있을 때의 액터만 유효한 타겟이다. 만약 액터가 트레이스나 오버랩을 벗어났다면 (액터가 움직이거나 우리가 다른데를 봤을 때), 그 액터들은 더 이상 유효하지 않다. 만약 타겟 액터가 마지막으로 유효한 타겟(들)을 기억하길 원한다면, 커스텀 타겟 액터 클래스에 이 기능을 넣어야 한다. 나는 타겟 액터가 확인이나 취소를 받을 때까지 지속되기 때문에 이러한 타겟을 지속적인 타겟이라고 한다. 그 타겟 액터는 트레이스와 오버랩 안에서 새로운 유효한 타겟을 찾거나 대상이 더 이상 유효하지 않은지(제거) 찾는다. GASShooter는 지속적인 타겟을 로켓 런처의 2번째 어빌리티인 추적능력에 사용한다.
4.11.3 Target Data Filters
Make GameplayTargetDataFilter와 Make Filter Handle 노드 모두를 사용한다면, 플레이어 폰을 필터링 하거나 특정 클래스만 선택할 수 있다. 만약 더 발전된 필터링이 필요하다면 FGameplayTargetDataFilter의 하위클래스를 만들어 FIlterPassesForActor 함수를 재정의하면 된다.
USTRUCT(BlueprintType)
struct GASDOCUMENTATION_API FGDNameTargetDataFilter : public FGameplayTargetDataFilter
{
GENERATED_BODY()
/** Returns true if the actor passes the filter and will be targeted */
virtual bool FilterPassesForActor(const AActor* ActorToBeFiltered) const override;
};
그러나, 이것은 FGameplayTargetDataFilterHandle이 필요하기때문에 Wait Target Data 노드에 바로 사용할 수 없다. 하위 클래스를 허용하려면 새로운 커스텀 Make Filter Handle을 반드시 만들어야 한다.
FGameplayTargetDataFilterHandle UGDTargetDataFilterBlueprintLibrary::MakeGDNameFilterHandle(FGDNameTargetDataFilter Filter, AActor* FilterActor)
{
FGameplayTargetDataFilter* NewFilter = new FGDNameTargetDataFilter(Filter);
NewFilter->InitializeFilterContext(FilterActor);
FGameplayTargetDataFilterHandle FilterHandle;
FilterHandle.Filter = TSharedPtr<FGameplayTargetDataFilter>(NewFilter);
return FilterHandle;
}
4.11.4 Gameplay Ability World Reticles
AGameplayAbilityWorldReticles(Reticles)는 누구를 타겟팅하고 있는지 non-Instant confirmed 타겟 액터를 타겟팅할 때, 타겟팅을 시각화 해준다. 타겟 액터는 모든 Reticles에 대해 수명과 생성 파괴의 책임이 있다. Reticles는 액터이다. 그러므로 시각적인 표현을 위한 컴포넌트를 사용할 수 있다. GASShooter에서처럼 흔한 구현으로는 WidgetComponent를 사용하여 스크린 공간(언제나 플레이어의 카메라를 향한다)의 UMG 위젯에 표시하는 것이다. Reticles는 어느 액터에 그들이 올라와 있는지 모른다. 그러나, 커스텀 타겟 액터로 하위 클래스로 만들어 해당 기능을 사용할 수 있다. 타겟 액터는 일반적으로 타겟의 로케이션에 맞게 Reticle의 위치를 Tick()에서 업데이트한다.
GASShooter는 로켓 런처의 2번째 어빌리티의 추적 로켓의 타겟 락온에서 Reticle을 사용한다. 적들 위의 빨간 인디케이터가 Reticle이다. 비슷한 이미지의 하얀색은 런처의 조준선이다.
Reticles는 디자이너를 위해 편리한 BlueprintImplementableEvents가 제공된다.
/** Called whenever bIsTargetValid changes value. */
UFUNCTION(BlueprintImplementableEvent, Category = Reticle)
void OnValidTargetChanged(bool bNewValue);
/** Called whenever bIsTargetAnActor changes value. */
UFUNCTION(BlueprintImplementableEvent, Category = Reticle)
void OnTargetingAnActor(bool bNewValue);
UFUNCTION(BlueprintImplementableEvent, Category = Reticle)
void OnParametersInitialized();
UFUNCTION(BlueprintImplementableEvent, Category = Reticle)
void SetReticleMaterialParamFloat(FName ParamName, float value);
UFUNCTION(BlueprintImplementableEvent, Category = Reticle)
void SetReticleMaterialParamVector(FName ParamName, FVector value);
Reticles는 선택적으로 타겟 액터에서 제공하는 FWorldReticleParameters를 구성에 사용할 수 있다. 기본 구조는 FVector AOEScale 하나의 변수만 제공한다. 당신은 이 구조체로 하위 클래스를 만들 수 있지만 타겟 액터는 오직 베이스 구조체만 받는다. It seems a little short-sighted to not allow this to be subclassed with default TargetActors. 그러나, 만약 당신이 본인의 커스텀 타겟 액터를 만든다면, 커스텀 Reticle 매개변수 구조체를 제공할 수 있고, 이를 생성할 때 수동으로 AGameplayAbilityWorldReticles의 하위 클래스로 넘길 수 있다.
Reticles는 기본적으로 리플리케이트되지 않는다. 그러나, 만약 당신의 게임에서 로컬 플레이어가 어디를 타겟팅 하고 있는지 다른 플레이어에게 보여줘야 한다면 리플리케이트되도록 만들 수 있다. 예를 들어, 타겟을 트레이스 하기 위해 AGameplayAbilityTargetActor_SingleLineTrace를 사용하는 중이라면, Reticle은 트레이스가 적을 직접적으로 지날때만 나타날 것이다. 만약 당신이 다른 곳을 본다면, 그 적은 더이상 유효한 타겟이 아니고 Reticle은 없어질 것이다. 만약 당신이 마지막으로 유효한 타겟이 Reticle이 남아있길 원한다면, 타겟 액터가 마지막으로 유효한 타겟을 기억하고 Reticle은 거기에 남기기도록 커스터마이징하면 된다. 나는 타겟 액터가 확인이나 취소를 받을 때까지 지속되기 때문에 이러한 타겟을 지속적인 타겟이라고 한다. 그 타겟 액터는 트레이스와 오버랩 안에서 새로운 유효한 타겟을 찾거나 대상이 더 이상 유효하지 않은지(제거) 찾는다. GASShooter는 지속적인 타겟을 로켓 런처의 2번째 어빌리티인 추적능력에 사용한다.
4.11.5 Gameplay Effect Containers Targeting
GameplayEffectContainers는 선택적이고 효율적인 타겟 데이터 생성 수단을 제공한다. 이 타겟팅은 클라이언트와 서버에 EffectContainer가 적용될 때 즉시 발생한다. 이것은 Target Actors보다 더 효율적이다. 왜냐하면 이것은 타겟팅 오브젝트의 CDO(Class Default Object)에서 돌아간다(액터를 생성하거나 제거하지 않음). 그러나 플레이어 인풋이 부족하고, 확인할 필요 없이 즉시 발생하고, 취소할 수 없고 그리고 클라이언트로부터 서버에 데이터를 보낼 수 없다(둘 다 데이터를 생성할 수도 없음). Instant 트레이스와 콜리전 오버랩에선 잘 작동한다. 에픽의 ActionRPG 샘플 프로젝트는 타겟팅의 타입과 그것의 컨테이너의 예시 2가지를 포함한다 - target the ability owner and pull TargetData from an event. 또한 플레이어로부터 특정 오프셋(set by child blueprint classes)으로 인스턴스 스피어 트레이스를 수행하기 위한 블루프린트도 하나 구현되어 있다. 자신의 타겟팅 타입을 만들기 위해 C++이나 블루프린트에서 URPGTargetType의 하위 클래스를 만들 수 있다.
'Unreal Engine > Gameplay Ability System' 카테고리의 다른 글
[UE] Gameplay Ability System -9- Prediction (0) | 2023.06.03 |
---|---|
[UE] Gameplay Ability System -8- Abilty System Globals (0) | 2023.06.02 |
[UE] Gameplay Ability System -7- Gameplay Cue (0) | 2023.06.02 |
[UE] Gameplay Ability System -6- Ability Task (0) | 2023.05.30 |
[UE] Gameplay Ability System -5- Gameplay Ability (0) | 2023.05.30 |