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.