Search Unity

Feature Request Can Unity 2021.2 version support emoji?

Discussion in 'UI Toolkit' started by YuuHao, May 26, 2021.

  1. YuuHao

    YuuHao

    Joined:
    Apr 14, 2020
    Posts:
    48
    We use 2020.3 and ui-toolkit-preview.14 in the project, and use TextMeshPro to implement Emoji support for Label and TextField.

    This involves the modification of single and combined emoji width measurement and overall cropping, selection and deletion.

    We currently cache uitoolkit in the Package and modify the source code, but when we upgrade to version 2021.2, we can no longer modify the source code, so we look forward to this feature very much.
     
  2. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    Hello, can you explain a bit why you're having to modify the code to support emojis? They're already supported with the new text engine, preview.14 has it and 2021.2 of course has it too. Having feedback from you would help us understand if we missed some use case :)
     
  3. magnetic_scho

    magnetic_scho

    Joined:
    Feb 2, 2020
    Posts:
    97
    @JuliaP_Unity:
    Is there documentation on the emoticon topic?
    I'm especially looking for documentation on how to define and use your own emoticons for e.g. showing resources in the game ("100 " - where the -emoticon is custom).
     
  4. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    Have you checked the getting started document linked on the new text engine announcement I linked previously? You basically have to create the asset that maps your sprite to be used as emojis.
     
  5. YuuHao

    YuuHao

    Joined:
    Apr 14, 2020
    Posts:
    48
    Our project is a cross-platform project, which will require text input.
    Text input must support most emojis. When dealing with emoji, simple emoji can be implemented using the function you said, but for some combined emoji, it won’t work.

    For example, the emoji of the banner below consists of seven unicode characters, but only one Unicode value can be set in SpriteAsset.
    upload_2021-5-27_14-28-50.png

    upload_2021-5-27_14-29-47.png

    If I only set a unicode value, then a complete emoji will be displayed as follows in unity, and unmatched unicode will be displayed as a box.
    upload_2021-5-27_14-45-32.png
     
    Last edited: May 27, 2021
  6. YuuHao

    YuuHao

    Joined:
    Apr 14, 2020
    Posts:
    48
    For another example, the following two expressions will have overlapping unicodes, so if they can’t be processed completely, it will cause confusion.
    upload_2021-5-27_14-47-5.png

    In addition, we expect emoji to be treated as a whole when omitting characters. When editing emoji in the input box, emoji is also treated as a whole when selecting and deleting.

    The following is the case when characters are omitted.
    upload_2021-5-27_14-47-25.png

    On mobile devices, the soft keyboard handles the overall emoji operation problem, but on the desktop side, this problem still needs us to deal with.
     
  7. YuuHao

    YuuHao

    Joined:
    Apr 14, 2020
    Posts:
    48
    I tried to modify the question like this. For clarity, I showed a screenshot of the git commit record.

    I use GetSpriteNameFromCharSequence of TextMeshPro to find the combined emoji.




     
  8. YuuHao

    YuuHao

    Joined:
    Apr 14, 2020
    Posts:
    48
    Here is the logic for processing the search for combined expressions and the logic for omitting related.
    upload_2021-5-27_15-12-32.png
    upload_2021-5-27_15-12-53.png
    upload_2021-5-27_15-14-19.png
    upload_2021-5-27_15-15-13.png
    upload_2021-5-27_15-15-26.png

    Here is the modified code.
    Code (CSharp):
    1. public class EmojiSafeTextElementEnumerator : IEnumerator
    2. {
    3.     //The combination of emoticons currently seen is composed of up to seven text elements (text elements)
    4.     private const int MAX_SEARCH_NUM = 7;
    5.     private int index = -1;
    6.     private string currentElement;
    7.     private string originString;
    8.     private TextElementEnumerator mainTextElementEnumerator;
    9.     private bool isFinish = false;
    10.  
    11.     public object Current
    12.     {
    13.         get
    14.         {
    15.             if (index == -1)
    16.             {
    17.                 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
    18.             }
    19.             else if (isFinish)
    20.             {
    21.                 throw new InvalidOperationException("Enumeration already finished.");
    22.             }
    23.             else
    24.             {
    25.                 return currentElement;
    26.             }
    27.         }
    28.     }
    29.  
    30.     public int ElementIndex
    31.     {
    32.         get
    33.         {
    34.             if (index == -1)
    35.             {
    36.                 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
    37.             }
    38.             else
    39.             {
    40.                 return index;
    41.             }
    42.         }
    43.     }
    44.  
    45.     public EmojiSafeTextElementEnumerator(string input)
    46.     {
    47.         originString = input;
    48.         mainTextElementEnumerator = StringInfo.GetTextElementEnumerator(originString);
    49.     }
    50.  
    51.     public string GetTextElement()
    52.     {
    53.         return currentElement;
    54.     }
    55.  
    56.     public bool MoveNext()
    57.     {
    58.         if (mainTextElementEnumerator.MoveNext())
    59.         {
    60.             var realIndex = mainTextElementEnumerator.ElementIndex;
    61.             //Use the current text element by default
    62.             currentElement = mainTextElementEnumerator.GetTextElement();
    63.             TextElementEnumerator tempTextElementEnumerator = StringInfo.GetTextElementEnumerator(originString, realIndex);
    64.             int count = 0, skipCount = 0;
    65.             string temp = string.Empty;
    66.             bool findCombindEmoji = false;
    67.             while (tempTextElementEnumerator.MoveNext() && count <= MAX_SEARCH_NUM)
    68.             {
    69.                 count++;
    70.                 temp += tempTextElementEnumerator.Current;
    71.  
    72.                 if (TextDelegates.IsCombinedEmojiCharSequenceSafe(temp))
    73.                 {
    74.                     //If a combined emoticon is found, the multiple text elements that make up the combined emoticon are used
    75.                     currentElement = temp;
    76.                     skipCount = count;
    77.                     findCombindEmoji = true;
    78.                 }
    79.             }
    80.             //Skip processed text elements
    81.             while (findCombindEmoji && skipCount > 1)
    82.             {
    83.                 mainTextElementEnumerator.MoveNext();
    84.                 skipCount--;
    85.             }
    86.             index++;
    87.             return true;
    88.         }
    89.         else
    90.         {
    91.             currentElement = string.Empty;
    92.             isFinish = true;
    93.             return false;
    94.         }
    95.     }
    96.  
    97.     public void Reset()
    98.     {
    99.         index = -1;
    100.         isFinish = false;
    101.         currentElement = string.Empty;
    102.         mainTextElementEnumerator.Reset();
    103.     }
    104. }
    Code (CSharp):
    1. internal string ElideText(string drawText, string ellipsisText, float width, TextOverflowPosition textOverflowPosition)
    2. {
    3.     // The pixelOffset represent the maximum value that could be removed from the measured with by the layout when scaling is not 100%.
    4.     // the offset is caused by alining the borders+spacing+padding on the grid.
    5.     // We still want the text to render without being elided even when there is a small gap missing.  https://fogbugz.unity3d.com/f/cases/1268016/
    6.     float pixelOffset = 1 / scaledPixelsPerPoint;
    7.  
    8.     // Try full size first
    9.     var size = MeasureTextSize(drawText, 0, MeasureMode.Undefined, 0, MeasureMode.Undefined);
    10.     if (size.x - pixelOffset <= width || string.IsNullOrEmpty(ellipsisText))
    11.         return drawText;
    12.  
    13.     var minText = drawText.Length > 1 ? ellipsisText : drawText;
    14.     var minSize = MeasureTextSize(minText, 0, MeasureMode.Undefined, 0, MeasureMode.Undefined);
    15.     if (minSize.x >= width)
    16.         return minText;
    17.  
    18.     List<string> textElements = new List<string>();
    19.     EmojiSafeTextElementEnumerator safeTextElementEnumerator = new EmojiSafeTextElementEnumerator(drawText);
    20.     while (safeTextElementEnumerator.MoveNext())
    21.     {
    22.         textElements.Add(safeTextElementEnumerator.GetTextElement());
    23.     }
    24.  
    25.     // Text will need to be truncated somehow
    26.     var drawTextMax = textElements.Count - 1;
    27.     var prevFitMid = -1;
    28.     var truncatedText = drawText;
    29.  
    30.     // Don't assume that k_EllipsisText takes as much space as any other string of the same length;
    31.     // we will start by removing one character at a time
    32.     var min = textOverflowPosition == TextOverflowPosition.Start ? 1 : 0;
    33.     var max = (textOverflowPosition == TextOverflowPosition.Start ||
    34.         textOverflowPosition == TextOverflowPosition.Middle) ? drawTextMax : drawTextMax - 1;
    35.     var mid = (min + max) / 2;
    36.  
    37.     while (min <= max)
    38.     {
    39.         if (textOverflowPosition == TextOverflowPosition.Start)
    40.             truncatedText = ellipsisText + string.Join(string.Empty, textElements.GetRange(mid, drawTextMax - (mid - 1)).ToArray());
    41.         else if (textOverflowPosition == TextOverflowPosition.End)
    42.             truncatedText = string.Join(string.Empty, textElements.GetRange(0, mid).ToArray()) + ellipsisText;
    43.         else if (textOverflowPosition == TextOverflowPosition.Middle)
    44.             truncatedText = string.Join(string.Empty, textElements.GetRange(0, mid - 1).ToArray()) + ellipsisText +
    45.                 string.Join(string.Empty, textElements.GetRange(drawTextMax - (mid - 1), drawTextMax - (drawTextMax - (mid - 1) - 1)).ToArray());
    46.  
    47.         size = MeasureTextSize(truncatedText, 0, MeasureMode.Undefined,
    48.             0, MeasureMode.Undefined);
    49.  
    50.         if (Math.Abs(size.x - width) < Mathf.Epsilon)
    51.             return truncatedText;
    52.  
    53.         if (textOverflowPosition == TextOverflowPosition.Start)
    54.         {
    55.             if (size.x > width)
    56.             {
    57.                 if (prevFitMid == mid - 1)
    58.                     return ellipsisText + string.Join(string.Empty, textElements.GetRange(prevFitMid, drawTextMax - (prevFitMid - 1)).ToArray());
    59.                 min = mid + 1;
    60.             }
    61.             else
    62.             {
    63.                 max = mid - 1;
    64.                 prevFitMid = mid;
    65.             }
    66.         }
    67.         else if (textOverflowPosition == TextOverflowPosition.End || textOverflowPosition == TextOverflowPosition.Middle)
    68.         {
    69.             if (size.x > width)
    70.             {
    71.                 if (prevFitMid == mid - 1)
    72.                     if (textOverflowPosition == TextOverflowPosition.End)
    73.                         return string.Join(string.Empty, textElements.GetRange(0, prevFitMid).ToArray()) + ellipsisText;
    74.                     else
    75.                         return string.Join(string.Empty, textElements.GetRange(0, prevFitMid - 1).ToArray()) + ellipsisText + string.Join(string.Empty, textElements.GetRange(drawTextMax - (prevFitMid - 1), drawTextMax - (drawTextMax - (prevFitMid - 1) - 1)).ToArray());
    76.                 max = mid - 1;
    77.             }
    78.             else
    79.             {
    80.                 min = mid + 1;
    81.                 prevFitMid = mid;
    82.             }
    83.         }
    84.  
    85.         mid = (min + max) / 2;
    86.     }
    87.  
    88.     return truncatedText;
    89. }
     
  9. YuuHao

    YuuHao

    Joined:
    Apr 14, 2020
    Posts:
    48
    Thank you for your attention to this issue, thank you for your reply.

    Here is a list of emojis for your reference.https://unicode.org/emoji/charts/full-emoji-list.html
     
    Last edited: May 27, 2021
  10. HugoBD-Unity

    HugoBD-Unity

    Unity Technologies

    Joined:
    May 14, 2018
    Posts:
    499
    Hi @YuuHao !

    This is unfortunate, but since com.unity.ui and com.unity.textcore are now core modules, the source code is no longer available like packages... We plan to release a version of the packages compatible with 21.2 and earlier versions, but keep in mind that the packages will be missing some features from the latest unity release, like transform and transitions.

    On the bright side, we are currently working on a native implementation of Emoji Sequences (ligature) for 22.1.
     
  11. YuuHao

    YuuHao

    Joined:
    Apr 14, 2020
    Posts:
    48
    Thanks for your reply, I am glad to hear these news. :p
     
  12. mikejm_

    mikejm_

    Joined:
    Oct 9, 2021
    Posts:
    346
    Is there any update on this? I looked at:

    https://blog.unity.com/technology/u...06.839417355.1641617101-2139480756.1640576341

    And there is no mention of Emoji support. Any estimated time on delivery for this function if you're still working on it? I'm not looking to quote you or anyone about it or give anyone a hard time.

    It would just be helpful in planning for my own app development and whether I need to develop workarounds depending on when it might possibly be reasonable to expect.

    Thanks
     
  13. HugoBD-Unity

    HugoBD-Unity

    Unity Technologies

    Joined:
    May 14, 2018
    Posts:
    499
    Hi @mikejm_ !

    We are actively working to integrate ligature support in TextCore as we speak. It should land in 2022.2 in the near future and if all goes well we also plan to backport it to 2022.1.

    Are there other missing features you need to start the development of your app?
     
  14. RahmatAli_Noob

    RahmatAli_Noob

    Joined:
    Sep 13, 2022
    Posts:
    74
    Hi there,
    I am using the Emoji's with the Sprite asset and I can use the Emoji's with single Unicode Like "U+1F600" , but there is the Issue that how can I use the Emoji's with the long Unicode Like" u1f3c3_1f3fb_200d_2640" the Running Girl Emoji.
    I try to write it and it shows the Girl but with the Emoji it shows the Boxes.
    Will you please let me know How I can Do it.
    Thanks in Advance.
    @Stephan_B
    @HugoBD-Unity
    @JuliaP_Unity