1. 마우스를 누르고 있을때 마우스 위치로 움직이게 하기
캐릭터를 현재 WASD로 움직이고 있는데 이제 이것을 클릭으로 움직이게 할 것이다. 이때 서버와 클라이언트 모두가 작동하게 만들어야 한다.
움직일때 가장 중요한 점은 장애물을 피할 수 있는 능력이 있어야 한다. 이를 감안하여 구현하도록 하자.
플레이어 컨트롤러 클래스로 들어가자.
클릭으로 이동을 구현할때 한번만 클릭하면 그곳으로 이동, 계속 마우스를 누르고 있으면 마우스를 따라 이동하게 구현할 것이다.
변수들을 선언하자.
FVector CachedDestination = FVector::ZeroVector; // 클릭 지점 정보
float FollowTime = 0.f; // 마우스를 누르고 있는 시간
float ShortPressThreshold = 0.5f; // 마우스를 따라가게 하는데 필요한 클릭 시간
bool bAutoRunning = false; // 한번의 클릭으로 이동하고 있는 중인지 아닌지 (마우스를 꾹 눌러서 이동시키는 중이면 false)
bool bTargeting = false; // 에너미를 타겟팅 중인지 아닌지
UPROPERTY(EditDefaultsOnly)
float AutoRunAcceptanceRadius = 50.f; // 얼마나 목적지에 가깝게 갈지
UPROPERTY(VisibleAnywhere)
TObjectPtr<USplineComponent> Spline; // 곡선의 경로를 정의하는 컴포넌트
이제 이 스플라인 컴포넌트는 실제로 할당해주어야 한다. 생성자에서 진행하겠다.
Spline = CreateDefaultSubobject<USplineComponent>("Spline");
이제 마우스를 누를때 반응을 구현하는 콜백함수를 정의해보자.
void AAuraPlayerController::AbilityInputTagPressed(FGameplayTag InputTag)
{
if (InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
{
bTargeting = ThisActor ? true : false;
bAutoRunning = false;
}
}
입력이 왼쪽 마우스 클릭인지와 현재 타겟팅 중인지 아닌지만 확인하면 된다. 추가로 오토러닝도 일단 false로 설정하자.
마우스를 꾹 누를때의 반응을 만들어보자.
누르고 있는 시간을 더하면서 이하의 작업들을 진행한다.
커서 트레이스를 통해 무언가 있으면 그곳을 목적지로 삼는다.
FollowTime += GetWorld()->GetDeltaSeconds();
FHitResult CursorHit;
if (GetHitResultUnderCursor(ECC_Visibility, false, CursorHit))
{
CachedDestination = CursorHit.ImpactPoint;
}
그 후 목적지의 방향을 알아낸뒤 함수를 호출하여 캐릭터를 이동시킨다.
if (APawn* ControlledPawn = GetPawn())
{
const FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();
ControlledPawn->AddMovementInput(WorldDirection);
}
지금까지의 작업은 마우스를 꾹 누르고 있을때의 작업이다.
전체 코드를 보자.
void AAuraPlayerController::AbilityInputTagHeld(FGameplayTag InputTag)
{
if (!InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
{
if (GetASC())
{
GetASC()->AbilityInputTagHeld(InputTag);
}
return;
}
if (bTargeting)
{
if (GetASC())
{
GetASC()->AbilityInputTagHeld(InputTag);
}
}
else
{
FollowTime += GetWorld()->GetDeltaSeconds();
FHitResult CursorHit;
if (GetHitResultUnderCursor(ECC_Visibility, false, CursorHit))
{
CachedDestination = CursorHit.ImpactPoint;
}
if (APawn* ControlledPawn = GetPawn())
{
const FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();
ControlledPawn->AddMovementInput(WorldDirection);
}
}
}
타겟팅이 있는지, 마우스 왼쪽 클릭이 맞는지들을 미리 체크해두었다. 이제 컴파일하고 에디터로 돌아가 확인하자.
마우스를 꾹 누르고 있으면 캐릭터가 그 방향으로 이동하는 것을 볼 수 있다.
이제 마우스를 한번만 누르면 그 위치로 이동하게 해보자.
2. 마우스를 한번만 누르면 이동할 경로 구하기
이제 마우스를 짧게 누르면 해당 위치로 자동으로 이동하게 할 것이다. 마우스를 누르고 있던 시간을 알기 때문에 이 작업이 가능해진다.
Follow 타임이 한계시간보다 짧으면 경로찾기를 진행할 것이다.
우리가 이때 사용할 함수에 대해 알아보자.
함수 정의
UNavigationPath* UNavigationSystemV1::FindPathToLocationSynchronously(UObject* WorldContextObject, const FVector& PathStart, const FVector& PathEnd, AActor* PathfindingContext, TSubclassOf<UNavigationQueryFilter> FilterClass)
매개변수 설명
- WorldContextObject
- 경로를 계산할 월드 컨텍스트.
- 보통 GetWorld() 또는 UObject를 넘깁니다.
- PathStart
- 경로 탐색의 시작 위치 (FVector).
- 이동을 시작할 좌표.
- PathEnd
- 경로 탐색의 목표 위치 (FVector).
- 이동할 목적지 좌표.
- PathfindingContext
- 경로 탐색에 사용될 컨텍스트 액터 (AActor*).
- AI 캐릭터나 네비게이션 관련 정보를 제공하는 액터를 지정.
- 지정하지 않으면 기본 네비게이션 에이전트 설정이 사용됩니다.
- FilterClass
- 네비게이션 필터 클래스 (TSubclassOf<UNavigationQueryFilter>).
- 특정 경로 조건(예: 장애물 회피, 비용 계산)을 설정할 수 있는 네비게이션 필터.
- 기본값은 nullptr.
위의 함수는 반환으로 계산된 경로를 나타내는 UNavigationPath* 포인터를 반환한다. 해당 포인터에는 PathPoints라는 배열이 존재하는데 경로를 나타내는 배열이다.
이제 이 경로를 스플라인에 추가하면 부드러운 경로를 얻을 수 있을 것이다.
지금까지 진행한 코드를 보자.
APawn* ControlledPawn = GetPawn();
if (FollowTime <= ShortPressThreshold && ControlledPawn)
{
if (UNavigationPath* NavPath = UNavigationSystemV1::FindPathToLocationSynchronously(this, ControlledPawn->GetActorLocation(), CachedDestination))
{
Spline->ClearSplinePoints();
for (const FVector& PointLoc : NavPath->PathPoints)
{
Spline->AddSplinePoint(PointLoc, ESplineCoordinateSpace::World);
}
}
}
"NavigationSystem"
해당 모듈을 추가해주자.
이러면 컴파일이 정상적으로 된다. 실제로 이동시키는 부분을 구현하기 전 한가지 작업을 하자. 월드에 네비게이션 볼륨이 없으면 파인드 패스가 작동하지 않는다. 네비게이션 볼륨을 실제 필드에 깔아두도록 하자.
3. 마우스 클릭시 해당 위치로 움직이게 하기
이제 경로는 구했으니 실제로 움직이게 할 차례이다.
Tick 함수에서 캐릭터를 움직이게 할 것이다.
const FVector LocationOnSpline = Spline->FindLocationClosestToWorldLocation(ControlledPawn->GetActorLocation(), ESplineCoordinateSpace::World);
const FVector Direction = Spline->FindDirectionClosestToWorldLocation(LocationOnSpline, ESplineCoordinateSpace::World);
첫번째 변수는 가장 가까운 스플라인 위치를 저장하는 변수이다.
두번째 변수는 가장 가까운 스플라인의 방향을 저장하는 변수이다.
아래는 전체 코드이다.
void AAuraPlayerController::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
CursorTrace();
AutoRun();
}
void AAuraPlayerController::AutoRun()
{
if (!bAutoRunning) return;
if (APawn* ControlledPawn = GetPawn())
{
const FVector LocationOnSpline = Spline->FindLocationClosestToWorldLocation(ControlledPawn->GetActorLocation(), ESplineCoordinateSpace::World);
const FVector Direction = Spline->FindDirectionClosestToWorldLocation(LocationOnSpline, ESplineCoordinateSpace::World);
ControlledPawn->AddMovementInput(Direction);
const float DistanceToDestination = (LocationOnSpline - CachedDestination).Length();
if (DistanceToDestination <= AutoRunAcceptanceRadius)
{
bAutoRunning = false;
}
}
}
컴파일하고 에디터로 들어가자.
다시 설정을 해야할 게 있다. 프로젝트 세팅으로 들어가자.
해당항목을 True로 설정해주어야 한다.
이제 캐릭터가 잘 이동한다. 하지만 한가지 문제점이 있다. 캐릭터가 가지 못하는 곳을 누르면 하염없이 이동을 한다는 것이다. 이를 방지하도록 하자.
if (NavPath->PathPoints.Num() > 0)
{
CachedDestination = NavPath->PathPoints.Last();
bAutoRunning = true;
}
목적지가 실제로 존재하는 목적지로 설정해주면 된다.
이렇게 해서 캐릭터를 마우스로 움직이는데 성공했다.
'게임프로그래밍 > 실습2' 카테고리의 다른 글
[실습2] 44. 어빌리티 태스크 (0) | 2024.12.04 |
---|---|
[실습2] 43. 발사체 생성 능력 만들기 (0) | 2024.12.03 |
[실습2] 41. 커스텀 인풋 컴포넌트 만들기 (0) | 2024.12.03 |
[실습2] 40. 인풋과 게임플레이 태그 연결하기 (0) | 2024.12.03 |
[실습2] 39. 어빌리티 부여하기 (0) | 2024.12.03 |