How to make a textbox like they have in pokemon
How do you make a text box like they have in pokemon? I managed to make something similar with the following code:
using UnityEngine;
using System.Collections;
using Extensions;
using UnityEngine.UI;
public class DialogController : MonoBehaviour
{
private Text _textComponent;
private string _textToDraw = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dictum eros ultricies tristique tincidunt. Nulla tristique rhoncus elit vel fringilla. Nunc egestas bibendum risus eget accumsan. Integer mauris";
void Start()
{
_textComponent = gameObject.FindInChildren("Text").GetComponent<Text>();
_textComponent.text = string.Empty;
StartRollingText(_textToDraw,0.1f);
}
public void StartRollingText(string text, float time)
{
StartCoroutine(DrawText(text,time));
}
IEnumerator DrawText(string text,float time) //Keeps adding more characters to the Text component text
{
foreach (char c in text)
{
_textComponent.text += c;
yield return new WaitForSeconds(time);
}
}
}
I should probably use something like substring to boost performance but just to test this out its fine.
There is however a issue with this code. Sometimes when its at the end of a line it can suddenly make the word jump to the next line while its typing it because it gets too long. Now i can probably think of some hacky way to fix this but whats the best practice for this?
Is it important to you to do this with a coroutine rather than activating an Update function as long as your text is displaying ? Coroutines are a bit more tricky I think, and this way to do might the cause of your problem. (Anyway, even if you try another way than coroutine, we still can answer the original question, it will be usefull for everybody)
Another thing is that I don't think I have ever seen examples of coroutines with a "for" loop. Did you try something like writing a coroutine that just do the "wait" then add a letter. And this coroutine is called by a coroutine with a while loop that waits for the complete chain. Don't know if it's helping but in my head, that is the way I would try to perform this.
I just used coroutine here because i wanted a delay between the updates but without blocking anything. I could do this with a for loop aswell but foreach was faster to write. It works fine except for the fact that the words sometimes jump to the next line because the previous line gets too long.
So you mean that what you need to do is calcultating the size of the next word and check if it has to be written on the following line ? (by for I meant foreach)
Answer by Jessespike · Apr 14, 2016 at 07:30 PM
It's easier to determine if a word needs a new line if the font has fixed width characters, because then you can just check if the word exceeds the character limit on a line. This isn't the case though, so what this snippet does is:
makes a backup of the current visible text, and stores the text component's height,
add the next word to the text component, and check the components height, compare it to the previous height calculation.
if they are different than add a new line character to the visible text.
set the textcomponent back to the visible text and add each character in the next word.
This is probably not the best, or cleanest solution, but I think it's on the right track:
IEnumerator DrawText(string text,float time) //Keeps adding more characters to the Text component text
{
string[] words = text.Split(' ');
for (int i = 0; i < words.Length; i++)
{
string word = words[i];
// add a space for words except for the last
if (i != words.Length - 1) word += " ";
string previousText = _textComponent.text;
// determine if next word needs to be on new line
float lastHeight = _textComponent.preferredHeight;
_textComponent.text += word;
if (_textComponent.preferredHeight > lastHeight)
{
previousText += System.Environment.NewLine;
}
for (int j = 0; j < word.Length; j++)
{
_textComponent.text = previousText + word.Substring(0, j+1);
yield return new WaitForSeconds(time);
}
}
}
Hmm checking the height is pretty smart actually. I was first thinking of actually calculating the length of the words and check if they fit or not.
This is of course already done internally to deter$$anonymous$$e if a word should go to the next line and by checking the height you are using the information thats already there.
This a nice idea iam going to use to make a nice little easy to use script for.
Edit: I have slightly modified your script to fix a bug in it. Now when a newline is inserted it will remove the last character which should be always a space. This is to prevent the text from double jumping (the space can jump to the next line and the newline will do this again). Other than that this is working. Nice!
/// <summary>
/// This coroutine will draw text over time in a textcomponent
/// </summary>
/// <param name="text"></param>The text to draw
/// <param name="time"></param>The time before the next char should be drawn
/// <returns></returns>
IEnumerator DrawText(string text, float time)
{
string[] words = text.Split(' ');
for (int i = 0; i < words.Length; i++)
{
string word = words[i];
// add a space for words except for the last
if (i != words.Length - 1) word += " ";
string previousText = _textComponent.text;
// deter$$anonymous$$e if next word needs to be on new line
float lastHeight = _textComponent.preferredHeight;
_textComponent.text += word;
if (_textComponent.preferredHeight > lastHeight)
{
previousText = previousText.Remove(previousText.Length - 1); //Removes the last character. This is always a space and thus we dont want this because it can make our text jump 2 lines.
previousText += System.Environment.NewLine;
}
for (int j = 0; j < word.Length; j++)
{
_textComponent.text = previousText + word.Substring(0, j + 1);
yield return new WaitForSeconds(time);
}
}
}