Infinite Fighter 개발일지 (14)

유영준·2023년 6월 18일
0

UE5 UNSEEN

목록 보기
14/18
post-thumbnail

23.06.12 ~ 2023.06.18 개발일지

개선한 점

게임 플레이 태그 사용

bool이나 enum값으로 체크하던 상태이상들을 Gameplay Tag를 사용하도록 바꾸었다

각각의 묶음을 DataTable을 통해 묶고, 이를 연동시켜주었다

DataTable을 생성하고,

테이블 리스트 추가를 통해 추가해주었다

이에 따라 Get, Set을 컨테이너가 태그를 보유중인지를 체크하는 방식으로 코드를 수정하였고

AIFEnemy.cpp

bool AIFEnemy::GetStunState()
{
	return EnemyState.HasTag(FGameplayTag::RequestGameplayTag(TEXT("Enemy.Stunned")));
}

애님 인스턴스 등에서도 이를 통해 값을 받아오도록 변경하였다

다만 게임 플레이 태그를 사용하며 기대한 부분 중 하나가 BT에서의 활용이었는데,

이는 아직 해결방법을 찾지 못해 조금 더 공부가 필요할 것 같다

데코레이터 BlackBoard 대신

CheckGameplayTagsOnActor 를 사용하고자 했는데, 잘 적용되지 않는 것 같다

FGameplayTagContainer 변수를 선언해서 사용하는 것이 맞는지 한번 다시 체크가 필요할 것 같다

적 감지 로직 변경

이전에는 각각의 적 객체마다 캐릭터를 판정하는 영역을 두고,

그 영역안에 캐릭터가 진입하거나 탈출할 시 캐릭터의 Target 변수에 자신을 할당하는 방식을 사용했다

생각해보니, 적이 계속적으로 캐릭터를 찾기 위해 Overlap 함수를 시행할 필요성도 없었고,

적이 자신을 할당하는 방식은 적이 죽게 되었을때 댕글링 포인터 등의 문제가 생길 위험도 높았기에 이번에 구조를 바꾸게 되었다

바꾼 방식은 GetOverlappingActors를 사용하였다

AIFCharacter.cpp

void AIFCharacter::WeakAttack()
{
	TSet<AActor*> Enemies;
	GetOverlappingActors(Enemies, AIFEnemy::StaticClass());
	if (Enemies.Num())
	{
		for (const auto& Enemy : Enemies)
		{
			// check if enemy is in character's sight (DotProduct on chracter's forward vector and character to enemy vector)
			float DotProduct = FVector::DotProduct(GetActorForwardVector(), 
            (Enemy->GetActorLocation() - GetActorLocation()).GetSafeNormal());
			
            if (DotProduct > 0.4)
			{
				// set the closest Enemy to Target
				Target = Cast<AIFEnemy>(Enemy);
				if(GetDistanceTo(Enemy) <= GetDistanceTo(Target))
					Target = Cast<AIFEnemy>(Enemy);
			}

	...

}

적의 배열을 받고, 그 중 가장 가까운 적을 타겟으로 하여 이동값을 보정하는 방식을 사용했다

처형 모션을 취할때는 이와 비슷하게 가장 가까운 적 중 스턴 상태인 적에게 처형을 시행하였다

UI 불러오기 변경

UI를 불러오는 방식을 GetWidgetFromName 함수에서 UPROPERTY(meta = (BindWidget)) 로 바꿔주었다

UI와 변수의 이름을 통일시켜주면 바로 적용이 되기 때문에 훨씬 편하게 UI를 불러올 수 있게 되었다

추가한 부분

적 점프 공격 추가

일정 거리를 도약해서 공격하는 방식을 추가해주었다

적과 캐릭터가 600만큼의 거리가 있을때 시행하게 하기 위해,

먼저 Vector 값을 정해주는 SetRangedAttackVector Task 를 만들었다

BTTask_SetRangedAttackVector

// Fill out your copyright notice in the Description page of Project Settings.


#include "AI/BTTask_SetRangedAttackVector.h"
#include "AIController.h"
#include "BehaviorTree/BlackboardComponent.h"

UBTTask_SetRangedAttackVector::UBTTask_SetRangedAttackVector()
{
    NodeName = TEXT("SetVector");
}

EBTNodeResult::Type UBTTask_SetRangedAttackVector::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
    EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory);

	APawn* ControllingPawn = Cast<APawn>(OwnerComp.GetAIOwner()->GetPawn());
	if (nullptr == ControllingPawn)
	{
		return EBTNodeResult::Failed;
	}

	APawn* TargetPawn = Cast<APawn>(OwnerComp.GetBlackboardComponent()->GetValueAsObject(TEXT("Target")));
	if (nullptr == TargetPawn)
	{
		return EBTNodeResult::Failed;
	}

	FVector Direction = (ControllingPawn->GetActorLocation() - TargetPawn->GetActorLocation()).GetSafeNormal();
	float DesiredDistance = 600.0f;
	FVector DesiredLocation = TargetPawn->GetActorLocation() + (Direction * DesiredDistance);

	OwnerComp.GetBlackboardComponent()->SetValueAsVector(TEXT("RangeAttackVector"), DesiredLocation);

    return EBTNodeResult::Succeeded;
}

지정한 위치로 이동하고, 캐릭터를 바라보고 공격하도록 설정하였다

결과는 다음과 같다

다음으로는 적 유닛이 일정한 확률로 전에 만든 근접공격과 이번에 만든 공격을 섞어서 할 수 있게 랜덤성을 추가해주었다

UBTDecorator_RandomChance

bool UBTDecorator_RandomChance::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
{
    float Rand = FMath::FRand();

    return SucceedRate >= Rand;
}

간단하게 설정한 값이 랜덤으로 나오는 변수보다 큰지 작은지를 통해 판별하도록 했다

이떄 변수 SucceedRate는 UPROPERTY에서 ClampMin, ClampMax를 설정해

0~1의 값만 설정 가능하도록 구현했다

처형 모션 중 무적 시간 추가

처형 애니메이션이 나가는 중 캐릭터를 무적으로 설정하지 않아 적에게 공격 받는 버그를 수정했다

이를 위해 각각의 애니메이션이 끝나는 시간을 담는 변수를 두고,

그 변수의 값만큼 기다렸다 무적을 해제하는 Delegate를 호출하는 방식을 사용했다

(이제 Data Asset이 시간 변수도 가지게 된다)

방어 모션 추가

공격을 받았을시, 내적하여 정면에 위치했다면 공격받지 않고 밀리기만 하도록 구현하였다

간단하게 공격을 받을때 방어 모션을 취하는 중이라면 데미지를 받지 않도록 설정하였다

profile
토비폭스가 되고픈 게임 개발자

0개의 댓글