0

I was working on a script where, if you click the right arrow, the player moves at a constant speed towards the right side of the screen, then stops after leaving the screen. (Similarly, if you click the left arrow, the player moves at a constant speed towards the left side of the screen, then stops after leaving the screen.)

However, after I added the code, I encountered a problem where the player was moving very slowly, little by little. Additionally, I'm not sure if my code is concise.

I used a while loop to move the player to the target position, but the code severely overloaded the computer, forcing me to close Unity. I'm worried that this will cause problems with the computer. The code caused severe lag, and I have no idea what is causing it. Please help me...

Here is my code:(Logic includes moving with direction keys)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class PlayerManager : MonoBehaviour
{
    public float speed = 500f;
    public float runSpeed = 200f;

    public GameObject player;
    public GameObject rightArrow;
    public GameObject leftArrow;

    private Vector2 movement; 
    private bool isRunning;
    private Animator anim;
    private Rigidbody2D rb;

    private void Start()
    {
        anim = player.GetComponent<Animator>();
        rb = player.GetComponent<Rigidbody2D>();
    }

    
    private void Update()
    {
        // Get input
        movement.x = Input.GetAxisRaw("Horizontal");
        movement.y = Input.GetAxisRaw("Vertical");
        isRunning = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);

        
        if (Input.GetMouseButtonDown(0))
        {
            Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector2.zero);

            if (hit.collider != null)
            {
                if (hit.collider.gameObject == rightArrow)
                {
                    int clickX = 1;
                    ArrowClick(clickX);
                }
                else if (hit.collider.gameObject == leftArrow)
                {
                    int clickX = -1;
                    ArrowClick(clickX);
                }
            }
        }
    }

    public void ArrowClick(int _clickX)
    {
        Vector2 target = new Vector2(100 * _clickX, 0);
        while (true)
        {
            player.transform.position
            = Vector2.MoveTowards(player.transform.position, target, 10);
        }
    }


    private void FixedUpdate()
    {
        // Move the character
        float currentSpeed = isRunning ? speed * runSpeed : speed;
        Vector2 velocity = movement.normalized * currentSpeed;
        rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);

        
    }

    private void LateUpdate()
    {
        // Update animator
        anim.SetFloat("DirX", movement.x);
        anim.SetFloat("DirY", movement.y);
        anim.SetBool("Idle", movement.magnitude == 0);

        
    }
}
3
  • "but the code severely overloaded the computer, forcing me to close Unity" - No, you just made an infinite loop. What did you expect Unity to do except wait for you call to end?
    – Max Play
    Commented Jul 10 at 11:34
  • Since the target is constant, I expected it to not run when it arrived at the target Commented Jul 11 at 1:16
  • But you don't check against anything? You just pass true which results in the loop running forever.
    – Max Play
    Commented Jul 11 at 6:45

1 Answer 1

0

You made an infinite loop that has no exit condition (while (true) will never be false...), so when it seemed that Unity became unresponsive, it was still executing the code within the while (true) over. and over. and over. etc.

Second, moving an object (or doing anything, really) in a loop like that will happen within a single frame, which would result in your object teleporting to the destination.

You can solve both of these issues at the same time by instead making your ArrowClick function into a Coroutine, which is a single piece of code (function) that can pause and resume execution:

public IEnumerator ArrowClick(int _clickX)
{
    float speed = 10;
    Vector2 target = new Vector2(100 * _clickX, 0);
    
    // Changed "while true" to be "while far from the destination"
    while (Vector2.Distance(player.transform.position, target) > 0.05f)
    {
        // Changed from hardcoded "10" to rather be based on the "Time.deltaTime": enables framerate independence (player will move the same amount in a second, regardless of FPS)
        player.transform.position = Vector2.MoveTowards(player.transform.position, target, speed * Time.deltaTime);
 
        // Tells the game to wait a frame before continuing execution
        yield return null;
    }
}

Which then can be started as such:

// This is a class variable, put it at the top
Coroutine playerMoveRoutine;

//Inside your click code
if (hit.collider.gameObject == rightArrow)
{
    int clickX = 1;
    // If we're already moving, cancel that coroutine so the two don't execute at the same time
    if (playerMoveRoutine != null)
    {
        StopCoroutine(playerMoveRoutine);
    }
    playerMoveRoutine = StartCoroutine(ArrowClick(clickX));
}
else if (hit.collider.gameObject == leftArrow)
{
    int clickX = -1;
    // If we're already moving, cancel that coroutine so the two don't execute at the same time
    if (playerMoveRoutine != null)
    {
        StopCoroutine(playerMoveRoutine);
    }
    playerMoveRoutine = StartCoroutine(ArrowClick(clickX));
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.