관리 메뉴

Mini

[UE5] Click To Move 구현 본문

UE5/Gameplay Abilities

[UE5] Click To Move 구현

Mini_96 2024. 6. 9. 17:39

* 언리얼 TopDown 예시 살펴보기

서버는 click to move되는데 클라에서는 안되는 모습

- 원인

쭉누르면 AddMove 실행 : 자동복제기능 있음
짧게 눌렀다떼면 SimpleMove 실행 : 복제기능없음, 서버용임 / but (+) 자동길찾기, 장애물피하기 가능

눌럿다떼면 복제기능이 없는 simpleMove가 실행되서 안되는거임

즉, 멀티플에이어인경우 클라의경우 계속눌르고있어야 이동가능함

 

* 우리의 Click to move설계

AddMove를 써야함 && 장애물처리 구현해야함

 

* 설계

장애물이 없는경우 쉬움
장애물이 있는경우, 정점생성후 방향을 바꿔주는 방법 / but, 방향이 갑자기 바뀌면 이상함
Spline으로 해결!

 

 

* 언리얼을 배껴서 변수생성

PC

    FVector CachedDestination=FVector::ZeroVector; //클릭된 목적지 저장
    float FollowTime=0.f; //놓기전에 얼마나 오랫동안 마우스 눌렀는지 저장
    float ShortPressThreshold = 0.f; //짧게눌렀는지 판단하는 기준
    bool bAutoRunning=false;

    UPROPERTY(EditDefaultsOnly)
    float AutoRunAcceptanceRadius = 50.f; //자동이동 가능 범위

    UPROPERTY(VisibleAnywhere)
    TObjectPtr<USplineComponent> Spline; //부드러운 자동이동위한 스플라인, 생성자에서 초기화
    
};

스플라인 초기화


AAuraPlayerController::AAuraPlayerController()
{
    bReplicates = true; // 해당 컨트롤러가 네트워크 상에서 복제될 수 있도록 함

    Spline=CreateDefaultSubobject<USplineComponent>("Spline");
}

 

* 쭉누르면 이동 구현

추가적으로, 적을 타겟팅하고 있는지 아닌지에 따라 분기해줘야함

bool bTargeting = false; //적을 타겟팅하고 있는지

이동구현 : (처음누름, 뗌, 누르는중) 구분 && 타겟팅중인지 구분

