Announcement

Collapse
No announcement yet.

Top Down Template Movement

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Top Down Template Movement

    Hello, I've been playing with the top-down template and have been trying to make the character more reactive to nearby clicks, I tried to work through a couple solutions, such as messing with the movement component, and the navmesh setting, however, from what I've tried the problem is that the LineTrace is hitting the character's Root Capsule Component and when that happens the character ignores the move request because it's on him. You can fix this by reducing the size of the capsule and you will see that suddenly the character responds much more readily to the clicks near where he is located.

    However, I would like to avoid messing with the capsule component because then his hit-box (capsule?), won't be properly adjusted to the character's mesh. So I walked through the source code and found in the PlayerController.cpp that the Top-Down Template player controller is using the function GetWorld()->LiveTraceSingle(*) this function provides the ability to specify a FCollisionQueryParams object and this object provides the ability to ignore a specific actor in its LineTrace, so I added the pawn as the ignore actor in the constructor of the FCollisionQueryParams and I thought that would solve the problem however, the character still won't respond to nearby movement requests, as if the ignore pawn section isn't including the root component as something to ignore.

    Here is the line I modified in the code in each of the overloads of GetHitResultAtScreenPosition, which is called by the TopDownCharacterController:

    Code:
    return GetWorld()->LineTraceSingle(HitResult, WorldOrigin, WorldOrigin + WorldDirection * 100000.f, ECC_Visibility, FCollisionQueryParams("ClickableTrace", bTraceComplex, GetPawn()));


    And here are the three GetHitResultAtScreenPosition sections of code that I modified in the PlayerController.cpp file, hoping they would allow me to request that the linetrace ignore the player's selected pawn:

    Code:
    bool APlayerController::GetHitResultAtScreenPosition(const FVector2D ScreenPosition, const ECollisionChannel TraceChannel, bool bTraceComplex, FHitResult& HitResult) const
    {
    	// Early out if we clicked on a HUD hitbox
    	if( GetHUD() != NULL && GetHUD()->GetHitBoxAtCoordinates(ScreenPosition, true) )
    	{
    		return false;
    	}
    
    	ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player);
    
    	if (LocalPlayer != NULL && LocalPlayer->ViewportClient != NULL && LocalPlayer->ViewportClient->Viewport != NULL)
    	{
    		// Create a view family for the game viewport
    		FSceneViewFamilyContext ViewFamily( FSceneViewFamily::ConstructionValues(
    			LocalPlayer->ViewportClient->Viewport,
    			GetWorld()->Scene,
    			LocalPlayer->ViewportClient->EngineShowFlags )
    			.SetRealtimeUpdate(true) );
    
    
    		// Calculate a view where the player is to update the streaming from the players start location
    		FVector ViewLocation;
    		FRotator ViewRotation;
    		FSceneView* SceneView = LocalPlayer->CalcSceneView( &ViewFamily, /*out*/ ViewLocation, /*out*/ ViewRotation, LocalPlayer->ViewportClient->Viewport );
    
    		if (SceneView)
    		{
    			FVector WorldOrigin;
    			FVector WorldDirection;
    			SceneView->DeprojectFVector2D(ScreenPosition, WorldOrigin, WorldDirection);
    			
    			return GetWorld()->LineTraceSingle(HitResult, WorldOrigin, WorldOrigin + WorldDirection * 100000.f, TraceChannel, FCollisionQueryParams ("ClickableTrace", bTraceComplex, GetPawn()));
    		}
    	}
    
    	return false;
    }
    
    bool APlayerController::GetHitResultAtScreenPosition(const FVector2D ScreenPosition, const ETraceTypeQuery TraceChannel, bool bTraceComplex, FHitResult& HitResult) const
    {
    	// Early out if we clicked on a HUD hitbox
    	if (GetHUD() != NULL && GetHUD()->GetHitBoxAtCoordinates(ScreenPosition, true))
    	{
    		return false;
    	}
    
    	ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player);
    
    	if (LocalPlayer != NULL && LocalPlayer->ViewportClient != NULL && LocalPlayer->ViewportClient->Viewport != NULL)
    	{
    		// Create a view family for the game viewport
    		FSceneViewFamilyContext ViewFamily( FSceneViewFamily::ConstructionValues(
    			LocalPlayer->ViewportClient->Viewport,
    			GetWorld()->Scene,
    			LocalPlayer->ViewportClient->EngineShowFlags )
    			.SetRealtimeUpdate(true) );
    
    
    		// Calculate a view where the player is to update the streaming from the players start location
    		FVector ViewLocation;
    		FRotator ViewRotation;
    		FSceneView* SceneView = LocalPlayer->CalcSceneView( &ViewFamily, /*out*/ ViewLocation, /*out*/ ViewRotation, LocalPlayer->ViewportClient->Viewport );
    
    		if (SceneView)
    		{
    			FVector WorldOrigin;
    			FVector WorldDirection;
    			SceneView->DeprojectFVector2D(ScreenPosition, WorldOrigin, WorldDirection);
    
    			return GetWorld()->LineTraceSingle(HitResult, WorldOrigin, WorldOrigin + WorldDirection * 100000.f, UEngineTypes::ConvertToCollisionChannel(TraceChannel), FCollisionQueryParams("ClickableTrace", bTraceComplex, GetPawn()));
    		}
    	}
    
    	return false;
    }
    
    bool APlayerController::GetHitResultAtScreenPosition(const FVector2D ScreenPosition, const TArray<TEnumAsByte<EObjectTypeQuery> > & ObjectTypes, bool bTraceComplex, FHitResult& HitResult) const
    {
    	// Early out if we clicked on a HUD hitbox
    	if (GetHUD() != NULL && GetHUD()->GetHitBoxAtCoordinates(ScreenPosition, true))
    	{
    		return false;
    	}
    
    	ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player);
    
    	if (LocalPlayer != NULL && LocalPlayer->ViewportClient != NULL && LocalPlayer->ViewportClient->Viewport != NULL)
    	{
    		// Create a view family for the game viewport
    		FSceneViewFamilyContext ViewFamily( FSceneViewFamily::ConstructionValues(
    			LocalPlayer->ViewportClient->Viewport,
    			GetWorld()->Scene,
    			LocalPlayer->ViewportClient->EngineShowFlags )
    			.SetRealtimeUpdate(true) );
    
    
    		// Calculate a view where the player is to update the streaming from the players start location
    		FVector ViewLocation;
    		FRotator ViewRotation;
    		FSceneView* SceneView = LocalPlayer->CalcSceneView( &ViewFamily, /*out*/ ViewLocation, /*out*/ ViewRotation, LocalPlayer->ViewportClient->Viewport );
    
    		if (SceneView)
    		{
    			FVector WorldOrigin;
    			FVector WorldDirection;
    			SceneView->DeprojectFVector2D(ScreenPosition, WorldOrigin, WorldDirection);
    
    			/*FCollisionObjectQueryParams ObjParam(ObjectTypes);*/
    			/*FCollisionObjectQueryParams ObjParam();*/
    			
    			/*return GetWorld()->LineTraceSingle(HitResult, WorldOrigin, WorldOrigin + WorldDirection * 100000.f, FCollisionQueryParams("ClickableTrace", bTraceComplex, GetPawn()), ObjParam);*/
    			return GetWorld()->LineTraceSingle(HitResult, WorldOrigin, WorldOrigin + WorldDirection * 100000.f, ECC_Visibility, FCollisionQueryParams("ClickableTrace", bTraceComplex, GetPawn()));
    		}
    	}
    
    	return false;
    }

    #2
    It sounds like you've got the same problem I had last week.

    Turns out it's because the function it uses to move towards the point you've clicked on tells the character to stop when he's within two times his collision radius of the destination. I forget which function it was using by default (something on the NavigationSystem, perhaps?), but that was telling it that the goal distance was his collision radius, and to stop when the edge of his collision capsule was within the goal distance.

    In the end I had to dig into the function and call what it was calling, but with the parameters I wanted. So I ended up with this:
    Code:
    const float GoalDistance = 5.0f;
    
    UNavigationComponent* PFindComp = NULL;
    UPathFollowingComponent* PFollowComp = NULL;
    InitNavigationControl(PFindComp, PFollowComp);
    
    if (PFindComp && PFollowComp)
    {
          if (!PFollowComp->HasReached(DestLocation))
          {
                // Walk to the given point if possible
                const bool bPathExists = PFindComp->FindPathToLocation(DestLocation);
                if (bPathExists)
                {
                      UPathFollowingComponent::FRequestCompletedSignature CompletedDelegate;
                      CompletedDelegate.BindUObject(this, &AMyPlayerController::OnMoveCompleted);
    
                      PFollowComp->RequestMove(PFindComp->GetPath(), CompletedDelegate, NULL, GoalDistance, false);
                }
          }
    }
    I set the goal distance to be low because I wanted him walking to the exact point I clicked on, and the 'false' at the end there tells it to use the actor's location rather than the edge of his collision capsule when checking the distance.

    I also had to add the AIModule to my project cuz the PathFollowingComponent is in there in version 4.4.

    Hope that helps.

    Comment


      #3
      I've been away for a couple of days, but thank you very much, I appreciate the time you took to reply, I'll look into that function call then, I guess I was barking up the wrong tree in this case, funny that lowering the collision size would trick me like that though :P

      Comment

      Working...
      X