Search Unity

Resolved Pixel-perfect centering with TextMeshPro (How to keep center position a whole number)?

Discussion in 'UGUI & TextMesh Pro' started by BasicallyGames, Apr 13, 2021.

  1. BasicallyGames

    BasicallyGames

    Joined:
    Aug 31, 2018
    Posts:
    91
    My game's UI is designed with pixel art. I'm trying to keep all my text pixel perfect as well, but there's one issue I can't seem to figure out on my own. If my text is left aligned, everything is fine. However, when it's centered, depending on if the number of horizontal pixels of text is even or odd, it will either be aligned with the pixel grid, or off by half a pixel respectively. It seems that when there's an odd number of pixels horizontally, rather than centering the text with a whole number, it will be centered by half a Unity unity (Which I suppose is what you'd expect, but that's undesirable in my situation). The UI in my game is all set up so that one Unity unit is equal to one pixel, so when that happens the text is all off.

    Here's what text looks like with an even number of horizontal pixels:
    upload_2021-4-13_11-59-12.png

    And here it is with an odd number of horizontal pixels (The "1" is 3 pixels wide):
    upload_2021-4-13_11-59-59.png

    I'd really like to be able to center text and have it line up with the pixel grid properly. Anyone have any idea on how this could be achieved?
     
  2. BasicallyGames

    BasicallyGames

    Joined:
    Aug 31, 2018
    Posts:
    91
  3. BasicallyGames

    BasicallyGames

    Joined:
    Aug 31, 2018
    Posts:
    91
    Bump bump.
     
  4. Threepwood

    Threepwood

    Joined:
    Jul 23, 2009
    Posts:
    95
    Same question. Bump.

    Seems that you'd need to maybe alter the TMP rect or something to a whole number? If you justify the text left or right it works but not centered, for obvious reasons.
     
  5. BasicallyGames

    BasicallyGames

    Joined:
    Aug 31, 2018
    Posts:
    91
    I've thought about that, but the problem with that method is that if there are multiple lines and you have one that's aligned and one that's off, there's no way you'd be able offset the rect such that both are aligned.

    I'm thinking the best solution would be some way to get the number of font units for each line, and then add a blank character of one unit to lines with an odd number of font units, but I'm not sure if the TextMeshPro API has the ability to get that info. I *might* be able to manually create a database with that info though, so I'll look into doing that soon.
     
  6. BasicallyGames

    BasicallyGames

    Joined:
    Aug 31, 2018
    Posts:
    91
    I think I found a solution! I realized I could prevent an odd value of text units by making sure all glyphs had an even AD value. I created an editor script that goes through all the glyphs in an TMP font asset, and subtracts 1 from any that are odd. After doing that, everything seems to be lining up like I want, and most characters don't appear to be too close to each other (A few will probably need to have there AD values increased manually).
     
    Stephan_B likes this.
  7. pikminscience

    pikminscience

    Joined:
    Mar 26, 2019
    Posts:
    36
    Ran in to this issue too - AD value works.

    Wondering if there's another way to handle centering? think I'll look in to potentially rounding the centered value based on my pixel units.

    I know this wouldn't be perfect centering but if the text is positioned within the RectTransform from the leading letter, rounding that to the closest PPU value would fix the issue and not skewer text spacing.
     
  8. pikminscience

    pikminscience

    Joined:
    Mar 26, 2019
    Posts:
    36
    TMPro_Private

    Code (CSharp):
    1.  
    2. case HorizontalAlignmentOptions.Center:
    3.  
    4. justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - lineInfo.maxAdvance / 2, 0, 0);
    5. break;
    To:
    Code (CSharp):
    1.                    
    2. case HorizontalAlignmentOptions.Center:
    3.  
    4. float ppuVal = 1/32; //example - hardcode this value elsewhere as I doubt it'd change and save the calc:D
    5. float roundedPPU = Mathf.Round((lineInfo.marginLeft + lineInfo.width / 2 - lineInfo.maxAdvance / 2)/ ppuVal)* ppuVal;
    6.  
    7. justificationOffset = new Vector3(roundedPPU, 0, 0);
    8. break;
    Seems OK? it's dependant on the parent object being pixel perfect too.

    edit: this would be different for UGUI
     
    Last edited: Aug 12, 2021
  9. pikminscience

    pikminscience

    Joined:
    Mar 26, 2019
    Posts:
    36
  10. Ayukay

    Ayukay

    Joined:
    Feb 27, 2020
    Posts:
    1
    Hi, I have ran into this exact issue lately. I found a homemade solution. I don't know if you are using pixel perfect camera, and because of this issue I had sometimes vertical lines of pixels of the text that didn't appear in game mode, depending of course of if the text had an odd number of pixels or not.

    My fix was to move the transform of the canvas containing the text by one quarter (1/4) of a pixel. The pixel perfect camera seems to work in the way like if a color is dominant in the domain of a pixel, then the camera displays this color for the full pixel. So with this 1/4 offset, a pixel of the text always covers 1/4 of an in-game pixel, and 3/4 of a neighboring in-game pixel. In this case, the perfect pixel camera renders the pixel covered with 3/4 and not the one covered with 1/4. That way the property that there is always exactly one pixel rendered by the perfect pixel camera is true independent of the 1/2-offset due to odd/even width in pixels of the text.

    Don't know if its clear, but it has been a fix for me.