[BUG] Player still doing some goffy movements when he reaches his destination

Hi guys,

My guy is still doing a little dance when he reaches his destination. I am not seeing a solution besides changing
if (playerToClickPoint.magnitude > 0f)
to
if (playerToClickPoint.magnitude > 0.5f)

Has anyone expierenced this and found more favorable solution?

using System;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;
using UnityStandardAssets.CrossPlatformInput;

[RequireComponent(typeof (ThirdPersonCharacter))]
public class PlayerMovement : MonoBehaviour
{
    [SerializeField]
    float walkMoveStopRadius = 0.2f;
    [SerializeField]
    float meleeAttackMoveStopRadius = .5f;
    [SerializeField]
    float rangedAttackMoveStopRadius = 2f;  //Put in place now for future ranged attacks.
    ThirdPersonCharacter ThirdPersonCharacter;   // A reference to the ThirdPersonCharacter on the object    
    [SerializeField]
    bool isInClickToMove = true;
    CameraRaycaster cameraRaycaster;
    private Transform mainCamera;                  // A reference to the main camera in the scenes transform
    private Vector3 CameraForward;             // The current forward direction of the camera
    private Vector3 PlayerMovementDirection;
    Vector3 currentDestination, clickMousePoint;


    private void Start()
    {
        cameraRaycaster = Camera.main.GetComponent<CameraRaycaster>();
        ThirdPersonCharacter = GetComponent<ThirdPersonCharacter>();
        currentDestination = transform.position;
        mainCamera = GameObject.FindGameObjectWithTag("MainCamera").transform;
    }

    // Fixed update is called in sync with physics
    private void FixedUpdate()
    {
        //TODO Have this a selectable option in the options menu later.
        if (Input.GetKeyDown(KeyCode.G)){
            isInClickToMove = !isInClickToMove;
            currentDestination = transform.position;  //Clear the click target.
        }
        if (isInClickToMove)
        {
            ProcessClickToMove();
        }else
        {
            ProcessWASDMovement();
        }
        

    }


    private void ProcessClickToMove()
    {
        if (Input.GetMouseButton(0))
        {
            clickMousePoint = cameraRaycaster.hit.point;
            switch (cameraRaycaster.currentLayerHit)
            {
                case Layer.Walkable:
                    currentDestination = ShortDestination(clickMousePoint, walkMoveStopRadius);  // Set the target move location to the shortened move location.
                    break;
                case Layer.Enemy:
                    currentDestination = ShortDestination(clickMousePoint, meleeAttackMoveStopRadius);  // Set the target move location to the mouse raycast hit point.
                    break;
                case Layer.RaycastEndStop:
                    break;
                default:
                    break;
            }

        }
        WalkToDestination();
    }    

    private void ProcessWASDMovement()
    {
        // read inputs
        float h = CrossPlatformInputManager.GetAxis("Horizontal");
        float v = CrossPlatformInputManager.GetAxis("Vertical");
        bool crouch = Input.GetKey(KeyCode.C);

        // calculate move direction to pass to character
        if (mainCamera != null)
        {
            // calculate camera relative direction to move:
            CameraForward = Vector3.Scale(mainCamera.forward, new Vector3(1, 0, 1)).normalized;
            PlayerMovementDirection = v * CameraForward + h * mainCamera.right;
        }
        else
        {
            //TODO We should never have camera relative directions.  This else section should not be needed.
            //Commented out line on March 9th, 2017, but left in to see if there are any issues.
            // we use world-relative directions in the case of no main camera
            //m_Move = v * Vector3.forward + h * Vector3.right;
        }
#if !MOBILE_INPUT
        // walk speed multiplier
        if (Input.GetKey(KeyCode.LeftShift)) PlayerMovementDirection *= 0.5f;
#endif

        // pass all parameters to the character control script
        ThirdPersonCharacter.Move(PlayerMovementDirection, false, false);
        

    }

    Vector3 ShortDestination( Vector3 dest, float shortening)
    {
        Vector3 reductionVector = (dest - transform.position).normalized * shortening;
        return dest - reductionVector;
    }

    private void WalkToDestination()
        {
            var playerToClickPoint = currentDestination - transform.position;
            if (playerToClickPoint.magnitude > 0f)
            {
                ThirdPersonCharacter.Move(playerToClickPoint, false, false);
            }
            else
            {
            print("I am here!");
                ThirdPersonCharacter.Move(Vector3.zero, false, false);
            }
        }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawLine(transform.position, currentDestination);        
        Gizmos.DrawSphere(currentDestination, 0.1f);
        Gizmos.DrawSphere(clickMousePoint, 0.2f);
    }
}

I saw the same problem with the “goofy” dance. I changed it in code the same way that you did.

