unity-game-enginetimerdisabled-input

Unity2D Disable player movement for X seconds


I am trying to disable a player movement for 1 second when it collides with a specific object. The object collides and it detects that just fine however currently the entire game freezes and does not return.

Ive implemented a 0 value (as found in other examples) to the X and Y movements with a timer. This freezes the game entirely and I have to kill process and reopen Unity.

public Boolean frozen = false;
    float shipDisabledTimer = 1f;
    // Start is called before the first frame update
    void Start()
    {
        SetUpMoveBoundaries();
        gameSession = FindObjectOfType<GameSession>();

    }
    // Update is called once per frame
    void Update()
    {

        Move(frozen);
        Fire();

        if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("Game"))
        {
            if (Input.GetKey(KeyCode.Escape))

                SceneManager.LoadScene("Game Over");
        }
    }



    public void Move(Boolean Frozen)
    {

        UnityEngine.Debug.Log("Update1 - " + Frozen);



            var deltaX = Input.GetAxis("Horizontal") * Time.deltaTime * moveSpeed;
            var deltaY = Input.GetAxis("Vertical") * Time.deltaTime * moveSpeed;

            var newXPos = Mathf.Clamp(transform.position.x + deltaX, xMin, xMax);
            var newYPos = Mathf.Clamp(transform.position.y + deltaY, yMin, yMax);
            transform.position = new Vector2(newXPos, newYPos);

        if (frozen == true)
        {
            float timer = 0f;
            timer += Time.deltaTime;
            UnityEngine.Debug.Log(frozen);
            while (timer < shipDisabledTimer)
            {
                newXPos = 0;
                newYPos = 0;

            }
            disableship(frozen = false);

        }

    }


    public Boolean disableship(Boolean frozen)
    {
        return frozen;
    }
    private void OnTriggerEnter2D(Collider2D other)
    {
        UnityEngine.Debug.Log(other.gameObject.name);
        if (other.gameObject.name.Equals("Orb Shot(Clone)"))
        {
            UnityEngine.Debug.Log("Orb hit player");
            //StartCoroutine(countdownTimer());
            disableship(frozen = true);
        }
    }

The ship alone should freeze. No other game object should freeze. All scores, counters, enemies should continue onward. Only the player X,Y are disabled for 1 second.


Solution

  • This

    while (timer < shipDisabledTimer)
    {
        newXPos = 0;
        newYPos = 0;
    }
    

    entirely blocks your game's main thread since the timer nor shipDisabledTime are not changed within the loop so it will never end.

    There are a few ways you could go here.

    Invoke

    Invoke allows you to call a method after a certain delay so you could easily do something like

    public void Freeze()
    {
        frozen = true;
        Invoke(nameof(Unfreeze), shipDisabledTime);
    }
    
    private void Unfreeze()
    {
        frozen = false;
    }
    

    Coroutine

    A Coroutine is like a temporary Update method. Simplest way in your case is using WaitForSeconds and do something like

    public void Freeze()
    {
        StartCoroutine (FreezeRoutine());
    }
    
    private IEnumerator FreezeRoutine()
    {
        frozen = true;
    
        yield return new WaitForSeconds(shipDisabledTime);
    
        frozen = false;
    }
    

    simple timer

    Or you could use a simple timer but in Update (not a while loop) like

    private float timer = -1;
    
    public void Freeze()
    {
        frozen = true;
        timer = shipDisabledTime;
    }
    
    private void Update()
    {
        if(timer > 0)
        {
            timer -= Time.deltaTime;
            if(timer <= 0)
            {
                timer = -1;
                frozen = false;
            }
        }
    
        ...
    }
    

    And then either way you simply don't take any movement input in the meantime

    public void Move()
    {
        if(frozen) return;
    
        ...
    }
    

    Except for the Invoke solution you can also extend them and decided whether you want to e.g. stack multiple freezes, ignore them while already frozen or simply start the timers over.


    General note: In c# rather simply use bool it's basically the same but easier to read and write ;)


    Also note that this call

    disableship(frozen = false);
    
    ...
    
    public Boolean disableship(Boolean frozen)
    {
        return frozen;
    }
    

    Is pretty strange ... first of all this method does absolutely nothing than just return the same value you pass in as parameter .. you are hiding the frozen field with a same named parameter so this does nothing!

    Second your method returns a Boolean but you are not assigning it to anything.

    If you want to change the value simply set it using frozen = XY but don't pass this in as parameter further.


    Aaand avoid calling Debug.Log every frame .. it will slow down your app even in a build where you don't even see the log!