563 lines
17 KiB
C#
563 lines
17 KiB
C#
using EasyTalk.Nodes.Utility;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
using EasyTalk.Controller;
|
|
using EasyTalk.Localization;
|
|
|
|
#if TEXTMESHPRO_INSTALLED
|
|
using TMPro;
|
|
#endif
|
|
|
|
namespace EasyTalk.Display
|
|
{
|
|
/// <summary>
|
|
/// A display for obtaining text input from the plpayer during dialogue playback.
|
|
/// </summary>
|
|
public class TextInputDisplay : DialoguePanel
|
|
{
|
|
/// <summary>
|
|
/// The text input field.
|
|
/// </summary>
|
|
[SerializeField]
|
|
private InputField inputField;
|
|
|
|
#if TEXTMESHPRO_INSTALLED
|
|
/// <summary>
|
|
/// The text input field (TextMesh Pro version).
|
|
/// </summary>
|
|
[SerializeField]
|
|
private TMP_InputField textMeshProInputField;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// The PlayerInput node being processed.
|
|
/// </summary>
|
|
private PlayerInputNode inputNode;
|
|
|
|
/// <summary>
|
|
/// Whether the input display should be hidden immediately on awake.
|
|
/// </summary>
|
|
private bool hideOnAwake = true;
|
|
|
|
/// <summary>
|
|
/// The default set of characters which the player can cycle through if using a gamepad.
|
|
/// </summary>
|
|
private char[] defaultCharacters = new char[] {
|
|
' ',
|
|
'A', 'B', 'C', 'D', 'E', 'F',
|
|
'G', 'H', 'I', 'J', 'K', 'L',
|
|
'M', 'N', 'O', 'P', 'Q', 'R',
|
|
'S', 'T', 'U', 'V', 'W', 'X',
|
|
'Y', 'Z', 'a', 'b', 'c', 'd',
|
|
'e', 'f', 'g', 'h', 'i', 'j',
|
|
'k', 'l', 'm', 'n', 'o', 'p',
|
|
'q', 'r', 's', 't', 'u', 'v',
|
|
'w', 'x', 'y', 'z'};
|
|
|
|
/// <summary>
|
|
/// The set of input characters in use (only applicable when cycling via a gamepad).
|
|
/// </summary>
|
|
private char[] inputCharacters = null;
|
|
|
|
/// <summary>
|
|
/// The index of the character currently chosen as the gamepad input character.
|
|
/// </summary>
|
|
private int currentCharacter = 0;
|
|
|
|
/// <summary>
|
|
/// Whether the display should quickly cycle forward through input characters (if a gamepad button is held down).
|
|
/// </summary>
|
|
private bool cyclingCharactersForward = false;
|
|
|
|
/// <summary>
|
|
/// Whether the display should quickly cycle backwards through input characters (if a gamepad button is held down).
|
|
/// </summary>
|
|
private bool cyclingCharactersBackward = false;
|
|
|
|
/// <summary>
|
|
/// Keeps track of the last time at which the input character was cycled (only relevant when a gamepad button is held down).
|
|
/// </summary>
|
|
private float lastCycleTime = 0.0f;
|
|
|
|
/// <summary>
|
|
/// /The delay between switching input characters when they are being cycled (only relevant when a gamepad button is held down).
|
|
/// </summary>
|
|
private float cycleDelay = 0.1f;
|
|
|
|
/// <inheritdoc>
|
|
private void Awake()
|
|
{
|
|
Init();
|
|
|
|
if (hideOnAwake)
|
|
{
|
|
HideImmediately();
|
|
}
|
|
|
|
onShowComplete.AddListener(FocusTextInput);
|
|
|
|
if (forceStandardText)
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
inputField.onSubmit.AddListener(TextInputEntered);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
#if TEXTMESHPRO_INSTALLED
|
|
if (textMeshProInputField != null)
|
|
{
|
|
textMeshProInputField.onSubmit.AddListener(TextInputEntered);
|
|
}
|
|
#else
|
|
if (inputField != null)
|
|
{
|
|
inputField.onSubmit.AddListener(TextInputEntered);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//Update the character set being used based on the currently set language
|
|
UpdateInputCharacterSet();
|
|
|
|
//Register the display with the game state so that we can detect when the language changes and update the character set
|
|
EasyTalkGameState.Instance.onLanguageChanged += LanguageChanged;
|
|
}
|
|
|
|
/// <inheritdoc>
|
|
private void OnDestroy()
|
|
{
|
|
//Unregister from the game state
|
|
EasyTalkGameState.Instance.onLanguageChanged -= LanguageChanged;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called whenever the language is changed on the EasyTalkGameState. This will attempt to update the character set used by the input display.
|
|
/// </summary>
|
|
/// <param name="oldLanguage">The prior language being used.</param>
|
|
/// <param name="newLanguage">The new language to use.</param>
|
|
protected void LanguageChanged(string oldLanguage, string newLanguage)
|
|
{
|
|
UpdateInputCharacterSet();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the characters being used.
|
|
/// </summary>
|
|
private void UpdateInputCharacterSet()
|
|
{
|
|
bool foundCharacterSet = false;
|
|
DialogueDisplay parentDisplay = DialogueDisplay.GetParentDialogueDisplay(this.gameObject);
|
|
|
|
if (parentDisplay != null && parentDisplay.DialogueSettings != null && parentDisplay.DialogueSettings.LanguageInputs != null)
|
|
{
|
|
|
|
foreach (LanguageInputCharacterSet characterSet in parentDisplay.DialogueSettings.LanguageInputs.CharacterSet)
|
|
{
|
|
if (characterSet.LanguageCode.Equals(EasyTalkGameState.Instance.Language))
|
|
{
|
|
inputCharacters = characterSet.Characters;
|
|
foundCharacterSet = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundCharacterSet) { inputCharacters = defaultCharacters; }
|
|
}
|
|
|
|
/// <inheritdoc>
|
|
private void Update()
|
|
{
|
|
if (Time.time - lastCycleTime > cycleDelay)
|
|
{
|
|
if (cyclingCharactersForward)
|
|
{
|
|
NextCharacter();
|
|
}
|
|
else if (cyclingCharactersBackward)
|
|
{
|
|
LastCharacter();
|
|
}
|
|
|
|
lastCycleTime = Time.time;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Switches the currently selected input character to the last character in the character list.
|
|
/// </summary>
|
|
public void LastCharacter()
|
|
{
|
|
//Change the character at the cursor location to the last character.
|
|
currentCharacter--;
|
|
if(currentCharacter < 0) { currentCharacter = inputCharacters.Length - 1; }
|
|
UpdateCharacterAtCursor();
|
|
|
|
int caretPos = GetCaretPosition();
|
|
if (caretPos < 1) { SetCaretPosition(1); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Switches the currently selected input character to the next character in the character list.
|
|
/// </summary>
|
|
public void NextCharacter()
|
|
{
|
|
//Change the character at the cursor location to the next character
|
|
currentCharacter++;
|
|
if(currentCharacter >= inputCharacters.Length) { currentCharacter = 0; }
|
|
UpdateCharacterAtCursor();
|
|
|
|
int caretPos = GetCaretPosition();
|
|
if (caretPos < 1) { SetCaretPosition(1); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Turns on input character cycling, meaning the input display will cycle through to the next input character
|
|
/// automatically while a gamepad input is held down.
|
|
/// </summary>
|
|
public void CycleNextCharacters()
|
|
{
|
|
cyclingCharactersForward = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Turns on input character cycling, meaning the input display will cycle through to the previous input character
|
|
/// automatically while a gamepad input is held down.
|
|
/// </summary>
|
|
public void CycleLastCharacters()
|
|
{
|
|
cyclingCharactersBackward = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Turns automatic input character cycling off.
|
|
/// </summary>
|
|
public void StopCharacterCycling()
|
|
{
|
|
cyclingCharactersForward = false;
|
|
cyclingCharactersBackward = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the text input character at the current caret position to the currently selected character.
|
|
/// </summary>
|
|
private void UpdateCharacterAtCursor()
|
|
{
|
|
string currentText = GetText();
|
|
|
|
int caretPosition = GetCaretPosition();
|
|
string preText = "";
|
|
if (caretPosition > 1)
|
|
{
|
|
preText = currentText.Substring(0, caretPosition - 1);
|
|
}
|
|
|
|
string postText = "";
|
|
if ((caretPosition) < currentText.Length)
|
|
{
|
|
postText = currentText.Substring(caretPosition, currentText.Length - caretPosition);
|
|
}
|
|
|
|
string newText = preText + inputCharacters[currentCharacter] + postText;
|
|
SetText(newText);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves the cursor (caret position) left by one character.
|
|
/// </summary>
|
|
public void MoveCursorLeft()
|
|
{
|
|
int caretPosition = GetCaretPosition();
|
|
caretPosition--;
|
|
if(caretPosition < 1) { caretPosition = 1; }
|
|
|
|
SetCurrentCharacter(GetText()[caretPosition-1]);
|
|
SetCaretPosition(caretPosition );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves the cursor (caret position) right by one character.
|
|
/// </summary>
|
|
public void MoveCursorRight()
|
|
{
|
|
int caretPosition = GetCaretPosition();
|
|
caretPosition++;
|
|
|
|
//Extend the text by adding spaces to move the cursor over to the right.
|
|
string currentText = GetText();
|
|
while(currentText.Length < caretPosition)
|
|
{
|
|
currentText += ' ';
|
|
}
|
|
|
|
SetText(currentText);
|
|
SetCurrentCharacter(currentText[caretPosition - 1]);
|
|
SetCaretPosition(caretPosition);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches through the input character list to find the specified character and sets the currently selected character index if found.
|
|
/// </summary>
|
|
/// <param name="character"></param>
|
|
private void SetCurrentCharacter(char character)
|
|
{
|
|
for(int i = 0; i < inputCharacters.Length; i++)
|
|
{
|
|
if(inputCharacters[i] == character)
|
|
{
|
|
currentCharacter = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the current text value of the input display.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public string GetText()
|
|
{
|
|
if (forceStandardText)
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
return inputField.text;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if TEXTMESHPRO_INSTALLED
|
|
if (textMeshProInputField != null)
|
|
{
|
|
return textMeshProInputField.text;
|
|
}
|
|
#else
|
|
if (inputField != null)
|
|
{
|
|
return inputField.text;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the text value of the input display.
|
|
/// </summary>
|
|
/// <param name="text">The text to set.</param>
|
|
public void SetText(string text)
|
|
{
|
|
if (forceStandardText)
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
inputField.text = text;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if TEXTMESHPRO_INSTALLED
|
|
if (textMeshProInputField != null)
|
|
{
|
|
textMeshProInputField.text = text;
|
|
}
|
|
#else
|
|
if (inputField != null)
|
|
{
|
|
inputField.text = text;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the current caret position of the input display.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private int GetCaretPosition()
|
|
{
|
|
if (forceStandardText)
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
return inputField.caretPosition;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if TEXTMESHPRO_INSTALLED
|
|
if (textMeshProInputField != null)
|
|
{
|
|
return textMeshProInputField.caretPosition;
|
|
}
|
|
#else
|
|
if (inputField != null)
|
|
{
|
|
return inputField.caretPosition;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the current caret position of the input display.
|
|
/// </summary>
|
|
/// <param name="pos">The position to place the caret (cursor) at.</param>
|
|
private void SetCaretPosition(int pos)
|
|
{
|
|
if (forceStandardText)
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
inputField.caretPosition = pos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if TEXTMESHPRO_INSTALLED
|
|
if (textMeshProInputField != null)
|
|
{
|
|
textMeshProInputField.caretPosition = pos;
|
|
}
|
|
#else
|
|
if (inputField != null)
|
|
{
|
|
inputField.caretPosition = pos;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets wether or not the display will b e hidden immediately on Awake().
|
|
/// </summary>
|
|
public bool HideOnAwake
|
|
{
|
|
get { return hideOnAwake; }
|
|
set { this.hideOnAwake = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the text input.
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
currentCharacter = 0;
|
|
SetText("");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the active PlayerInput node. This method also sets the hint/placeholder text configured in the node.
|
|
/// </summary>
|
|
/// <param name="node">The PlayerInput node to use.</param>
|
|
public void SetInputNode(PlayerInputNode node)
|
|
{
|
|
this.inputNode = node;
|
|
|
|
if (forceStandardText)
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
inputField.placeholder.GetComponent<Text>().text = inputNode.HintText;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if TEXTMESHPRO_INSTALLED
|
|
if (textMeshProInputField != null)
|
|
{
|
|
textMeshProInputField.placeholder.GetComponent<TMP_Text>().text = inputNode.HintText;
|
|
}
|
|
#else
|
|
if (inputField != null)
|
|
{
|
|
inputField.placeholder.GetComponent<Text>().text = inputNode.HintText;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gives the text input focus.
|
|
/// </summary>
|
|
private void FocusTextInput()
|
|
{
|
|
EventSystem.current.SetSelectedGameObject(null);
|
|
|
|
if (forceStandardText)
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
EventSystem.current.SetSelectedGameObject(inputField.gameObject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if TEXTMESHPRO_INSTALLED
|
|
if (textMeshProInputField != null)
|
|
{
|
|
EventSystem.current.SetSelectedGameObject(textMeshProInputField.gameObject);
|
|
}
|
|
#else
|
|
if (inputField != null)
|
|
{
|
|
EventSystem.current.SetSelectedGameObject(inputField.gameObject);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// When called, this will call TextInputEntered() with the current text of the input field.
|
|
/// </summary>
|
|
public void TextInputEntered()
|
|
{
|
|
string text = GetText();
|
|
if (text == null) { text = ""; }
|
|
|
|
TextInputEntered(text.TrimEnd());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the text entered by the player on the active PlayerInput node and calls ExecutionCompleted();
|
|
/// </summary>
|
|
/// <param name="text"></param>
|
|
private void TextInputEntered(string text)
|
|
{
|
|
if (this.inputNode != null)
|
|
{
|
|
this.inputNode.InputText = text;
|
|
this.inputNode.ExecutionCompleted();
|
|
this.inputNode = null;
|
|
}
|
|
|
|
Hide();
|
|
}
|
|
|
|
private void OnValidate()
|
|
{
|
|
#if TEXTMESHPRO_INSTALLED
|
|
if (this.textMeshProInputField == null)
|
|
{
|
|
TMP_InputField[] tmpTextComponents = GetComponentsInChildren<TMP_InputField>(true);
|
|
foreach (TMP_InputField txt in tmpTextComponents)
|
|
{
|
|
if (txt.gameObject.name.Contains("InputField") && txt.gameObject.name.Contains("TMP"))
|
|
{
|
|
this.textMeshProInputField = txt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|