[68. Enemy Combat #3]
MoveTo에 전달했던 NavPath의 정보와 DrawDebugSphere를 이용해서 경로를 시각적으로 확인해 보도록 한다.
// Enemy.cpp
#include "Kismet/KismetSystemLibrary.h"
void AEnemy::MoveToTarget(AMain* Target)
{
if (NavPath) // 내비게이션 메시 밖으로 나가면 크래시가 나므로 null체크를 해줘야 한다
{
auto PathPoints = NavPath->GetPathPoints();
for (const auto& Point : PathPoints)
{
auto Location = Point.Location;
UKismetSystemLibrary::DrawDebugSphere(this, Location, 25.f, 8, FLinearColor::Red, 10.f, .5f);
}
}
}
장애물이 없는 직선상에서는 Enemy의 원점, Target의 원점 두개가 그려지고 Target의 원점으로 쭉 이동한다.
하지만 코너같이 장애물이 있는 경우 그것을 우회하는 경로 여러개가 그려진다.
MoveRequest의 Acceptance radius에 의한 허용 반경은 캡슐 콜리전 사이의 거리와 관련이 있다.
[69. Enemy Combat #4]
지금까지는 어그로 범위 안에 들어오면 추적하는 것을 구현해 보았고 이제 전투 범위에 들어오면 공격하는 것을 구현할 차례이다.
우선은 어그로 범위에 들어오면 MoveToTarget을 호출하고 MovementStatus를 변경했던 것처럼 공격도 마찬가지로 구현하고 애니메이션 블루프린트도 동일하게 만들어준다.
void AEnemy::CombatSphereOnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor)
{
AMain* Main = Cast<AMain>(OtherActor);
if (Main)
{
SetEnemyMovementStatus(EEnemyMovementStatus::EMS_Attacking);
}
}
UE_LOG(LogTemp, Warning, TEXT("Super::CombatSphereOnOverlapBegin()"));
}
void AEnemy::CombatSphereOnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (OtherActor)
{
AMain* Main = Cast<AMain>(OtherActor);
if (Main)
{
SetEnemyMovementStatus(EEnemyMovementStatus::EMS_MoveToTarget);
MoveToTarget(Main); // 공격 범위에서 벗어나도 다시 추적하도록 해야한다
}
}
UE_LOG(LogTemp, Warning, TEXT("Super:CombatSphereOnOverlapEnd()"));
}
IdleWalk로 돌아오는것은 Not Equal로 해준다.
어그로 범위에 들어가면 추적하고 공격 범위에 들어오면 공격 모션을 취한다. 하지만 공격 모션 도중에 공격 범위를 벗어나면 애니메이션이 중단되고 다시 추적하는 문제가 있지만 잠시 뒤에 수정하도록 한다.
어느정도 의도한 대로 작동하고 있지만 몬스터가 영원히 쫓아오는것은 원치 않는다. 어느정도 범위를 벗어나면 몬스터가 추적을 멈추고 도망갈수 있기를 원한다.
// Enemy.cpp
void AEnemy::AgroSphereOnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (OtherActor)
{
AMain* Main = Cast<AMain>(OtherActor);
if (Main)
{
SetEnemyMovementStatus(EEnemyMovementStatus::EMS_Idle);
if (AIController)
{
AIController->StopMovement();
}
}
}
UE_LOG(LogTemp, Warning, TEXT("Super:AgroSphereOnOverlapEnd()"));
}
StopMovement()는 현재 무엇을 하고 있던간에 행동을 중지시킨다.
공격 도중 타겟이 범위를 벗어나면 모션이 끊기는 것을 수정해 보도록 한다.
애니메이션 노티파이를 이용해서 공격 모션이 재생되면 중단 없이 쭉 재생하고 모션의 끝에서 공격이 끝났다는 노티파이를 발생시킨 뒤 그것을 통해 다시 타겟을 추적하도록 만들 것이다.
몽타주를 따로 만들지 않았으므로 공격 애니메이션 애셋에서 노티파이를 직접 추가해주면 된다.
블루프린트로 처리할것이기 때문에 코드를 조금 수정하도록 한다.
// Enemy.h
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="AI")
bool bOverlappingCombatSphere; // 전투 영역에 겹침 상태인지 확인하기 위함
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "AI")
AMain* CombatTarget; // 블루프린트에서 전투중인 타겟을 알기 위함
// Enemy.cpp
void AEnemy::CombatSphereOnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor)
{
AMain* Main = Cast<AMain>(OtherActor);
if (Main)
{
CombatTarget = Main; // 전투 영역에 들어오면 타겟을 설정한다
bOverlappingCombatSphere = true;
SetEnemyMovementStatus(EEnemyMovementStatus::EMS_Attacking);
}
}
UE_LOG(LogTemp, Warning, TEXT("Super::CombatSphereOnOverlapBegin()"));
}
void AEnemy::CombatSphereOnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (OtherActor)
{
AMain* Main = Cast<AMain>(OtherActor);
if (Main)
{
bOverlappingCombatSphere = false;
// 아래 조건문이 실행될수가 없음
if (EnemyMovementStatus != EEnemyMovementStatus::EMS_Attacking)
{
MoveToTarget(Main);
CombatTarget = nullptr; // 전투 영역을 벗어나면 타겟을 해제한다
}
}
}
UE_LOG(LogTemp, Warning, TEXT("Super::CombatSphereOnOverlapEnd()"));
}
EndAttack 노티파이가 발생하면(모션이 끝나면) MoveToTarget을 호출한다.
여기서 문제점이 다시 발생한다. 타겟이 움직이지 않고 여전히 전투 영역에 있는 경우에도 EndAttack이 발생하면 전투상태가 풀리고 MoveToTarget이 무조건 호출이 되기때문에 연속적으로 공격을 하지 않는다.
전투 영역과 겹침 상태가 아닐때만 MoveToTarget을 호출하도록 노드의 흐름을 바꿔주면 된다.
[정리]
- MoveTo에 전달되는 FNavPathSharedPtr에 경로와 관련된 정보를 돌려받는다.
- AIController의 StopMovement 함수를 통해 행동을 중단시킬수 있다.
- 공격 모션이 도중에 끊기는 것은 애니메이션 노티파이를 통해 해결할 수 있다. (코드만으로도 가능한지 확인 필요)
'언리얼 엔진 > UE4 C++' 카테고리의 다른 글
[UE C++] Combat #6 (0) | 2022.09.13 |
---|---|
[UE C++] Combat #5 (0) | 2022.09.12 |
[UE C++] Combat #3 (0) | 2022.09.11 |
[UE C++] Combat #2 (0) | 2022.09.11 |
[UE C++] Combat #1 (0) | 2022.09.10 |