내일배움캠프 언리얼트랙 41일차 - 보안요원 State Tree

2026. 6. 19. 12:18·내일배움캠프

플러그인 활성화

StateTree AI Compponent 선택

 

트러블슈팅

 

비상~하향평준화되어버림;; 주말동안 수리 예정..

 

1. 감지 범위 밖으로 벗어나 있는 상태인데도 계속 추격함. (해결)

원래 구현 목표는 감지범위(노랑, 초록, 시야각)을 벗어나면 마지막으로 목격했던 곳으로 보안요원이 이동하는것입니다. 계속 플레이어를 추격하면 안됨.

 

2. 공격범위 내에 들어갔다 나오면 추격이 아예 멈춤. 감지범위를 들락날락거려도 가만히있음. (해결)

원래 구현 목표는 공격 범위 안에 있으면 공격을 당하고 그 범위를 벗어나는 즉시 다시 플레이어 추격을 진행.

 

3. 플레이어가 감지범위 내에 있는 상황에서 ft.Security.TestCall를 했을 때 플레이어를 추격하는게 아닌 신고된 위치로 먼저 이동함. 이동 중 만약 플레이어가 감지범위를 벗어나려하면 그때 플레이어를 추격함. (해결)

원래 구현 목표는 ft.Security.TestCall를 했을 때 플레이어가 감지범위 안에 잇으면 바로 플레이어 추격, 감지 범위 밖에 있다면 신고된 위치로 이동.

 

bHasSeenTarget을 Perception 이벤트에만 의존하지 않게 변경
매 Tick마다 실제 거리, 시야각, LoseSightRadius, Line of Sight 기준으로 bHasSeenTarget 갱신
ft.Security.TestCall 직후에도 즉시 현재 보이는지 계산
State Tree에서 쓰기 쉽게 bIsTargetInAttackRange, TargetDistance 추가
FTSecurityAIController.h에 추가

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "FT|Security")
bool bIsTargetInAttackRange = false;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "FT|Security")
float TargetDistance = 0.0f;


void UpdateTargetState();
bool IsTargetCurrentlyVisible() const;
FTSecurityAIController.cpp에 추가

void AFTSecurityAIController::UpdateTargetState()
{
	const APawn* ControlledPawn = GetPawn();
	if (!ControlledPawn || !TargetActor)
	{
		TargetDistance = 0.0f;
		bHasSeenTarget = false;
		bIsTargetInAttackRange = false;
		return;
	}

	TargetDistance = FVector::Dist(ControlledPawn->GetActorLocation(), TargetActor->GetActorLocation());
	bIsTargetInAttackRange = TargetDistance <= AttackRange;
	bHasSeenTarget = IsTargetCurrentlyVisible();
}

bool AFTSecurityAIController::IsTargetCurrentlyVisible() const
{
	const APawn* ControlledPawn = GetPawn();
	if (!ControlledPawn || !TargetActor || !SightConfig)
	{
		return false;
	}

	const FVector ToTarget = TargetActor->GetActorLocation() - ControlledPawn->GetActorLocation();
	if (ToTarget.SizeSquared() > FMath::Square(SightConfig->LoseSightRadius))
	{
		return false;
	}

	const FVector Forward = ControlledPawn->GetActorForwardVector();
	const FVector DirectionToTarget = ToTarget.GetSafeNormal();
	const float Dot = FVector::DotProduct(Forward, DirectionToTarget);
	const float AngleDegrees = FMath::RadiansToDegrees(FMath::Acos(FMath::Clamp(Dot, -1.0f, 1.0f)));
	if (AngleDegrees > SightConfig->PeripheralVisionAngleDegrees)
	{
		return false;
	}

	return LineOfSightTo(TargetActor);
}
기존 State Tree

Idle -> Chase
조건 :
AIController.bSecurityCalled == True
&&
AIController.bHasSeenTarget == True
&&
AIController.TargetActor is Valid

Idle -> Investigate
조건 :
AIController.bSecurityCalled == True
&&
AIController.bHasSeenTarget == False
&&
AIController.TargetActor is Valid

Investigate -> Chase
조건 : 
AIController.bHasSeenTarget == True
&&
AIController.TargetActor is Valid

Chase -> Attack
조건 : 
Distance from GetActorLocation(Actor) to GetActorLocation(AIController.TargetActor) <= AIController.AttackRange
&&
AIController.TargetActor is Valid

Chase -> Investigate
조건 :
AIController.bHasSeenTarget == False