void AAuraPlayerController::AbilityInputTagPressed(FGameplayTag InputTag) //처음누름
{
    //LMB인 경우
    if(InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
    {
       //ThisActor 가있으면 타겟팅중임
       bTargeting = ThisActor ? true : false;
       bAutoRunning=false;
    }
}

void AAuraPlayerController::AbilityInputTagReleased(FGameplayTag InputTag) //뗌
{
    if(GetASC()==nullptr) return;
    
    GetASC()->AbilityInputTagHeld(InputTag);
}

void AAuraPlayerController::AbilityInputTagHeld(FGameplayTag InputTag) //누르는중
{
    //LMB가 아닌 일반태그인 경우, 그냥 능력을 활성화하고 끝냄
    if(!InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
    {
       if(GetASC())
       {
          GetASC()->AbilityInputTagHeld(InputTag); //ASC의 함수호출
       }
       return;
    }
    
    //LMB인경우, 달리는 문제를 해결해야함
    //타겟팅중인 적이 있는 경우
    if(bTargeting)
    {
       if(GetASC())
       {
          GetASC()->AbilityInputTagHeld(InputTag); //ASC의 함수호출
       }
    }
    else //타겟팅중인 적이 없는경우
    {
       FollowTime+=GetWorld()->GetDeltaSeconds(); //몇초눌렀는지 저장

       FHitResult Hit;
       if(GetHitResultUnderCursor(ECC_Visibility,false,Hit))
       {
          CachedDestination = Hit.ImpactPoint; //커서눌린위치를 저장
       }

       //이동, 이동하려면 폰이필요함
       if(APawn* ControlledPawn = GetPawn())
       {
          const FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal(); //b-a 하면 방향이나옴
          ControlledPawn->AddMovementInput(WorldDirection); //그 방향으로 이동
       }
    }
}

결과 : 길게눌렀을때 정상작동(서버ㅡ클라 모두) / but, 픽업위젯 클라에서 작동안하는 버그발견

 

* 짧게누르면 이동구현

void AAuraPlayerController::AbilityInputTagReleased(FGameplayTag InputTag) //뗌
{
    //LMB가 아닌 일반태그인 경우, 그냥 능력을 활성화하고 끝냄
    if(!InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
    {
       if(GetASC())
       {
          GetASC()->AbilityInputTagReleased(InputTag); //ASC의 함수호출
       }
       return;
    }
    
    if(bTargeting)
    {
       if(GetASC())
       {
          GetASC()->AbilityInputTagReleased(InputTag); //ASC의 함수호출
       }
    }
    else //짧게누른경우 거기로 부드럽게 이동 구현
    {
       APawn* ControlledPawn = GetPawn();
       //짧게누른경우
       if(FollowTime<=ShortPressThreshold && ControlledPawn)
       {
          //(시작위치, 끝위치)
          UNavigationPath* NavPath =
             UNavigationSystemV1::FindPathToLocationSynchronously(this, ControlledPawn->GetActorLocation(),CachedDestination);
          if(NavPath)
          {
             Spline->ClearSplinePoints();
             for(const FVector& PointLoc : NavPath->PathPoints) //목적지로가는 경로들에 대해
             {
                Spline->AddSplinePoint(PointLoc, ESplineCoordinateSpace::World); //스플라인 지점 추가
                DrawDebugSphere(GetWorld(),PointLoc,8.f,8,FColor::Green, false); //디버깅용
             }
             bAutoRunning=true; //오토러닝 켜주기
          }
          
       }
       FollowTime=0.f; //팔로우타임리셋
       bTargeting=false; //타게팅여부 리셋
    }
    
}

 

- 모듈추가 해줘야됨

결과 : 아무일도안일어남

해결 : NavMeshBoundsVolume 추가해야함

 

목적지(cached)로 가는 경로에 초록원이 찍히는 모습

 

* 장애물 추가 실험

콜리전이 있는지 확인
p 눌러서 메시에 구멍이 생기는지 확인
결과 : v를 클릭했더니 v로가는 경로에 점이 찍힘

 

* 틱마다 점들로 이동하면 되는거임

void AAuraPlayerController::PlayerTick(float DeltaTime)
{
    Super::PlayerTick(DeltaTime);
    CursorTrace();

    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;
       }
    }
}

- 함수로 리팩토링

void AAuraPlayerController::AutoRun()
{
    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;
       }
    }
}
void AAuraPlayerController::PlayerTick(float DeltaTime)
{
    Super::PlayerTick(DeltaTime);
    CursorTrace();
    
    AutoRun(); //리팩토링
}

결과 : 무한대로 달리는 버그

- 원인 : 무조건 AutoRun하고있음

-해결 : AutoRun이 아니면 return 하도록 fix

결과 : 장애물이 있어도 잘찾아감

* 멀티플레이 Test

클라에서 실행X, 디버그 구체도 안나오는 버그
해결 : 클라에서 네비게이션 허용 켜야함
결과 : 클라에서도 잘 작동함

 

* 버그발견

특정 지점을 클릭하면 무한히 달리는 버그

- 원인 : nav mash에 없는 항목(ex : 기둥 자체) 을 클릭해서 그럼

-해결1 : 기둥 콜리전 > 가시성을 무시하면, 클릭이 무시되는듯?

 

- 문제2 : navmash 볼륨자체를 클릭하면, 마찬가지로 무한이동 버그가생김

- 해결 : 클릭한지점을 PathPoint위의 지점으로 강제로 변경 => 항상 도달가능한 목적지로 달려감

void AAuraPlayerController::AbilityInputTagReleased(FGameplayTag InputTag) //뗌
{
...
CachedDestination = NavPath->PathPoints[NavPath->PathPoints.Num()-1]; //이상한곳 클릭방지, 클릭지점을 PathPoint 위의 점으로 강제설정

이제 우리는 아무곳이나 클릭해도 됨!

결과 : 서버-클라 모두 정상작동!