AuraPlayerController의 AbilityInputPressed(), AbilityInputHeld(), AbilityInputReleased() 함수는 아래와 같이 작성한다.
AAuraPlayerController::AAuraPlayerController()
{
bReplicates = true;
Spline = CreateDefaultSubobject<USplineComponent>(TEXT("Spline"));
}
void AAuraPlayerController::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
CursorTrace();
AutoRun();
}
void AAuraPlayerController::AbilityInputTagPressed(FGameplayTag InputTag)
{
if (InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
{
bTargeting = CurrentTarget ? true : false;
bAutoRunning = false;
}
}
void AAuraPlayerController::AbilityInputTagReleased(FGameplayTag InputTag)
{
if (!InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
{
if (GetASC())
{
GetASC()->AbilityInputTagReleased(InputTag);
}
return;
}
if (bTargeting)
{
if (GetASC())
{
GetASC()->AbilityInputTagReleased(InputTag);
}
}
else
{
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);
DrawDebugSphere(GetWorld(), PointLoc, 8.f, 8, FColor::Green, false, 5.f);
}
CachedDestination = NavPath->PathPoints[NavPath->PathPoints.Num() - 1];
bAutoRunning = true;
}
}
FollowTime = 0.f;
bTargeting = false;
}
}
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 Hit;
if (GetHitResultUnderCursor(ECC_Visibility, false, Hit))
{
CachedDestination = Hit.ImpactPoint;
}
if (APawn* ControlledPawn = GetPawn<APawn>())
{
const FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();
ControlledPawn->AddMovementInput(WorldDirection);
}
}
}
void AAuraPlayerController::AutoRun()
{
if (!bAutoRunning) return;
if (APawn* ControlledPawn = GetPawn<APawn>())
{
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;
}
}
}
- AbilityInputTagPressed()
- 새로 클릭 이벤트가 들어왔을 때, 마우스 좌클릭인지 확인하여, 좌클릭인 경우에는 이동과 GameplayAbility의 사용을 구분해야 하기 때문에, 현재 마우스 커서가 적을 가르키고 있는지를 검사하여 bTargeting의 값을 변경한다.
- 새로 클릭 이벤트가 들어왔으므로, GameplayAbility를 사용하던, 새로운 지점으로 이동하던 어느 쪽이든 관계 없이 기존의 자동 이동은 취소해야 하므로, bAutoRunning을 false로 한다.
- AbilityInputTagHeld()
- 마우스 좌클릭이 아닌 입력이거나, 마우스 좌클릭이라 해도 커서 아래 적이 있는 경우에는 AuraAbilitySystemComponent의 AbilityInputHeld()를 호출한다.
- 둘 다 아닌 경우는 땅을 클릭한 것이라고 생각할 수 있으므로,
- 커서가 눌리는 동안 캐릭터가 커서를 따라다니는 시간을 저장하는 변수 FollowTime에 DeltaSecond를 더해준다.
- 커서 트레이스(APlayerController의 GetHitResultAtScreenPosition()을 통해 실시되는 LineTrace)를 실시하여 목표 지점(CachedDestination)을 캐싱해둔다.
- 캐싱된 목표 지점과 캐릭터의 현재 위치를 통해 방향을 구해서 AddMovementInput()을 호출한다.
- AbilityInputTagReleased()
- 마우스 좌클릭이 아니거나 적이 커서 아래 있는 경우, AuraAbilitySystemComponent의 AbilityInputReleased()를 호출한다.
- 둘 다 아닌 경우 땅을 클릭한 것이라고 볼 수 있다.
- 마우스 클릭을 짧게 한 경우, 클릭 지점까지의 경로를 찾아서 이동하도록 할 것이다.
- UNavigationSystemV1은 FindPathToLocationSynchronously()를 통해 찾아낸 경로인 UNavigationPath를 반환한다.
- UNavigationSystemV1의 함수를 사용하는데, 이는 Build.cs에 Dependency에 NavigationSystem을 명시해주어야 빌드가 가능하다.
- NavmeshBoundVolume을 레벨에 배치해야만이 UNavigationSystemV1의 길찾기 함수가 제대로 작동한다.
(AI 길찾기 기능 또한 NavmeshBoundVolume이 필요)
- 에디터 - 프로젝트 설정의 ‘Navigation System’에서 반드시 Allow Client Side Navigation를 체크해주어야 클라이언트 측에서도 길찾기 기능을 사용할 수 있다.
(기본적으로는 서버 측만 사용 가능)
- AuraPlayerController의 Spline의 현재 모든 포인트를 제거하기 위해 ClearSplinePoints()를 호출하고, 다시 UNavigationPath가 제공하는 PathPoints (찾아낸 경로 상의 포인트들) 들을 AddSplinePoint()로 더해준다.
- 단, 목표 지점이 Spline의 끝부분과 불일치 할 수 있다.
(다른 액터를 클릭하거나 한 경우)
이를 방지하기 위해, CachedDestination을 PathPoints의 마지막 지점으로 변경해주고, AutoRun()을 호출할 수 있도록 bAutoRunning을 true로 한다.
- 클릭이 종료되었으므로, FollowTime과 bTargeting은 초기화한다.
- AutoRun()
- Spline은 Spline상에서의 위치를 월드 상의 위치(벡터)로 변환해주는 함수나, Spline상에서의 방향을 월드 상의 방향(벡터) 값으로 변환해주는 함수를 제공한다.
- FindLocationClosestToWorldLocation()을 통해 현재 캐릭터의 Spline상에서의 위치를 얻는다.
- FindDirectionClosestToWorldLocation()을 통해 해당 위치에서의 방향 벡터를 얻어, 해당 방향으로 AddMovementInput()을 실시하여 캐릭터를 이동시킨다.
- 목표 지점에 도달했는지 검사를 위해, CachedDestination과 현재 Spline상에서의 위치의 차이를 길이로 변환하여, 이 값이 AutoRunAcceptanceRadius보다 작은지 확인하고, 작은 경우 bAutoRunning을 false로 하여 자동 이동을 중단하도록 한다.