언리얼 엔진5 Advanced - 캐릭터의 입력 처리

타입·2025년 4월 14일
0

언리얼 강의

목록 보기
40/47

플레이어의 ASC 설정

플레이어 캐릭터의 ASC 설정

분수대 액터와 같이 플레이어 캐릭터에 설정하는 것이 가능
하지만 네트워크 멀티플레이를 감안했을 때, 서버에서 클라이언트로 배포되는 액터가 보다 적합
이 때 많이 사용하는 액터가 주기적으로 플레이어 정보를 배포하는 PlayerState 액터
따라서 Owner를 PlayerState로 설정하고, Avatar를 Character로 설정하는 것이 일반적인 방법

GAS의 데이터 관리를 담당하는 액터를 Owner, 물체와 상호작용하며 비주얼을 담당하는 액터를 Avatar
PlayerState에서 ASC를 생성하여 포인터를 Character에 넘겨주는 것이 바람직함
이후에 발동하는 캐릭터의 어빌리티들은 대부분 캐릭터 중심으로 진행되지만, 어빌리티로 인한 결과는 ASC가 데이터로 보간하고 실시간으로 서버에서 클라이언트로 전송됨

플레이어의 입력 설정

게임플레이 어빌리티 스펙 (Spec)

게임플레이 어빌리티에 대한 정보를 담고 있는 구조체

ASC는 직접 어빌리티를 참조하지 않고 스펙 정보만 가지고 있음
스펙은 어빌리티의 현재 상태와 같은 다양한 정보를 가지고 있음
ASC로부터 어빌리티를 다루고자 할 경우 스펙에 있는 Handle을 사용해 컨트롤함
Handle 값은 전역으로 설정되어 있으며, 스펙 생성시 자동으로 1씩 증가함 (기본값 -1)

어빌리티 정보: 스펙
어빌리티 인스턴스에 대한 레퍼런스: 스펙 핸들

어빌리티 시스템 컴포넌트의 입력 처리

게임 어빌리티 스펙에는 입력 값을 설정하는 필드 InputID가 제공됨

ASC에 등록된 스펙을 검사해 입력에 매핑된 GA를 찾을 수 있음 - FindAbilitySpecFromInputID()

사용자 입력이 들어오면 ASC에서 입력에 관련된 GA를 검색함

해당 GA를 발견하면, 현재 발동 중인지를 판별
GA가 발동중이면 입력이 왔다는 신호를 전달 - AbilitySpecInputPressed()
GA가 발동하지 않았으면 새롭게 발동시킴 - TryActivateAbility()

입력이 떨어지면 동일하게 처리
GA에게 입력이 떨어졌다는 신호를 전달 - AbilitySpecInputReleased()

EnhancedInputComponent의 BindAction() 함수를 활용하면 범용적인 입력 처리가 가능해짐

UGameplayAbility_CharacterJump 클래스

// 어빌리티 활성화 시 점프
void UGameplayAbility_CharacterJump::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	...

		ACharacter * Character = CastChecked<ACharacter>(ActorInfo->AvatarActor.Get());
		Character->Jump();
}

// 점프가 가능한 상황인지 검사
bool UGameplayAbility_CharacterJump::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, OUT FGameplayTagContainer* OptionalRelevantTags) const
{
	if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags))
	{
		return false;
	}

	const ACharacter* Character = CastChecked<ACharacter>(ActorInfo->AvatarActor.Get(), ECastCheckedType::NullAllowed);
	return (Character && Character->CanJump());
}

// 입력이 떨어졌을 때 어빌리티 취소
void UGameplayAbility_CharacterJump::InputReleased(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo)
{
	if (ActorInfo != NULL && ActorInfo->AvatarActor != NULL)
	{
		CancelAbility(Handle, ActorInfo, ActivationInfo, true);
	}
}

UGameplayAbility_CharacterJump::UGameplayAbility_CharacterJump(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
	InstancingPolicy = EGameplayAbilityInstancingPolicy::NonInstanced;
}

플레이어의 공격 구현

게임플레이 어빌리티의 인스턴싱 옵션

상황에 따라 다양한 인스턴스 정책을 지정할 수 있음

  • NonInstanced
    인스턴싱 없이 CDO에서 일괄 처리
    (가장 가볍지만 상태가 부여되는 어빌리티엔 적합하지 않음)
  • InstancedPerActor
    액터마다 하나의 어빌리티 인스턴스를 만들어 처리
    (Primary Instance: InstancedPerExecution로 인스턴스가 여러개 생겼을 때 가장 먼저 생성된 것)
  • InstancedPerExecution
    발동시 인스턴스를 생산

