일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- DSP
- Unreal Engine
- sampling theory
- Double free
- TSet
- Rr
- 게임개발
- 메카님
- Race condition
- 운영체제
- dtft
- 언리얼엔진
- 게임 개발
- CTF
- frequency-domain spectrum analysis
- ability task
- 언리얼 엔진
- AINCAA
- DP
- gas
- linear difference equation
- stride
- MLFQ
- 유니티
- ret2libc
- Security
- dirty cow
- pdlc
- 유스케이스
- MAC
- Today
- Total
다양한 기록
[Unreal GAS] Implementing Attack Hit Detect System 본문
[Unreal GAS] Implementing Attack Hit Detect System
라구넹 2025. 1. 13. 00:58
일단 애니메이션 노티파이 하나 생성
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "GameplayTagContainer.h"
#include "AnimNotify_GASAttackHitCheck.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLEGAS_API UAnimNotify_GASAttackHitCheck : public UAnimNotify
{
GENERATED_BODY()
public:
UAnimNotify_GASAttackHitCheck();
protected:
virtual FString GetNotifyName_Implementation() const override;
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override;
protected:
UPROPERTY(EditAnywhere)
FGameplayTag TriggerGameplayTag;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Animation/AnimNotify_GASAttackHitCheck.h"
#include "AbilitySystemBlueprintLibrary.h"
UAnimNotify_GASAttackHitCheck::UAnimNotify_GASAttackHitCheck()
{
}
FString UAnimNotify_GASAttackHitCheck::GetNotifyName_Implementation() const
{
return TEXT("GASAttackHitCheck");
}
void UAnimNotify_GASAttackHitCheck::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
Super::Notify(MeshComp, Animation, EventReference);
if ( MeshComp )
{
AActor* OwnerActor = MeshComp->GetOwner();
if ( OwnerActor )
{
FGameplayEventData PayloadData;
UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(OwnerActor, TriggerGameplayTag, PayloadData);
}
}
}
SendGameplayEventToActor
다른 액터한테 태그 날려서 이벤트를 발생시킬 수가 있음
* 해당 모듈보다 위에 있는 모듈의 코드에서 노티파이 사용 불가 주의
노티파이 설정
태그 만들고
각 노티파이에 태그 설정
저 태그를 트리거로 사용할 것임
// Fill out your copyright notice in the Description page of Project Settings.
#include "GA/ABGA_AttackHitCheck.h"
#include "ArenaBattleGAS.h"
UABGA_AttackHitCheck::UABGA_AttackHitCheck()
{
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
}
void UABGA_AttackHitCheck::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
ABGAS_LOG(LogABGAS, Log, TEXT("Begin"));
bool bReplicatedEndAbility = true;
bool bWasCancelled = false;
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled);
}
게임 어빌리티
이걸 트리거로 발생시키려면?
위의 게임어빌리티 상속해서 블루프린트로 만들고 트리거 태그에 추가
그리고 캐릭터 어빌리티에 추가
다음은 어빌리티 태스크로 판정 만들어주기
ABAT_Trace
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "ABAT_Trace.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FTraceResultDelegate);
/**
*
*/
UCLASS()
class ARENABATTLEGAS_API UABAT_Trace : public UAbilityTask
{
GENERATED_BODY()
public:
UABAT_Trace();
UFUNCTION(BlueprintCallable, Category = "Ability|Tasks", meta = (DisplayName = "JumpAndWaitForLanding", HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE"))
static UABAT_Trace* CreateTask(UGameplayAbility* OwningAbility);
virtual void Activate() override;
virtual void OnDestroy(bool AbilityEnded) override;
UPROPERTY(BlueprintAssignable)
FTraceResultDelegate OnComplete;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "GA/AT/ABAT_Trace.h"
UABAT_Trace::UABAT_Trace()
{
}
UABAT_Trace* UABAT_Trace::CreateTask(UGameplayAbility* OwningAbility)
{
UABAT_Trace* NewTask = NewAbilityTask<UABAT_Trace>(OwningAbility);
return NewTask;
}
void UABAT_Trace::Activate()
{
Super::Activate();
if ( ShouldBroadcastAbilityTaskDelegates() )
{
OnComplete.Broadcast();
}
EndTask();
}
void UABAT_Trace::OnDestroy(bool AbilityEnded)
{
Super::OnDestroy(AbilityEnded);
}
기본 골격만 만든 상태
* 게임플레이 어빌리티 타겟 액터
- 게임플레이 어빌리티에서 대상에 대한 판정(주로 물리 판정)을 구현할 때 사용하는 특수한 액터
- 줄여서 TA라고 함
- AGameplayAbilityTargetActor 클래스를 상속받아 구현
- 왜 TA가 필요한가?
- 타겟을 설정하는 다양한 방법이 있음
- Trace를 사용해 즉각적으로 타겟을 검출하는 방법
- 사용자의 최종 확인을 한번 더 거치는 방법이 있음 (ex. 원거리 범위 공격)
- 공격 범위 확인을 위한 추가 시각화 (시각화를 수행하는 액터를 월드레티클(WorldReticle)이라고 함)
- 주요 함수
- StartTargeting : 타겟팅을 시작
- ConfirmTargetingAndContinue : 타겟팅을 확정하고 이후 남은 프로세스를 진행
- ConfirmTargeting : 태스크 진행 없이 타겟팅만 확정
- CancleTargeting : 타겟팅을 취소
* 게임플레이 어빌리티 타겟 데이터
- 타겟 액터에서 판정한 결과를 담은 데이터
- 다음의 속성을 가지고 있음
- Trace 히트 결과 (HitResult)
- 판정된 다수의 액터 포인터
- 시작 지점
- 끝 지점
- 타겟 데이터를 여러 개 묶어 전송하는 것이 일반적인데 이를 타겟 데이터 핸들이라고 함
타겟 액터 생성
즉각적인 판정만 낼 거라 Cancel은 안하고 StartTargeting과 ConfirmTargeting만 구현
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Abilities/GameplayAbilityTargetActor.h"
#include "ABTA_Trace.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLEGAS_API AABTA_Trace : public AGameplayAbilityTargetActor
{
GENERATED_BODY()
public:
AABTA_Trace();
virtual void StartTargeting(UGameplayAbility* Ability) override;
virtual void ConfirmTargetingAndContinue() override;
void SetShowDebug(bool InShowDebug) { bShowDebug = InShowDebug; }
protected:
virtual FGameplayAbilityTargetDataHandle MakeTargetData() const;
bool bShowDebug = false;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "GA/TA/ABTA_Trace.h"
#include "Abilities/GameplayAbility.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
#include "Physics/ABCollision.h"
#include "DrawDebugHelpers.h"
AABTA_Trace::AABTA_Trace()
{
}
void AABTA_Trace::StartTargeting(UGameplayAbility* Ability)
{
Super::StartTargeting(Ability);
SourceActor = Ability->GetCurrentActorInfo()->AvatarActor.Get();
}
void AABTA_Trace::ConfirmTargetingAndContinue()
{
if ( SourceActor )
{
FGameplayAbilityTargetDataHandle DataHandle = MakeTargetData();
TargetDataReadyDelegate.Broadcast(DataHandle);
}
}
FGameplayAbilityTargetDataHandle AABTA_Trace::MakeTargetData() const
{
ACharacter* Character = CastChecked<ACharacter>(SourceActor);
FHitResult OutHitResult;
const float AttackRange = 100.0f;
const float AttackRaduis = 50.0f;
FCollisionQueryParams Params(SCENE_QUERY_STAT(UABAT_Trace), false, Character);
const FVector Forward = Character->GetActorForwardVector();
const FVector Start = Character->GetActorLocation() + Forward * Character->GetCapsuleComponent()->GetScaledCapsuleRadius();
const FVector End = Start + Forward * AttackRange;
bool HitDetected = GetWorld()->SweepSingleByChannel(OutHitResult, Start, End, FQuat::Identity, CCHANNEL_ABACTION, FCollisionShape::MakeSphere(AttackRaduis), Params);
FGameplayAbilityTargetDataHandle DataHandle;
if ( HitDetected )
{
FGameplayAbilityTargetData_SingleTargetHit* TargetData = new FGameplayAbilityTargetData_SingleTargetHit(OutHitResult);
DataHandle.Add(TargetData);
}
#if ENABLE_DRAW_DEBUG
if ( bShowDebug )
{
FVector CapsuleOrigin = Start + (End - Start) * 0.5f;
float CapsultHalfHeight = AttackRange * 0.5f;
FColor DrawColor = HitDetected ? FColor::Green : FColor::Red;
DrawDebugCapsule(GetWorld(), CapsuleOrigin, CapsultHalfHeight, AttackRaduis, FRotationMatrix::MakeFromZ(Forward).ToQuat(), DrawColor, false, 5.0f);
}
#endif
return DataHandle;
}
타겟 액터
어빌리티 태스크에서 타겟 액터를 만들고 StartTargeting이랑 ConfirmTargetingAndContinue 호출할 것임
TargetDataReadyDelegate는 타겟 액터 기본 제공 델리게이트
타겟 데이터 생성이 끝나고 브로드캐스트 해주면 됨
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "ABAT_Trace.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FTraceResultDelegate, const FGameplayAbilityTargetDataHandle&, TargetDataHandle);
/**
*
*/
UCLASS()
class ARENABATTLEGAS_API UABAT_Trace : public UAbilityTask
{
GENERATED_BODY()
public:
UABAT_Trace();
UFUNCTION(BlueprintCallable, Category = "Ability|Tasks", meta = (DisplayName = "JumpAndWaitForLanding", HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE"))
static UABAT_Trace* CreateTask(UGameplayAbility* OwningAbility, TSubclassOf<class AABTA_Trace> TargetActorClass);
virtual void Activate() override;
virtual void OnDestroy(bool AbilityEnded) override;
void SpawnAndInitializeTargetActor();
void FinalizeTargetActor();
UPROPERTY(BlueprintAssignable)
FTraceResultDelegate OnComplete;
protected:
void OnTargetDataReadyCallback(const FGameplayAbilityTargetDataHandle& DataHandle);
UPROPERTY()
TSubclassOf<class AABTA_Trace> TargetActorClass;
UPROPERTY()
TObjectPtr<class AABTA_Trace> SpawnedTargetActor;
};
어빌리티 태스크 헤더
// Fill out your copyright notice in the Description page of Project Settings.
#include "GA/AT/ABAT_Trace.h"
#include "GA/TA/ABTA_Trace.h"
#include "AbilitySystemComponent.h"
UABAT_Trace::UABAT_Trace()
{
}
UABAT_Trace* UABAT_Trace::CreateTask(UGameplayAbility* OwningAbility, TSubclassOf<AABTA_Trace> TargetActorClass)
{
UABAT_Trace* NewTask = NewAbilityTask<UABAT_Trace>(OwningAbility);
NewTask->TargetActorClass = TargetActorClass;
return NewTask;
}
void UABAT_Trace::Activate()
{
Super::Activate();
SpawnAndInitializeTargetActor();
FinalizeTargetActor();
SetWaitingOnAvatar();
}
void UABAT_Trace::OnDestroy(bool AbilityEnded)
{
if ( SpawnedTargetActor )
{
SpawnedTargetActor->Destroy();
}
Super::OnDestroy(AbilityEnded);
}
void UABAT_Trace::SpawnAndInitializeTargetActor()
{
SpawnedTargetActor = Cast<AABTA_Trace>(GetWorld()->SpawnActorDeferred<AGameplayAbilityTargetActor>(TargetActorClass, FTransform::Identity, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn));
if (SpawnedTargetActor)
{
SpawnedTargetActor->SetShowDebug(true);
SpawnedTargetActor->TargetDataReadyDelegate.AddUObject(this, &UABAT_Trace::OnTargetDataReadyCallback);
}
}
void UABAT_Trace::FinalizeTargetActor()
{
UAbilitySystemComponent* ASC = AbilitySystemComponent.Get();
if ( ASC )
{
const FTransform SpawnTransform = ASC->GetAvatarActor()->GetTransform();
SpawnedTargetActor->FinishSpawning(SpawnTransform);
ASC->SpawnedTargetActors.Add(SpawnedTargetActor);
SpawnedTargetActor->StartTargeting(Ability);
SpawnedTargetActor->ConfirmTargeting();
}
}
void UABAT_Trace::OnTargetDataReadyCallback(const FGameplayAbilityTargetDataHandle& DataHandle)
{
if (ShouldBroadcastAbilityTaskDelegates())
{
OnComplete.Broadcast(DataHandle);
}
EndTask();
}
Activate에서 두 함수를 호출하고 대기시킴
void UABAT_Trace::SpawnAndInitializeTargetActor()
{
SpawnedTargetActor = Cast<AABTA_Trace>(GetWorld()->SpawnActorDeferred<AGameplayAbilityTargetActor>(TargetActorClass, FTransform::Identity, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn));
if (SpawnedTargetActor)
{
SpawnedTargetActor->SetShowDebug(true);
SpawnedTargetActor->TargetDataReadyDelegate.AddUObject(this, &UABAT_Trace::OnTargetDataReadyCallback);
}
}
SpawnAndInitializeTargetActor : 타겟 액터 스폰
타겟 액터의 TargetDataReadyDelegate 구독하여 OnTargetDataReadyCallback 바인딩
TargetDataReadyDelegate -> OnTargetDataReadyCallback
하면 OnComplete 브로드캐스트
TargetDataReadyDelegate -> OnComplete 로 이어짐
void UABAT_Trace::FinalizeTargetActor()
{
UAbilitySystemComponent* ASC = AbilitySystemComponent.Get();
if ( ASC )
{
const FTransform SpawnTransform = ASC->GetAvatarActor()->GetTransform();
SpawnedTargetActor->FinishSpawning(SpawnTransform);
ASC->SpawnedTargetActors.Add(SpawnedTargetActor);
SpawnedTargetActor->StartTargeting(Ability);
SpawnedTargetActor->ConfirmTargeting();
}
}
FinalizeTargetActor : FinishSpawning 후 ASC에 타겟 액터 등록시키기
그리고 StartTargeting, ConfirmTargeting 호출
이러면 MakeTargetData까지 호출되고 그렇게 만들어진 데이터 핸들을 인자로 델리게이트 브로드캐스트 시작
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Abilities/GameplayAbility.h"
#include "ABGA_AttackHitCheck.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLEGAS_API UABGA_AttackHitCheck : public UGameplayAbility
{
GENERATED_BODY()
public:
UABGA_AttackHitCheck();
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
protected:
UFUNCTION()
void OnTraceResultCallback(const FGameplayAbilityTargetDataHandle& TargetDataHandle);
};
게임 어빌리티 헤더
// Fill out your copyright notice in the Description page of Project Settings.
#include "GA/ABGA_AttackHitCheck.h"
#include "ArenaBattleGAS.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "GA/AT/ABAT_Trace.h"
#include "GA/TA/ABTA_Trace.h"
UABGA_AttackHitCheck::UABGA_AttackHitCheck()
{
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
}
void UABGA_AttackHitCheck::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
ABGAS_LOG(LogABGAS, Log, TEXT("Begin"));
UABAT_Trace* AttackTraceTask = UABAT_Trace::CreateTask(this, AABTA_Trace::StaticClass());
AttackTraceTask->OnComplete.AddDynamic(this, &UABGA_AttackHitCheck::OnTraceResultCallback);
AttackTraceTask->ReadyForActivation();
}
void UABGA_AttackHitCheck::OnTraceResultCallback(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{
if ( UAbilitySystemBlueprintLibrary::TargetDataHasHitResult(TargetDataHandle, 0) )
{
FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(TargetDataHandle, 0);
ABGAS_LOG(LogABGAS, Log, TEXT("Target %s Detected"), *HitResult.GetActor()->GetName());
}
bool bReplicatedEndAbility = true;
bool bWasCancelled = false;
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled);
}
어빌리티 태스크 만들고 OnComplete에 콜백 바인딩
그리고 ReadyForActivation으로 대기
타겟 액터부터 쭉 델리게이트 타고 올라와서 OnTraceResultCallback 실행될 것
어빌리티 종료
* 태스크가 타겟 액터를 스폰하려면 타겟 액터 클래스를 알아야 해서 게임플레이 어빌리티도 타겟 액터 헤더 인클루드 해야 함
제대로 실행되는 걸 확인 가능
정리
Anim Notify로 AttackHitCheck 활성화
-> 어빌리티 발동
-> 어빌리티 태스크 생성
-> 타겟 액터 생성
-> 타겟 액터 종료 델리게이트 브로드캐스트
-> 어빌리티 태스크 종료 델리게이트 브로드캐스트
-> 어빌리티 HitResult 획득
'언리얼 엔진 > Unreal Ability System' 카테고리의 다른 글
[Unreal GAS] Character Attribute Setting (0) | 2025.01.13 |
---|---|
[Unreal GAS] Implementing Combo Action (0) | 2025.01.12 |
[Unreal GAS] Character Input Handling (0) | 2025.01.12 |
[Unreal GAS] 개요 (0) | 2025.01.11 |