Attack -> Chase
조건 :
Distance from GetActorLocation(Actor) to GetActorLocation(AIController.TargetActor) > AIController.AttackRange
&&
AIController.bHasSeenTarget == True
&&
AIController.TargetActor is Valid

Attack -> Investigate
조건 :
AIController.bHasSeenTarget == False
State Tree 조건 변경

Idle -> Chase
조건 :
AIController.bSecurityCalled == True
&&
AIController.bHasSeenTarget == True
&&
AIController.TargetActor is Valid

Idle -> Investigate
조건 :
AIController.bSecurityCalled == True
&&
AIController.bHasSeenTarget == False
&&
AIController.TargetActor is Valid

Investigate -> Chase
조건 : 
AIController.bHasSeenTarget == True
&&
AIController.TargetActor is Valid

Chase -> Attack
조건 : 
Distance from GetActorLocation(Actor) to GetActorLocation(AIController.TargetActor) <= AIController.AttackRange
&&
AIController.TargetActor is Valid

Chase -> Investigate
조건 :
AIController.bHasSeenTarget == False

Attack -> Chase
조건 :
Distance from GetActorLocation(Actor) to GetActorLocation(AIController.TargetActor) > AIController.AttackRange
&&
AIController.bHasSeenTarget == True
&&
AIController.TargetActor is Valid

Attack -> Investigate
조건 :
AIController.bHasSeenTarget == False

트리거 On Tick으로 변경

변경 후 생긴 문제 1 (해결)

보안요원이 움직이지 않음. 감지는 잘 되고있는 것으로 확인됨.

(문제 영상) 이면서 이전 문제들의 해결 영상. 감지는 잘 됨.

 

구조 정리

수정 전

TargetDistance = FVector::Dist(ControlledPawn->GetActorLocation(), TargetActor->GetActorLocation());
bIsTargetInAttackRange = bHasSeenTarget && TargetDistance <= AttackRange;
bHasSeenTarget = IsTargetCurrentlyVisible();
수정 후

TargetDistance = FVector::Dist(ControlledPawn->GetActorLocation(), TargetActor->GetActorLocation());
bHasSeenTarget = IsTargetCurrentlyVisible();
bIsTargetInAttackRange = bHasSeenTarget && TargetDistance <= AttackRange;

bIsTargetInAttackRange가 이전 프레임의 bHasSeenTarget을 보고 계산되던 순서를 수정

(해결 영상 이 영상은 왜 링크밖에 안뜨지;;)

https://youtu.be/1_ETtdWHgzc

 

문제 2 (해결)

보안요원의 공격범위(빨간 스피어)에서 Idle 상태가 되고 공격하지 않음.

(문제 영상)

 

 

Idel -> Chase 추가하여 해결

조건:

bSecurityCalled == true
&& bHasSeenTarget == true
&& bIsTargetInAttackRange == false
&& TargetActor is Valid

 

(해결 영상)

 

'내일배움캠프' 카테고리의 다른 글

내일배움캠프 언리얼트랙 43일차 - 손님NPC AI Controller, State Tree(2)  (0) 2026.06.23
내일배움캠프 언리얼트랙 42일차 - 손님NPC AI Controller, State Tree  (0) 2026.06.22
내일배움캠프 언리얼트랙 40일차 - 보안요원 AI Controller  (0) 2026.06.18
내일배움캠프 언리얼트랙 38일차 - 심화반 2강  (0) 2026.06.16
내일배움캠프 언리얼트랙 35일차 - TA 1강  (0) 2026.06.11
'내일배움캠프' 카테고리의 다른 글
  • 내일배움캠프 언리얼트랙 43일차 - 손님NPC AI Controller, State Tree(2)
  • 내일배움캠프 언리얼트랙 42일차 - 손님NPC AI Controller, State Tree
  • 내일배움캠프 언리얼트랙 40일차 - 보안요원 AI Controller
  • 내일배움캠프 언리얼트랙 38일차 - 심화반 2강
thinklikethink
thinklikethink
생각처럼 개발 공부 블로그입니다.
  • thinklikethink
    생각처럼
    thinklikethink
  • 전체
    오늘
    어제
    • 분류 전체보기 (53) N
      • 사전캠프 (13)
      • 내일배움캠프 (40) N
  • 블로그 메뉴

    • 홈
    • 내일배움캠프
    • 사전캠프
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
thinklikethink
내일배움캠프 언리얼트랙 41일차 - 보안요원 State Tree
상단으로

티스토리툴바