네트워크 리플리케이션까지 고려했을 때 InstancedPerActor가 무난한 선택지임

NonInstanced 옵션은 UE 5.5 버전에서 DEPRECATED 되었습니다.
InstancedPerActor / InstancedPerExecution을 사용하여야 합니다.

UENUM(BlueprintType)
namespace EGameplayAbilityInstancingPolicy
{
	/**
	 *	How the ability is instanced when executed. This limits what an ability can do in its implementation. For example, a NonInstanced
	 *	Ability cannot have state. It is probably unsafe for an InstancedPerActor ability to have latent actions, etc.
	 */
	enum Type : int
	{
		// This ability is never instanced. Anything that executes the ability is operating on the CDO.
		NonInstanced UE_DEPRECATED_FORGAME(5.5, "Use InstancedPerActor as the default to avoid confusing corner cases"),

		// Each actor gets their own instance of this ability. State can be saved, replication is possible.
		InstancedPerActor,

		// We instance this ability each time it is executed. Replication currently unsupported.
		InstancedPerExecution,
	};
}

어빌리티 태스크(AT)의 활용

게임플레이 어빌리티의 실행(Activation)은 한 프레임에서 이루어짐

게임플레이 어빌리티가 시작되면 EndAbility() 함수가 호출되기까지는 끝나지 않음

애니메이션 재생 같이 시간이 소요되고 상태를 관리해야하는 어빌리티의 구현 방법
비동기적으로 작업을 수행하고 끝나면 결과를 통보받는 형태로 구현
이를 위해 GAS는 어빌리티 태스크를 제공하고 있음

  • 어빌리티 태스크의 활용 패턴
    1. 어빌리티 태스크에 작업이 끝나면 브로드캐스팅되는 종료 델리게이트를 선언
    2. GA는 AT를 생성한 후 바로 종료 델리게이트를 구독 - AddDynamic
    3. GA의 구독 설정이 완료되면 AT를 구동 - AT의 ReadyForActivation() 함수 호출
    4. AT의 작업이 끝나면 델리게이트를 구독한 GA의 콜백 함수가 호출
    5. GA의 콜백함수가 호출되면 GA의 EndAbility() 함수를 호출해 GA를 종료

GA는 필요에 따라 다수의 AT를 사용해 복잡한 액션 로직을 설계할 수 있음

GA의 블루프린트 상속 및 게임플레이 태그 설정

꼭 필요한 상황이 아니라면 GA와 AT는 가급적 자기 역할만 충실하게 구현하는 것이 좋음

게임플레이 태그를 C++에서 설정하는 경우 기획 변경때마다 소스코드 컴파일을 수행해야 함
게임플레이 태그 설정은 블루프린트에서 설정하는 것이 의존성 분리에 도움이 됨

  • 게임플레이 태그 설정 기획
    점프 GA의 ActivationOwnedTags에 Character.State.IsJumping 게임플레이 태그 설정
    공격 GA의 ActivationOwnedTags에 Character.State.IsAttacking 게임플레이 태그 설정

콘솔창에서 "ShowDebug AbilitySystem" 입력 시 현재 활성화 중인 태그 확인 가능

실습코드

https://github.com/dnjfs/ArenaBattle_GAS/commit/76292d6d396c2b07b64c6c6fc905c6a842e55e86

  • 입력 구현
    빙의 시 GAS와 관련된 입력을 ASC에 등록
    입력 처리 시 어빌리티 스펙의 InputId를 통해 어빌리티 검색

  • 점프 구현
    캐릭터에 점프 GA를 추가
    BindAction()으로 JumpAction 바인딩 후 입력이 들어오면 점프 GA 발동

  • 공격 구현
    공격 GA 구현 (ABGA_Attack)
    캐릭터에 공격 GA를 추가
    AT의 종료 델리게이트에 콜백 함수를 바인딩하여 애니메이션 종료 시 콜백 함수 호출
    EndAbility() 함수 호출하여 GA 종료

  • 캐릭터 상태 추가
    BPGA의 ActivationOwnedTags에 게임플레이 태그를 추가
    해당 GA가 활성화되었을 때 설정한 태그 활성화

profile
주니어 언리얼 프로그래머

0개의 댓글