Search code examples
c#unity-game-enginedelay

Delay before a specific action in OnTriggerStay()


I'm making a 3d-shooter in Unity. I want to let player nail up windows and doors so that he had a cover. Holes in the walls like windows and doors already have inactive boards on them. I'd like to have the player hold the button when he's near the window to set a board active. So, if it is required to nail down three boards to completly shut up the window, the player has to be near the window and hold a button, for instance, for 10 seconds to place one board. Same for the second and third boards. This process, however, can be interrupted and the window can have only one or two boards nailed down. Windows have invisible triggers that detect player's presence.

Here's the code attached to windows:

using System.Collections.Generic;
using UnityEngine;

public class ActivateBoards : MonoBehaviour
{
    public float delay = 10f;
    public List<GameObject> boards;
    private int currentAmount = 0;
    private int totalAmount;
    private float elapsed = 0f;
    // Start is called before the first frame update
    void Start()
    {
        totalAmount = boards.Count;
    }

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

    }

    private void OnTriggerStay(Collider other)
    {
        if (other.gameObject.CompareTag("Player") && Input.GetButtonDown("Fire1") && currentAmount != totalAmount)
        {
            elapsed += Time.deltaTime;
            if (elapsed >= delay)
            {
                boards[currentAmount].SetActive(true);
                currentAmount++;
                elapsed = 0;
            }
        }
    }
}

I tried to use a coroutine but it's a no go since the player can release the button right in the middle of yield return new WaitForSeconds() command. Also, I'd like to attach a UI image that would play a role of a progress bar and fill up as time goes by. I don't really get along with time in Unity - the code above doesn't work at all. And I suppose that Time.deltaTime only works well in Update() method? I need fuctionality from OnTriggerStay() to detect player and functionality from Update() to count time. How can I achieve it?


Solution

  • Your main issue I'ld say is that your are using GetButtonDown which is only true exactly one frame per button press.

    You should rather use GetButton which is true every frame as long the button stays pressed!

    private void OnTriggerStay(Collider other)
    {
        if (!other.gameObject.CompareTag("Player") return;
    
        // instead of totalAmount you could directly use the boards.Length (or .Count for a List)
        // I would also rather check for < instead of !=
        if(Input.GetButton("Fire1") && currentAmount < boards.Length)
        {
            elapsed += Time.deltaTime;
            if (elapsed >= delay)
            {
                boards[currentAmount].SetActive(true);
                currentAmount++;
                elapsed = 0;
            }
        }
        else if (Input.GetButtonUp("Fire1"))
        {
            // Button was released
            elapsed = 0;
        }
    }
    
    // I would then also reset if player leaves the collider
    private void OnTriggerExit (Collider other)
    {
        if (!other.gameObject.CompareTag("Player") return;
    
        elapsed = 0;
    }