Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Custom Caret

Discussion in 'Scripting' started by phoda, Apr 17, 2019.

  1. phoda

    phoda

    Joined:
    Nov 11, 2014
    Posts:
    384
    Hello,

    I'm doing my own terminal, like simple OS (imagine commodore 64 or fallout4 terminal), its for a game, like you can give it simple commands copy/cp list/ls. I've done it quite robust and brute but dont need anything special. Basically it gets input from player, char by char and saves it in list of chars then when enter is pressed it goes and recognizes commands and that all works, for displaying that list of chars i throw chars to string and that string to Text.text and update it to screen. Also works fine but i need my own InputField since terminal has it on focus all the time and allows me much more customization.

    THE QUESTION
    How to make my own caret (thing that flashes when typing), like i would do it with sprite, some white thick sprite like terminals have it but thing is how to deremine on screen position it should be at, basically how to see where at Text.text i am currently and show white sprite there, or maybe you have better suggestion.

    Thanks.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    What you want is the 'TextGenerator' class:
    https://docs.unity3d.com/ScriptReference/TextGenerator.html

    With this you can measure how strings will be displayed.

    The UnityEngine.UI.Text has one cached on itself which it uses for calculating its own rendering space:
    https://docs.unity3d.com/ScriptReference/UI.Text-cachedTextGenerator.html

    What you can do is check the 'verts' collection on it for all the vertices. The verts are in quads, groups of 4, so if you wanted to find a character before the end you need to figure that into your math. But since we just want to find the end of the string we can just use the last vert.

    Here is an example:
    Code (csharp):
    1.  
    2. public class zTest01 : MonoBehaviour
    3. {
    4.  
    5.  
    6.     public Text TextObject;
    7.     public GameObject Cursor;
    8.     public float CursorDelay = 0.3f;
    9.     public Vector3 CursorOffset;
    10.  
    11.     private string _msg = "";
    12.     private float _timer = 0f;
    13.  
    14.     private void Update()
    15.     {
    16.         //update msg
    17.         foreach (char c in Input.inputString)
    18.         {
    19.             if (c == '\b') // has backspace/delete been pressed?
    20.             {
    21.                 if (_msg.Length != 0)
    22.                 {
    23.                     _msg = _msg.Substring(0, _msg.Length - 1);
    24.                 }
    25.             }
    26.             else if ((c == '\n') || (c == '\r')) // enter/return
    27.             {
    28.                 _msg += "\r\n";
    29.             }
    30.             else
    31.             {
    32.                 _msg += c;
    33.             }
    34.         }
    35.  
    36.         TextObject.text = _msg;
    37.  
    38.         //place cursor
    39.         var gen = TextObject.cachedTextGenerator;
    40.         if(gen.verts.Count > 0)
    41.         {
    42.             var v = gen.verts[gen.verts.Count - 1].position;
    43.             v = TextObject.transform.TransformPoint(v);
    44.             Cursor.transform.position = v + CursorOffset;
    45.         }
    46.    
    47.         //do blink
    48.         _timer += Time.deltaTime;
    49.         if(_timer > CursorDelay)
    50.         {
    51.             _timer = 0f;
    52.             Cursor.SetActive(!Cursor.activeSelf);
    53.         }
    54.     }
    55.  
    56. }
    57.  
    Note that the string appending code is very naive and will generate TONS of garbage... it serves only as an example.

    As for the getting the cursor position, that should be fine in and of itself.

    Note that I have a 'CursorOffset' since not all cursors are the same. You might have big fat ones, thin little ones, etc. Use that value to adjust the cursor relative to the end however you like.

    Typing.gif
     
    Last edited: Apr 17, 2019
    phoda likes this.
  3. phoda

    phoda

    Joined:
    Nov 11, 2014
    Posts:
    384
    Just made new project to test your file, its basically what i need, i may implement later moving left right in command line but for now this works perfectly. ill study it a bit and implement in my project. Thank you very much.
     
  4. phoda

    phoda

    Joined:
    Nov 11, 2014
    Posts:
    384

    I implemented everything fine and it works perfectly, one thing that bothers me
    Code (CSharp):
    1. TextGenerator gen = inputText.cachedTextGenerator;          //Get TextGenerator from input
    2.         if (gen.verts.Count > 0)
    3.         {
    4.             Vector3 v = gen.verts[gen.verts.Count - 1].position;
    5.             v = inputText.transform.TransformPoint(v);
    6.             caret.transform.position = v + caretOffset;
    7.         }
    this code, after each input from player i make update screen function which is this
    Code (CSharp):
    1. inputText.text = "[" + currentIP + "] " + currentDirectory + ": " + CharsToString(command);          //Updates players input
    2.         historyText.text = "";          //Clears old history text
    3.  
    4.         for (int i = 0; i < history.Count; i++)          //Shows all players commands backwards
    5.         {
    6.             historyText.text += "\n" + history[i];
    7.         }
    like here i update that text shown to player, but if i put caret update part here, it always is one step before than current situation, putting it in update function works. maybe drawing of text is done later so it cant fix it self until next frame?
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Yeah, the code that pulls the information from 'cachedTextGenerator' is in the state that only includes the information before Update was called. This means that it doesn't contain the information from adding in the new characters yet.

    If you put in this line before 'if(gen.verts.Count > 0)' you should get what you want:

    Code (csharp):
    1. gen.Populate(_msg, ...);
    edit:
    err, you need to get the TextGenerationSettings for the second parameter... probably can get that from here:
    https://docs.unity3d.com/ScriptReference/UI.Text.GetGenerationSettings.html

    Sorry, at work, so can't help directly.
     
    phoda likes this.
  6. alienheretic

    alienheretic

    Joined:
    Oct 15, 2008
    Posts:
    60
    can you please share the project i am very much in need to know how to get this working with the current unity version as the positions are not correct no mater what offset i use thanks
     
  7. ben-luis

    ben-luis

    Joined:
    Jul 30, 2021
    Posts:
    1
    for me, the caret is always set to the level of the letter in front of it. (I use unity 2019)

    Unbenannt.png Unbenannt.png