Detect if condition is true for a period of time

Hi. I’m building an auto-rotating camera for my third person game. I built my camera’s auto-rotate code, but now I need to tell the camera WHEN to do it. How do I detect if a condition is true for a period of time? I want the camera to rotate when the player has been moving for about half of a second. You can write pseudocode to answer.

I’ve created a MotionDetector component.

Even though it’s called a detector, it doesn’t automatically monitor the behavior of an object (the subject). Instead, the subject (the player, for instance) has to inform the MotionDetector that one of the two available actions has been performed. This is done via the MotionDetector.ActionPerformed(..) method.
Available actions (enums):

  • MotionDetector.SubjectAction.Move

  • MotionDetector.SubjectAction.Idle

How to test it

Before using it in your game, I recommend that you create a simple test without graphics, audio, menus, etc.

  1. Create two new files, MotionDetector.cs and TestPlayer.cs, and copy/paste the code I provided below.

  2. Create a scene that contains two empty gameobjects called “MotionDetector” and “TestPlayer”.

  3. Add the MotionDetector component to the “MotionDetector” gameobject.

  4. Add the TestPlayer component to the “TestPlayer” gameobject.

  5. Select the “TestPlayer” gameobject and the Unity Inspector should reveal a reference called “MotionDetector”. Point this to the “MotionDetector” gameobject.

  6. Open the MotionDetector.cs file and change the following class members from private to public so you can keep an eye on their values while the Unity player is running:

  • detectedTheExpectedActionForThisManySeconds

  • interruptionDurationInSeconds

  1. Start the Unity player and press the W·S·A·D keys while looking at the console and the MotionDetector component in the Inspector.
    Try different movement patterns:
  • Long-press W
  • Tap W, D, W, D repeatedly (Zig-zag)
  • Tap D repeatedly
  • Etc…
  1. Adjust the values on the MotionDetector component in the Inspector if you wish.
    Of course, you shouldn’t attempt to adjust the ones you changed from private to public as they are used internally.

  2. Set AllowedInterruptionDurationInSeconds to 0 in the Inspector and try the Zig-zag movement pattern again (W, D, W, D). As you can see, without allowing short delays between presses it becomes practically impossible to detect the movement and therefore, your camera would just stand still. That’s why I asked you in a comment what should happen if the player taps the keys instead of holding them down.

  3. Change the MotionDetector’s class members from Step 6 back to private when you’re done testing.

Let me know if you have any questions.

The code

TestPlayer component:

public class TestPlayer : MonoBehaviour
{
    public MotionDetector MyMotionDetector;

    private void Update()
    {
        // TODO: Change the conditions to suit your needs
        bool hasMovedOnThisFrame =
            Input.GetKey(KeyCode.W) ||
            Input.GetKey(KeyCode.S) ||
            Input.GetKey(KeyCode.A) ||
            Input.GetKey(KeyCode.D);

        if (hasMovedOnThisFrame)
        {
            // Informs the MotionDetector that the player moved on this frame
            MyMotionDetector.ActionPerformed(MotionDetector.SubjectAction.Move);
        }
        else
        {
            // Informs the MotionDetector that the player was idle on this frame
            MyMotionDetector.ActionPerformed(MotionDetector.SubjectAction.Idle);
        }
    }
}

MotionDetector component:

public class MotionDetector : MonoBehaviour
{
    // The two actions that the MotionDetector can monitor
    public enum SubjectAction { Move, Idle }

    // The subject action that the MotionDetector is waiting for
    public SubjectAction ExpectedAction = SubjectAction.Move;

    // How long the subject has to stay still before it's considered idle
    public float SecondsUntilConsideredIdle = 0.5f;

    // How long the subject has to keep moving before it's considered in motion
    public float SecondsUntilConsideredMoving = 0.5f;
    
    // How long the subject has been performing the expected action
    private float detectedTheExpectedActionForThisManySeconds;

    // How long the subject is allowed to interrupt the expected action
    public float AllowedInterruptionDurationInSeconds = 0.3f;

    // How long the subject has interrupted the expected action
    private float interruptionDurationInSeconds;

    private void Start()
    {
        LogExpectedAction(); // Debugging
    }

    public void ActionPerformed (SubjectAction recentAction)
    {
        if (ExpectedAction == recentAction)
        {
            // The subject's recent action is the expected one

            interruptionDurationInSeconds = 0;
            detectedTheExpectedActionForThisManySeconds += Time.deltaTime;

            if (detectedTheExpectedActionForThisManySeconds >= GetSecondsToWaitForTheExpectedAction())
            {
                detectedTheExpectedActionForThisManySeconds = 0;
                // Switches the ExpectedAction from Idle to Move or vice versa
                ExpectedAction = ExpectedAction == SubjectAction.Idle ? SubjectAction.Move : SubjectAction.Idle;

                LogExpectedAction(); // Debugging
            }
        }
        else
        {
            // The subject's recent action is not the expected one

            interruptionDurationInSeconds += Time.deltaTime;

            if (interruptionDurationInSeconds >= AllowedInterruptionDurationInSeconds)
            {
                interruptionDurationInSeconds = 0;
                detectedTheExpectedActionForThisManySeconds = 0;
            }
        }
    }

    private float GetSecondsToWaitForTheExpectedAction ()
    {
        return ExpectedAction == SubjectAction.Idle
            ? SecondsUntilConsideredIdle
            : SecondsUntilConsideredMoving;
    }

    // Debugging
    private void LogExpectedAction ()
    {
        if (ExpectedAction == SubjectAction.Idle)
            Debug.Log($"MOVING - Now waiting for the subject to become IDLE    (#{logNumber++})");

        else if (ExpectedAction == SubjectAction.Move)
            Debug.Log($"IDLE - Now waiting for the subject to MOVE    (#{logNumber++})");
    }

    private int logNumber = 1; // Debugging -- Used to prevent Unity's console from collapsing repeated messages
}