Point and Click programming

A continuation of this thread kinda. I’ve been working on a Point and click demo, currently using a raycast and the movetoward method. however when the method is called, the player only moves one step toward the hit point. using a while loop causes an infinite loop and the editor hangs. what should I do to get the player to smoothly move to the clicked on point?

here’s my current code:

public override void Movement()
        {
            mousePos = Input.mousePosition;
            ray = cam.ScreenPointToRay(mousePos);
            var mask = layer;//layermask

           

            RaycastHit hit;//location of point ray hit
            if(Input.GetMouseButtonDown(0))//if LMB is clicked
            {
                if (Physics.Raycast(ray, out hit, Mathf.Infinity, ~mask))//if a ray hits
                {
                    Debug.Log(hit.point);//return hit location
                    Debug.Log(hit.collider.gameObject);//return hit object

                   
                    Travel(hit.point);//travel to point
                }
            }
        }
public void Travel(Vector3 destination)
        {
            //go to
                transform.position = Vector3.MoveTowards(transform.position, destination, .2f);//move toward location
        }

MoveToward does exactly what it sounds like. It moves a little bit from A to B, by a distance of the third parameter. Usually when people use MoveTowards they call it once each frame until the object reaches its destination. You’re literally just calling it once here, so naturally it only moves once.

What you do is:

When the player clicks the button, store the destination you want the player to go to (a Vector3 variable). Also store a bool variable saying that you are now moving (isMoving = true).

Then, in Update, if you see that you are moving (check the bool), call MoveTowards to move a little bit towards the destination, then if you have reached the destination, you can set isMoving = false.

One final note: you generally want to multiply the third parameter of MoveTowards by Time.deltaTime. This will ensure that your movement is smooth across different framerates. Otherwise your movement speed will change depending on the framerate of the game.

ah I see, I originally had “while (transform.position != destination)” but that kept hanging up. will moving the bool check prevent that?

EDIT: also, the way my player script is structured, one script handles receiving input and the other handles the response in a listener pattern(this is so that I can change control types quickly)
and the above script is the listener, and as such, doesn’t feature an update method.

void Update()
        {
            controller.Movement();
            startPing();
        }

this is what the update looks like of the other script

You don’t want a while loop here. Remember, update runs once per frame. What you should be going for is moving a little bit each frame. That means just move a little bit in Update and wait for Unity to call Update again.

okay, so if my movement method is in update, and my travel method is in my movement method, why does my current code only make it move one step?

Also I attempted to add Time.deltaTime and it just stops working. Wont even move one step if I add that.

EDIT: sorta got it working

because your current code is inside an if statement: if(Input.GetMouseButtonDown(0)). That is only true during the one frame during which the user clicked the mouse button. So you’re only calling MoveTowards once, during that single frame. What you should do is set a destination and

Currently the code is a little…odd. Now when the script starts the player moves away from the camera a little bit, and the player jitters when it reaches the destination

Update 2: after adding the isMoving boolean I’m back to square one

 public override void Movement()
        {
            mousePos = Input.mousePosition;
            ray = cam.ScreenPointToRay(mousePos);
            var mask = layer;//layermask

            bool isMoving = false ;
           

            RaycastHit hit;//location of point ray hit
            if(Input.GetMouseButtonDown(0))//if LMB is clicked
            {
                if (Physics.Raycast(ray, out hit, Mathf.Infinity, ~mask))//if a ray hits
                {
                    Debug.Log(hit.point);//return hit location
                    Debug.Log(hit.collider.gameObject);//return hit object
                    destination = hit.point;
                    isMoving = true;
                }
            }
            if(isMoving == true)
            {
                Travel(destination);//travel to point
            }
            if (transform.position == destination)
            {
                isMoving = false;
            }
           
        }

after adding if(isMoving == true), the player is back to only moving one step per click

Currently your isMoving variable is a local variable. That means it gets created anew every time Movement() runs. You also explicitly set it to false every frame on line 7. You need to make it an instance variable instead by declaring it outside the method:

bool isMoving = false;

public override void Movement()
        {
            mousePos = Input.mousePosition;
            ray = cam.ScreenPointToRay(mousePos);
            var mask = layer;//layermask
         
            RaycastHit hit;//location of point ray hit
            if(Input.GetMouseButtonDown(0))//if LMB is clicked
            {
                if (Physics.Raycast(ray, out hit, Mathf.Infinity, ~mask))//if a ray hits
                {
                    Debug.Log(hit.point);//return hit location
                    Debug.Log(hit.collider.gameObject);//return hit object
                    destination = hit.point;
                    isMoving = true;
                }
            }
            if(isMoving)
            {
                Travel(destination);//travel to point
            }
            if (transform.position == destination)
            {
                isMoving = false;
            }
         
        }
1 Like

You should not do a check like line 25, which compares floating point values for equality.

Instead, check the distance by subtracting the two and seeing if the magnitude of that is less than a small amount.

if ((transform.position - destination).magnitude < 0.1f) // small amount choice up to you
{
  isMoving = false;
}
3 Likes