
총을 발사하거나 재장전하는 등의 행동은 Aim을 한 상태에서만 가능하도록 해야합니다.
따라서 Aim을 하면 권한을 주고, Unaim을 하면 다시 권한을 뺏도록 해야합니다.
우선, GameAbility에서 각 능력을 Trigger 했을 때, 권한을 주거나 뺏을 자료형을 정의해야합니다.
USTRUCT(BlueprintType)
struct FPlayerWeaponData {
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
UInputMappingContext* WeaponMappingContext;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TArray<FPlayerAbilitySet> DefaultPlayerAbilities;
};
해당 구조체를 GA에 하나 선언해주고 값을 넣어줍니다.


Controller 로부터 EnhancedInputLocalPlayerSubsystem의 AddMappingContext 함수를 호출할 수 있습니다.
입력을 막을 때에는 반대로 RemoveMappingContext 를 사용할 수 있습니다.
void UPlayerAbilitySystemComponent::GrantHeroWeaponAbilities(const TArray<FPlayerAbilitySet>& InDefaultWeaponAbilities, int32 ApplyLevel, TArray<FGameplayAbilitySpecHandle>& OutGrantedAbilitySpecHandles)
{
if (InDefaultWeaponAbilities.IsEmpty()) return;
for (const FPlayerAbilitySet& AbilitySet : InDefaultWeaponAbilities) {
if (!AbilitySet.IsValid()) continue;
FGameplayAbilitySpec AbilitySpec(AbilitySet.AbilityToGrant);
AbilitySpec.SourceObject = GetAvatarActor();
AbilitySpec.Level = ApplyLevel;
AbilitySpec.DynamicAbilityTags.AddTag(AbilitySet.InputTag);
OutGrantedAbilitySpecHandles.AddUnique(GiveAbility(AbilitySpec));
}
}
void UPlayerAbilitySystemComponent::RemoveGrantedHeroWeaponAbilities(UPARAM(ref)TArray<FGameplayAbilitySpecHandle>& InSpecHandlesToRemove)
{
if (InSpecHandlesToRemove.IsEmpty()) return;
for (const FGameplayAbilitySpecHandle& SpecHandle : InSpecHandlesToRemove) {
if (!SpecHandle.IsValid()) continue;
ClearAbility(SpecHandle);
}
InSpecHandlesToRemove.Empty();
}
위의 함수는 저번 글에서 생략했던 GiveAbility 로 권한을 부여하는 부분입니다.
우선, GiveAbility를 호출하는데 필요한 AbilitySpec을 정의하고, 반환되는 FGameplayAbilitySpec 또한 배열에 저장합니다.
이를 저장하는 이유는, 이 정보를 토대로 다시 권한을 뺏을 수 있기 때문입니다.
아래의 함수는 권한을 뺏는 함수입니다.
위의 함수에서 저장해뒀던 배열을 가지고있다가, 아래의 함수를 호출할 때 파라미터로 전달하면 됩니다.
Ability의 권한을 주고/뺏을 수 있게 되었습니다.
이제 총을 조준하는 듯한 애니메이션을 실행할 수 있도록 캐릭터 스크립트에 bool 변수를 선언하고 설정해줍니다.

저는 bUseControllerRotationYaw 를 True로 바꾸어서 조준할때에는 방향을 움직여도 회전하지 않도록 했습니다.
이제 마지막으로 Zoom 하는 효과를 나타내겠습니다.
처음 캐릭터를 만들 때, CameraArmComponent 에 SocketOffset 을 설정했었습니다.
그렇기 때문에 ArmLength를 줄여도 어깨너머로 정면을 볼 수 있습니다.

우선 원만한 ZoomIn/Out을 위해 CurveFloat을 만들어주고, 이를 캐릭터에 넘겨줍니다.
Curve는 다음과 같이 사용할 수 있습니다.
/** Header File **/
protected:
UPROPERTY(EditAnywhere, Category="Camera")
UCurveFloat* CameraZoomCurve;
FTimeline ZoomTimeline;
UFUNCTION()
void ControlArm();
UFUNCTION(BlueprintCallable)
void ZoomInCamera();
UFUNCTION(BlueprintCallable)
void ZoomOutCamera();
private:
float TimelineValue;
float CurveFloatValue;
float PrevArmLength;
/** cpp File **/
void APlayerGunnerCharacter::BeginPlay()
{
Super::BeginPlay();
if (CameraZoomCurve) {
FOnTimelineFloat TimelineCallback;
FOnTimelineEvent TimelineFinishedCallback;
TimelineCallback.BindUFunction(this, FName("ControlArm"));
TimelineFinishedCallback.BindUFunction(this, FName("SetArm"));
ZoomTimeline.AddInterpFloat(CameraZoomCurve, TimelineCallback);
ZoomTimeline.SetTimelineFinishedFunc(TimelineFinishedCallback);
}
PrevArmLength = CameraBoom->TargetArmLength;
}
void APlayerGunnerCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
ZoomTimeline.TickTimeline(DeltaTime);
}
void APlayerGunnerCharacter::ControlArm()
{
TimelineValue = ZoomTimeline.GetPlaybackPosition();
CurveFloatValue = CameraZoomCurve->GetFloatValue(TimelineValue);
CameraBoom->TargetArmLength = MinArmLength + CurveFloatValue * (MaxArmLength - MinArmLength);
UE_LOG(LogTemp, Warning, TEXT("%f"), CurveFloatValue);
}
void APlayerGunnerCharacter::SetArm()
{
}
void APlayerGunnerCharacter::ZoomInCamera()
{
PrevArmLength = CameraBoom->TargetArmLength;
ZoomTimeline.ReverseFromEnd();
}
void APlayerGunnerCharacter::ZoomOutCamera()
{
PrevArmLength = CameraBoom->TargetArmLength;
ZoomTimeline.PlayFromStart();
}
BeginPlay 에서 콜백함수를 설정하고 해당 함수가 언제 실행될지 정합니다.
AddInterpFloat 에 들어간 TimelineCallback 에는 ControlArm 함수가 바인드 되었으므로, Float이 바뀔 때마다 ControlArm 이 호출되게 됩니다.
Aim과 비슷하게 Unaim, Fire, Reload GA도 간단하게 설정하고 실행해보겠습니다.

GA_Aim을 통해 여러 권한을 주고, 조준한 상태에서만 특정 어빌리티를 실행할 수 있도록 만들었습니다.
다음은 AttributeSet을 정의하고 간단한 적을 만들어 피가 닳도록 만들어보겠습니다.