-1

Whenever lives get removed by an enemy in my game in unity, the game temporarily freezes. I think it has to do with the line "Thread.Sleep(3000);" but I am not sure of an alternative wait that will not freeze the entire game temporarily. I would greatly appreciate if you could help me!

Thanks.

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

public class Enemy : MonoBehaviour
{

    public Transform attackPoint;
    public float attackrange = 0.5f;
    public LayerMask playerlayers;
    public float speed = 3f;
    private Transform target;

    bool hitID = false;


    private void Update()
    {
        if (target != null)
        {
            float step = speed * Time.deltaTime;
            transform.position = Vector2.MoveTowards(transform.position, target.position, step);
            Collider2D[] hitplayers = Physics2D.OverlapCircleAll(attackPoint.position, attackrange, playerlayers);
            foreach (Collider2D player in hitplayers)
            {
                if (hitID == false)
                {
                    hitID = true;
                    player.GetComponent<HeartSystem>().TakeDamage(1);
                    Thread.Sleep(3000);
                    hitID = false;
                }

            }
                    
        }
    }


    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.tag == "Player")
        {
            target = other.transform;


        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {

        if (other.gameObject.tag == "Player")
        {
            target = null;

        }
    }

    void OnDrawGizmosSelected()
    {
        if (attackPoint == null)
            return;

        Gizmos.DrawWireSphere(attackPoint.position, attackrange);
    }
}

2 Answers2

0

Enemy.cs

using System.Collections;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public Transform attackPoint;
    public float attackrange = 0.5f;
    public LayerMask playerlayers;
    public float speed = 3f;

    private Transform _target;
    private bool _hitID;

    private void Update()
    {
        if (_target != null)
        {
            var step = speed * Time.deltaTime;
            transform.position = Vector2.MoveTowards(transform.position, _target.position, step);
            var hitPlayers = new Collider2D[10];
            Physics2D.OverlapCircleNonAlloc(attackPoint.position, attackrange, hitPlayers, playerlayers);
            foreach (var player in hitPlayers)
            {
                if (_hitID == false)
                {
                    StartCoroutine(HitCoroutine(player));
                }
            }
        }
    }

    private IEnumerator HitCoroutine(Collider2D player)
    {
        _hitID = true;
        player.GetComponent<HeartSystem>().TakeDamage(1);
        yield return new WaitForSeconds(3);
        _hitID = false;
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.CompareTag("Player"))
        {
            _target = other.transform;
        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.gameObject.CompareTag("Player"))
        {
            _target = null;
        }
    }

    private void OnDrawGizmosSelected()
    {
        if (attackPoint == null)
            return;

        Gizmos.DrawWireSphere(attackPoint.position, attackrange);
    }
}

Try this.

In a case like this you want to use a coroutine.

You can read about coroutines here: https://docs.unity3d.com/Manual/Coroutines.html

Lyxodius
  • 344
  • 1
  • 5
  • 12
0

Unity is single threaded, so you are actually suspending the entire thread the game is running on. Since the frames are updated on this thread, you're even freezing the visual frame updates.

You have a few ways to manage "waiting" in game.

Coroutines:

A process that can be suspended until a later condition is met (including time)


void Update()
{
    //Your other code goes here
    if (hitID == false)
    {
        hitID = true;
        player.GetComponent<HeartSystem>().TakeDamage(1);
        StartCoroutine(ResetHitIDAfterSeconds(3))
    }
}

IEnumerator ResetHitIDAfterSeconds(float seconds) 
{
    yield return new WaitForSeconds(seconds);
    hitID = false;
}

The above code allows you to have the same logic, and will kick off a coroutine that waits for 3 seconds before setting hitID back to false.

Time.deltaTime:

You can also track the elapsed time in your Update method and manage your variables actively based on the elapsed time. Time.deltaTime returns the amount of time that elapsed since the previous frame.

public class ConstantRotation : MonoBehaviour
{
    public float timeElapsed = 0f;
    void Update()
    {
        //Your other code goes here
        if (hitID == false)
        {
            hitID = true;
            player.GetComponent<HeartSystem>().TakeDamage(1);
            timeElapsed = 0f;
        }
        else 
        {
            timeElapsed += Time.deltaTime
            if(timeElapsed >= 3.0f)
            {
                hitId = false;
            }
        }
    }
}

You would likely want to modify the above code a bit to avoid having the logic run on every frame, and only run it on hit.

Erik Overflow
  • 2,220
  • 1
  • 5
  • 16