That does not seem like it is the correct solution. It feels like all the work that was done is for not. I have moved on for now, but I put a TODO to revisit later.

I will probably do the same @Tim_Neufeld

This started to bug me and i found in my case i could use 0.1f rather than 0.5f and the problem was also gone.
I am yet to experience an issue

Yea, that is not working for me. I have it at 0.5f and it is still doing it. I tried higher and lower numbers and it doesn’t seem to matter. One thing I did find though is that it only does it when I click on a enemy. Does yours stop when you click on an enemy?

The most likely problem is an Enemy has its own stopping distance and it is smaller than player stopping distance.
So enemy is trying to get close and Player is trying to get away.
Try to use the same value in this field for both Player and Enemy.

I went back and tested (mainly as i had to put an Enemy in the scene)
Same effect for me on 0f clicking enemy makes no difference yet changing the float to 0.1f solves all my issues.

After some debugging I’ve narrowed down the issue to the use of Vector3.magnitude. The console is constantly spammed from the moment clicking to walk, and even after reaching destination (when it shouldn’t be). Here is my code and debug info (the debug is about 10 seconds or so after reaching destination, which you can see the constant variations):

UPDATE: Using a value of > .1f should work as @Marc_Carlyon suggested. The problem stems from the animation controller on the Player object, which causes the y-axis coordinate to vary, giving a slight magnitude value. I took a gif as a example:

Code:

Debug:

I believe it has something to do with the changes to the animation speed multiplier and move speed multiplier. That’s why it works for some people and it doesn’t for others. I left walkMoveStopRadius in there:
if (playerToClickPoint.magnitude >= walkMoveStopRadius)
and it works for me almost every time when I walk to a random point. Ethan still dances around every once in a few tries when I stop at attack range.

Yeah, I think that’s it. In the course I believe they set the move speed multiplier to 0.7. As soon as I do that Ethan stops dancing. I don’t like that speed though. I guess I’ll play a bit more with the walkMoveStopRadius.

1 Like

Guys I figured it out (provided that I’m talking about the same bug :stuck_out_tongue: ) . It has to do with the difference in the elevation of the player and the and the destination!!

So the problem is that when the target is higher or lower than the player then the target destination cannot always be reached (as you can see in the image).

There are two fixes I can think of:

  1. Keep moving the general direction of the target (and don’t calculate the new destination) until the player is in range of the clickPoint (!!) according to the stoppingDistance.
  2. Before moving the player make the currentDestination.y equal to the players transform.position.y so that the range is independent of the elevation (don’t know if that made sense to you)

@ben I believe that is a possible fix. what do you think?

p.s (sorry for my terrible writing in the image i used a mouse :stuck_out_tongue: )

UPDATE: That is the code i used. I used the second fix:

private void GoToDestination() {

    // if we are inside the range already don't move
    if ((currentClickTarget - currentDestination).magnitude >=
                      (currentClickTarget - transform.position).magnitude) {
        currentDestination = transform.position;
    }

    currentDestination.y = transform.position.y;
    var distanceToTarget = currentDestination - transform.position;
    // I used a bit of stopping distance to rule out any kind of jerkiness
    if (distanceToTarget.magnitude >= 0.05f) {
        thirdPersonCharacter.Move(distanceToTarget, false, false);
    }
    else {
        thirdPersonCharacter.Move(Vector3.zero, false, false);
    }
}

Aaaand it fixed it for me…

2 Likes

Thanks for all the good responses. I am pondering whether click to move is the best for my game. I am going to keep this open for a bit longer cause it seems to be helping others.

@AntonDan I am not sure your assumption is correct. I had my player and ememy on the same level and it didn’t matter. BUT… I did have a note to check the Y vector on each mouse click to see if it changed. An assumption I had was that if I clicked higher on the enemy that would change the Y vector, causing to go nuts. I have not looked into this assumption at all, so it may not be the case.

@Tim_Neufeld Ah I think I got your problem. I fixed that by not taking the raycast position when I was clicking on an enemy, but by taking the gameObject of the enemy and finding its position. that means that when they are on the same level you get the same Y. Which is exactly what I said defore. The difference in the Y attribute is making it go nuts because the destination it wants to go to is higher or lower (depending on where you clicked) than the one the player can actually go! even if that difference is small you have to have a pretty big stopping distance in order to compensate

This is what I did:

 GameObject enemyTarget = cameraRaycaster.hit.collider.gameObject;
 currentClickTarget = enemyTarget.transform.position;
2 Likes

@AntonDan Yes that seems like a good solution. Not sure why I didn’t think of that. Thanks.

2 Likes

I’ve posted a kind of fix here

This is the same approach that I used and I found that the best way round this was to change the float to “1f”.

Privacy & Terms