Enes Korkmaz
Enes Korkmaz

Reputation: 70

Inter-functional timing error (C#, Unity)

I have 2 scripts (RunControl.cs and FireControl.cs) that depend on mouse click event.

RunControl.cs : Triggered by a button at the top of the screen. When clicked, the game stops.

FireControl.cs : Allows the character to fire when the mouse is clicked.

Problem: Since I have to click when I want to stop the game, first the character shoots and then the game stops. I tried the following codes for blocking but I couldn't block it.

//RunControll.cs
private void Start()
    {
        isRun = true;
    }
    public void OnButtonClick()
    {
        if(btn.image.sprite == runSprite)
        {
            btn.image.sprite = stopSprite;
            FindObjectOfType<FireControl>().isRun = false;
            isRun = false;
            
        }
        else
        {
            btn.image.sprite = runSprite;
            FindObjectOfType<FireControl>().isRun = true;
            isRun = true;
            
        }
    }
//FireControl.cs
private void Start()
    {
        isRun = true; //Controlled by RunControl.cs
    }

    private void Update()
    {
        if (isRun)
        {
            if (Input.GetKeyDown(KeyCode.Mouse0))
            {
                fire = true;
            }
            else
            {
                fire = false;
            }
        }
    }

!!! Changing FireControl.cs to lateupdate didn't work

Upvotes: 1

Views: 97

Answers (1)

dgates82
dgates82

Reputation: 418

You are creating a 'race condition' - which will happen first, disabling the game, or firing. A singleton GameManager class that keeps track of state and is updated by the OnClick and read by the FireControl would go a long way in cleaning up your code, but it would not fix the race condition.

I would take a look at this answer to the question "How to detect left mouse click but not when the click occur on a UI Button component?"

Based off that answer you could update your FireControl to not fire when clicking over the button, which would avoid the race condition all together


    if (isRun)
    {
        if (Input.GetKeyDown(KeyCode.Mouse0) && !EventSystem.current.IsPointerOverGameObject())
        {
            fire = true;
        }
        else
        {
            fire = false;
        }
    }

Option 2 - Using RayCast

Tag your button so it is identifiable, we'll say as "button". Then do a RayCast and check if pointer is over button, and if so - do not fire

var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
var overButton = false;

if (Physics.Raycast(ray, out var hit)
{
    overButton = hit.collider.tag == "button";
}
else 
{
    overButton = false;
}

// Continue with your fire logic
if (Input.GetKeyDown(KeyCode.Mouse0) && !overButton)
{
    // Fire
}

The above is a little messy and could be opitmized, but should work

Upvotes: 1

Related Questions