본문 바로가기
포트폴리오 제작/Project_P

Project_P GE를 사용하여 데미지 전달 및 체력 감소

by k99812 2024. 10. 18.

데미지를 전달할 GameplayEffect 생성


 

GameplayEffect를 블루프린트로 상속받아 블루프린트 클래스를 생성한다

 

현재 기능은 단순히 캐릭터의 공격력 스탯(어트리뷰트)만큼 데미지를 전달하는 기능이기 때문에

Modifirers를 이용한다

 

좀더 복잡한 계산(방어력 등)이 필요하면 Executions을 이용하면 된다.

Executions는 GameplayEffectExecutionCalculation을 상속받아 구현할 수 있다

 

모디파이어 설정

  1. 수정할 어트리뷰트를 선택
  2. 연산자를 선택

 

  1. Magnitude Calculation Type에서 전달할 값을 어떤 방식으로 지정할 것인지 선택한다
    여기선 Attribute Based로 선택해 캐릭터의 어트리뷰트(스탯)를 가져온다
  2. Coefficient 항목에선 전달할 값을 변조(수정)할 수 있다
    위에선 커브테이블을 이용해 공격(콤보) 단계(1.0, 1.2, 1.5)가 오를수록 더욱 높은 데미지를 얻을 수 있다
    Miscellaneous 항목에서 커브테이블을 만들 수 있다
  3. Backing Attribute에선 가져올 어트리뷰트를 선택할 수 있다
  4. Attribute Calculation Type은 Base Value(기본 스탯), Magnitude(기본 스탯 + 추가 스탯), Bouns Magnitude(추가스탯)을 선택하여 어트리뷰트에서 어떤 값을 가져올건지 결정할 수 있다.

 

PPGA_AttackHitCheck GA에서 GE를 활성화 하여 타겟에 데미지 전달


헤더파일

	UPROPERTY(EditAnywhere, Category = "GAS")
	TSubclassOf<class UGameplayEffect> AttackDamageEffect;

	float CurrentLevel;
  • GE를 실행시킬 때 필요한 CurrentLevel, 생성할 GameplayEffect Class 정보를 저장할 변수를 헤더에 선언한다

 

Cpp파일

void UPPGA_AttackHitCheck::TraceResultCallback(const FGameplayAbilityTargetDataHandle& DataHandle)
{
	//#include "AbilitySystemBlueprintLibrary.h" 추가
	if (UAbilitySystemBlueprintLibrary::TargetDataHasHitResult(DataHandle, 0))
	{
		FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(DataHandle, 0);

		PPGAS_LOG(LogGAS, Log, TEXT("Target %s Detected"), *(HitResult.GetActor()->GetName()));

		FGameplayEffectSpecHandle SpecHandle = MakeOutgoingGameplayEffectSpec(AttackDamageEffect, CurrentLevel);
		if (SpecHandle.IsValid())
		{
			ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, SpecHandle, DataHandle);
		}
	}

	bool bReplicateEndAbility = true;
	bool bWasCancelled = false;
	EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicateEndAbility, bWasCancelled);
}

 

  • 결과 콜백함수 에서 EffectSpecHandle을 통해 GE를 타겟에 적용한다
  • FGameplayEffectSpecHandle SpecHandle = 
    MakeOutgoingGameplayEffectSpec(AttackDamageEffect, CurrentLevel);
    • GA에서 GE를 실행하려면 GameplayEffectSpecHandle을 생성하여 Handle을 통해 GE를 실행하는 방법이 있다
    • MakeOutgoingGameplayEffectSpec 함수로 SpecHandle을 만들 수 있으며
      인자로 (생성할 GE 클래스, 현재 레벨 정보)가 들어간다
      • 추가적으로 GA가 아닌 곳에서 GE를 생성하려면 ASC->MakeOutgoingSpec 함수를 호출 해야 되며
        인자가 (생성할 GE 클래스, 현재 레벨 정보, EffectContextHandle)로 추가적으로 EffectContextHandle을 생성하여 넣어줘야 한다
    • ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, SpecHandle, DataHandle);
      • ApplyGameplayEffectSpecToTarget 함수로 GE를 타겟에 적용한다
      • CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo 변수는 GA에 선언되어 있다
      • SpecHandle은 위에서 만들었고, DataHandle은 Overlap 이나 라인스윕 결과를 데이터핸들에 넣은것이다

 

PPCharacterAttributeSet 에서 데미지 반영


PostGameplayEffectExecute 함수를 상속받아 GE가 실행된후 데미지가 들어오면 데미지를 반영해 준다

 

PostGameplayEffectExecute

void UPPCharacterAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data);

	float MinHealth = 0.0f;

	//#include "GameplayEffectExtension.h" 추가
	//Data에 실행되는 GE스펙, 타겟 ASC, EvaluatedData에 변경되는 어트리뷰트 및 연산자 등이 들어있음
	if (Data.EvaluatedData.Attribute == GetHealthAttribute())
	{
		PPGAS_LOG(LogGAS, Warning, TEXT("Direct Healt Accesss : %f"), GetHealth());
		SetHealth(FMath::Clamp(GetHealth(), MinHealth, GetMaxHealth()));
	}
	else if (Data.EvaluatedData.Attribute == GetDamageAttribute())
	{
		PPGAS_LOG(LogGAS, Log, TEXT("Direct Healt Accesss : %f"), GetHealth());
		SetHealth(FMath::Clamp(GetHealth() - GetDamage(), MinHealth, GetMaxHealth()));
		SetDamage(0.0f);
	}
}

 

  • const FGameplayEffectModCallbackData& Data
    • Data를 사용하려면 #include "GameplayEffectExtension.h" 추가해야 됨
  •  Super::PostGameplayEffectExecute(Data);
    • 부모 로직 실행
  • float MinHealth = 0.0f;
    • 체력 최솟값 저장
  • if (Data.EvaluatedData.Attribute == GetHealthAttribute())
    {
    PPGAS_LOG(LogGAS, Warning, TEXT("Direct Healt Accesss : %f"), GetHealth());
    SetHealth(FMath::Clamp(GetHealth(), MinHealth, GetMaxHealth()));
    }
    • 들어온 데이터의 어트리뷰트가 헬스일 경우 경고 로그 출력
    • SetHealth 함수로 체력 반영
  • else if (Data.EvaluatedData.Attribute == GetDamageAttribute())
    {
    PPGAS_LOG(LogGAS, Log, TEXT("Direct Healt Accesss : %f"), GetHealth());
    SetHealth(FMath::Clamp(GetHealth() - GetDamage(), MinHealth, GetMaxHealth()));
    SetDamage(0.0f);
    }
    • 들어온 데이터의 어티리뷰트 일경우 로그 출력
    • SetHealth 함수로 (현재체력 - 들어온 데미지)로 설정 및 Clamp를 범위를 걸어 체력 지정
    • SetDamage(0.0)으로 데미지 초기화