플레이어 캐릭터의 ASC 설정
- 네트워크 멀티플레이 환경을 고려한다면 GAS에서도 데이터 보관 주체와 실제 캐릭터 조작 주체를 분리해야한다.
GAS에서 상태(State)는 결국 데이터로 관리되기 때문에 이러한 데이터를 누가 주도적으로 관리할 것인지 명확히 정하는 것이 중요하다. - 이때 PlayerState가 주기적으로 서버에서 클라이언트로 배포되는 Actor이기 때문에 가장 적합하며 실제 게임에서의 상호작용및 시각적 표현은 Pawn이 담당한다.
- 결국 PlayerState와 Pawn의 역할을 구분해주는 것이 필요하다.
- GAS에서 위를 감안하여 두 개로 나눠서 관리한다.
- ASC가 부착되며 GAS 프레임워크의 상태 및 데이터를 담당하는 Actor : OwnerActor
- 실제 게임에서 물체와 상호작용하는 비주얼적인 부분을 담당하는 Actor : AvatarActor - 결론적으로 플레이어 캐릭터를 생성할 때 GAS는 PlayerState에서 ASC를 생성하고
생성된 ASC 포인터를 캐릭터에 넘겨주는것이 바람직하다.
#include "Player/ABGASPlayerState.h"
#include "AbilitySystemComponent.h"
AABGASPlayerState::AABGASPlayerState()
{
//PlayerState에서 ASC생성
ASC = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("ASC"));
//네트워크 사용 시 컴포넌트가 리플리케이션되도록 함.
ASC->SetIsReplicated(true);
}
UAbilitySystemComponent* AABGASPlayerState::GetAbilitySystemComponent() const
{
return ASC;
}
위 코드와 같이 PlayerState에서 ASC를 생성해준다.
AABGASCharacterPlayer::AABGASCharacterPlayer()
{
//여기 Player에서도 ASC를 생성하면 데이터가 낭비 및 혼선이 생길 수 있다.
//그렇기에 의도적으로 nullptr로 설정한다.
ASC = nullptr;
}
UAbilitySystemComponent* AABGASCharacterPlayer::GetAbilitySystemComponent() const
{
return ASC;
}
void AABGASCharacterPlayer::PossessedBy(AController* NewController)
{
// ASC를 설정할 때 빙의가 될 때 호출되는 PossessedBy에서 실행한다.
Super::PossessedBy(NewController);
//PlayerState를 들고온다.
AABGASPlayerState* GASPS = GetPlayerState<AABGASPlayerState>();
if (GASPS)
{
//PlayerState에서 ASC를 들고와서 PlayerState에 있는 ASC를 Player 캐릭터 ASC에 넣어준다.
ASC = GASPS->GetAbilitySystemComponent();
//위 단계에서 OwnerActor는 PlayerState로 AvatarActor는 Player 캐릭터로 정해졌다.
//이제 ASC 초기화를 해주면 된다.
//첫번째 인자는 OwnerActor, 두번째 인자는 AvatarActor를 넣어주면 된다.
ASC->InitAbilityActorInfo(GASPS, this);
//StartAbilities Array에 있는 GameplayAbility를 ASC->GiveAbility하여 등록한다.
for (const auto& StartAbility : StartAbilities)
{
FGameplayAbilitySpec StartSpec(StartAbility);
ASC->GiveAbility(StartSpec);
}
}
}
PlayerCharacter는 위 코드와 같이 작성한다.
생성자에서 의도적으로 ASC를 null로 지정한것을 확인 가능하며
PossessecBy 단계에서 ASC PlayerState에서 받고 초기화하며 ASC에 Ability들을 등록하는 것을 확인 가능하다.
이제 입력 처리를 위해 게임플레이 어빌리티 스펙에 대해서 알아보자.
GameAbilitySpec
- 게임플레이 어빌리티에 대한 정보를 담고 있는 구조체
- ASC는 직접 어빌리티를 참조하지 않고 스펙 정보만 들고 있음.
- 스펙은 어빌리티의 현재 상태와 같은 다양한 정보를 가지고 있음.
- ASC로부터 어빌리티를 발동 및 중지 등 다루고자할 경우 스펙에 있는 Handle을 사용해 컨트롤한다.
- Handle값은 전역으로 설정되어 있으며 Spec생성 시 자동으로 1씩 증가한다. 기본값은 -1이다.
이 값은 현재 Handle이 잘못된 값인지 판단하는데 사용된다. - Ability에 대한 정보 : Spec
- Ability Instance에 대한 래퍼런스 : Spec Handle
Spec을 사용한 ASC의 입력 처리
- 게임 어빌리티 스펙에는 입력 값을 설정하는 필드 InputID가 제공된다.
- 사용자 입력이 들어오면 ASC에 등록된 Spec을 FindAbilitySpecFromInputID를 통해 입력에 매핑된 GA를 찾을 수 있다.
- 해당 GA를 발견하고 현재 발동중인지를 판별한다.
GA가 발동 중이면 입력이 들어왔다는 신호 전달 : AbilitySpecInputPressed
GA가 발동하지 않았으면 새롭게 발동 시킴 : TryActivateAbility - 입력이 떨어지면(Release) 동일하게 처리
GA가 입력이 떨어졌다는 신호를 전달 : AbilityuSpecInputReleased
Spec 입력처리 예시
void AABGASCharacterPlayer::PossessedBy(AController* NewController)
{
...
int32 InputId = 0;
for (const auto& StartAbility : StartAbilities)
{
FGameplayAbilitySpec StartSpec(StartAbility);
//InputId 정보를 Spec에 넣을 수 있다.
StartSpec.InputID = InputId++;
ASC->GiveAbility(StartSpec);
SetupGASInputComponent();
}
}
}
위처럼 GiveAbility할 게임 어빌리티 스펙에 Id를 넣어준다.
void AABGASCharacterPlayer::SetupGASInputComponent()
{
...
//Jump를 Trigger하거나 Completed하면 GASInputPressed & Released 함수 파라미터에 0이 들어간다.
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ThisClass::GASInputPressed, 0);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ThisClass::GASInputReleased, 0);
}
void AABGASCharacterPlayer::GASInputPressed(int32 InputId)
{
FGameplayAbilitySpec* Spec = ASC->FindAbilitySpecFromInputID(InputId);
if (Spec)
{
Spec->InputPressed = true;
if (Spec->IsActive()) //Ability가 이미 발동이 되었는지 확인한다.
{
//다시 눌러졌음을 알리기만한다.
ASC->AbilitySpecInputPressed(*Spec);
}
else
{
//어빌리티를 실행하고
//핸들을 사용해서 제어를 해야하기 때문에 Spec에 있는 Handle 값을 넣어준다.
ASC->TryActivateAbility(Spec->Handle);
}
}
}
void AABGASCharacterPlayer::GASInputReleased(int32 InputId)
{
FGameplayAbilitySpec* Spec = ASC->FindAbilitySpecFromInputID(InputId);
if (Spec)
{
Spec->InputPressed = false;
if (Spec->IsActive()) //Ability가 이미 발동이 되었는지 확인한다.
{
ASC->AbilitySpecInputReleased(*Spec);
}
}
}
이제 InputBind하는 함수이다.
BindAction에 마지막에 0이 들어가는 걸 확인 가능하다.
이걸 함수의 파라미터로 넘겨주면 GAS에서 FindAbilitySpecFromInputID를 통해 Id를 넣으면 어떤 GA인지 찾을 수 있다.
'Unreal 게임 개발 > Unreal 강의 개인 정리' 카테고리의 다른 글
| GA에서 AT 사용 방법 (+BP) - GAS (0) | 2025.08.20 |
|---|---|
| 공격 입력처리 & 인스턴싱 옵션 & 어빌리티 태스크 - GAS (3) | 2025.08.20 |
| Gameplay Tag - GAS (1) | 2025.08.19 |
| GAS 및 GA - GAS (0) | 2025.08.18 |
| Unreal GAS - 개요 (2) | 2025.08.17 |