Home > Net >  How do I get the for loop to only loop every time the player presses Z
How do I get the for loop to only loop every time the player presses Z

Time:12-17

I am trying to cycle through a list of dialogue to update a text component.

The var i is only supposed to increases when the player presses z but every time I try the var i increases to the max value instead of once every time I press Z.

IEnumerator gameDialog ( )
{
    while ( i < prologueDialog.Count )
    {
        PlayerPrefs.SetInt ( "StoryProgress(Prologue)", i );
        currentListIndex = PlayerPrefs.GetInt ( "StoryProgress(Prologue)" );

        if ( i == 0 )
        {
            dialogText.text = prologueDialog [ 0 ];
        }

        if ( Input.GetKey ( KeyCode.Z ) )
        {
            pressedZ = true;
            dialogText.text = "";
            dialogText.text = prologueDialog [ i ];
        }

        yield return new WaitUntil ( ( ) => pressedZ );
        pressedZ = false;
        oldi = i;
        i  ;
        if ( oldi   1 != i )
        {
            i = oldi   1;
        }
        Debug.Log ( i );
    }
}

I've tried everything I could think of using yield, WaitUntil, WaitForSeconds, bool values to stop the if statement from running more than once.

CodePudding user response:

Input.GetKey will return true while the key is held down. What you're after is likely Input.GetKeyDown which returns true during the frame it's initially pressed, and will not return true until the same key has been released.

CodePudding user response:

The WaitUntil IEnumerator causes the code execution to pause at that spot, until the function predicate returns true. What that means in layman's terms is that the code is going to stop on that line, and move no further, until pressedZ somehow becomes true. But, given that the code won't progress, you can start to see that the keys aren't ever read again, and therefore, pressedZ will never become true.

One thing to note is that saving any game state to the PlayerPrefs is almost universally frowned upon. But that's another issue that can be resolved after getting your dialogue running.

Instead of trying to poll your keyboard input in a "coroutine", might I suggest you use the already available Update method to use as your frame based polling mechanism? It also still includes the PlayerPrefs persistence, which I would suggest replacing at your earliest convenience. This has been written, but not tested.

public class Test : MonoBehaviour
{
    [SerializeField] private TextMeshPro dialogText;

    private List<string> _dialoguePhrases = null;
    private string _storyName;
    private int _dialogueIndex = 0;

    private void Awake ( )
    {
        HideDialogue ( );
    }

    public void ShowDialogue ( List<string> dialoguePhrases, string storyName = "StoryProgress(Prologue)" )
    {
        _dialogueIndex = PlayerPrefs.GetInt ( storyName, 0 );

        // check the saved progress index with the newly passed in dialoguePhrases (or -1 if null).
        if ( _dialogueIndex >= ( dialoguePhrases?.Count ?? -1 ) )
        {
            HideDialogue ( );
            return;
        }

        _dialoguePhrases = dialoguePhrases;
        _storyName = storyName;
        dialogText.text = dialoguePhrases [ _dialogueIndex ];
        enabled = true; // Lets the Update method run.
    }

    public void HideDialogue ( )
    {
        _dialoguePhrases = null;
        _dialogueIndex = 0;
        dialogText.text = string.Empty;
        enabled = false; // Stops the Update method from being called.
    }

    private void Update ( )
    {
        if ( Input.GetKeyDown ( KeyCode.Z ) && _dialoguePhrases != null )
        {
            if (   _dialogueIndex < _dialoguePhrases.Count )
            {
                dialogText.text = _dialoguePhrases [ _dialogueIndex ];
                PlayerPrefs.SetInt ( "StoryProgress(Prologue)", _dialogueIndex );
            }
            else
            {
                // You've clicked on the last one, and now you need to do something after the dialogue has finished.
                HideDialogue ( );
            }
        }
    }
}

The idea here is that you send through a list of dialogue phrases, and a name (id) of where the dialogue state would be persisted (preferably not in the PlayerPrefs).

The Update method is then enabled, when the component itself has it's enable flag set to true. The normal checks are made in Update, and when the end of the dialogue is reached, the component turns itself off waiting for the next show dialogue command.

  • Related