tapouillo / BMGlyphLabel

BMGlyphLabel and BMGlyphFont class for iOS7 Sprite Kit
MIT License
31 stars 8 forks source link

Alignment and justification issues #13

Open WarpRulez opened 8 years ago

WarpRulez commented 8 years ago

There seems to be a problem with the alignment and justification of BMGlyphLabel.

One would assume that if horizontalAlignment is BMGlyphHorizontalAlignmentCentered and verticalAlignment is BMGlyphVerticalAlignmentMiddle, then the label would be perfectly centered at the node's position on screen. Also, if the text has only one line, then one would assume that textJustify has no effect on this. However, neither of these is the case.

(And even if the text consists of multiple lines, one would still assume that the center of the label would be exactly at the node's position on screen. Justification should only affect how multiple lines are aligned within the node, not how the node is positioned on screen. Only the horizontal and vertical alignment settings should affect this.)

For example, using a monospaced font, if I create a BMGlyphLabel that has centered horizontal and vertical alignment, and centered justification, and the text "ABCDE", then the 'D' (rather than 'C') seems to be pretty much at the position of the node. If I specify left justification, then the node is positioned so that the position is near the left edge of the 'C'. If I specify right justification, then the node will be positioned as if I had specified a horizontal right alignment (ie. the position will be at the right edge of the 'E'.) This, however, is just coincidence. If I change the text to "ABCDEFGH", then the position changes to be about in the middle of the 'G'. It's really arbitrary.

Needless to say, the justification should have no effect on where the label is positioned, especially when using a single line of text. With a single line of text the label should look identical regardless of what the justification is. (And, of course, if both alignments are centered, then the position of the node should be at the dead center of the label.)

As it is now, BMGlyphLabel is almost unusable because it's almost impossible to predict where it will end up being. You just can't have a label with it that's perfectly centered at a given point; if you try to center it at a given position, its actual center will end up at a pretty arbitrary place, depending on the length of the text.

This causes especially problems when animating these labels, because the center of the node is not at the center of the visible label. (Therefore if you eg. scale the label larger in an animation, it will go to one side, as the anchor point will be off-center with regards to the visible contents of the label.)

tapouillo commented 8 years ago

i'll check that in the coming days, if you have a sample project for testing it would help

WarpRulez commented 8 years ago

I think that the problem happens only with certain fonts (perhaps monospaced ones?) When I try with a different font, it seems to work ok.

I would attach a zip file with a font that exhibits the problems I described. However, this page does not allow me to attach zip files. Instead, I'll just attach one of the character images generated by BMGlyph. I notice that there is a lot of empty space on the right of each character image, which may be the cause of the problem. I do not know why the space is there. (It does not appear when I use another variable-width font.) 65

tapouillo commented 8 years ago

could you send me the zip on my email pls ? squeraud@gmail.com I'm not sure if i reproduced the problem or not... and the png file you attached is blank for me.

WarpRulez commented 8 years ago

View the image against a non-white background.

tapouillo commented 8 years ago

yes i see, the monospaced is what you get when you click on 'align on grid' packing method ? could you email me your .bmglyph project so I can check exactly ...

as far as I can see, with that method, it is normal to have non-aligned behaviour on screen. All the placements are calculated from the width of each glyph, it does not detect if there is transparent pixels... which leads to what you describe. In that case, we could only align the font on the left side only. center and right won't work.

maybe one solution would be to have an option in the bmglyphlabel, and to calculate the width of the string, depending on the xadvanced parameter of each character, and not depending on the width.

tapouillo commented 8 years ago

thanks for the email, i made some test...

with this: [label setTextJustify:BMGlyphJustifyLeft]; label.position = CGPointMake(0+label.totalSize.width/2, CGRectGetMidY(self.frame)); we are able to position the label on the left of the screen

an with this: [label setTextJustify:BMGlyphJustifyRight]; label.position = CGPointMake(self.frame.size.width - label.totalSize.width/2 , CGRectGetMidY(self.frame)); it is positioning the label almost on the right of the screen. Almost, because currently it’s not detecting alone how much white space there is inside each letter. and the label itself is anchored in the middle of the total width of all the letters (white space included).

i’m currently not sure how to deal with that, but i guess this workaround is a start to have everything aligned correctly, with the 'visual size' of the label (currently the label.totalSize.width), the size of the label node itself (white space included).

WarpRulez commented 8 years ago

I was wondering why sometimes labels seemed to be a bit off-center, while sometimes they were perfectly centered (with character images that are perfectly centered). It turns out that justification not only affects the positioning of lines, but also the positioning of the characters within a line. Using BMGlyphJustifyCenter (as well as BMGlyphHorizontalAlignmentCentered) is needed to have a label perfectly centered at its position.

This behavior is confusing and counter-productive. It's, for example, hard to center a multiline label on a given point if you want the lines to be, for example, left-justified. If you try to do that, all the characters will be shifted a bit to the right, which means that the whole label will not be centered.

What I would suggest is to do it like cocos2d labels do. In other words:

Do not use horizontal and vertical alignments at all. Simply create a node containing the text, and set its size to the boundaries of the text. For alignment, have an anchorPoint property instead (which would work exactly like in SKSpriteNode.) Setting the anchorPoint property would simply shift everything by relatively that much in relation to the node's origin. (The default anchorPoint should be (0.5, 0.5).) This not only makes it more intuitive, it also allows finer alignment settings.

Justification should only have effect on multiline labels. In single-line labels its value should cause no effect whatsoever. In multiline labels the node's width should be that of the longest line, and each other line is positioned horizontally according to the justification setting.

A nice-to-have additional feature would be if you could specify the line spacing (which would override the one calculated automatically from the font; or perhaps add to it; maybe have both.) Also a character spacing setting would be nice.

tapouillo commented 8 years ago

thanks for the suggestions.

the main problem, as I mentioned earlier, is that it's hard to have a pixel-perfect alignment, in most of the situation, you will have blank space around each letter so the render on screen make us think that there is padding. I made several games with cocos2d, and I had the same 'problem'.

As you said, it make us think that the text is offcenter because it's hard to know where the pixels start and ends, we only have easily access to the letter/sprite width and height.

regarding to your suggestion: "set its size to the boundaries of the text." How do you know the right values ? You can only know the size of each letter/sprite, not what is inside (and bmGlyph is using what the true type font setting is saying - that's why on some malformed font, there is a lots of space. - and we could imagine there a pixel perfect sprite detection to clean all the left/right space around each letters, but it's a heavy and slow process, not sure it can be done live, maybe only at the end when exporting)

I don't think that a bitmap font is something you change a lot, most of the time once it's done, it won't change much. With cocos2d I was always obliged to deal with that problem too, so I made a x and y offset on all the positions (subclass the whole thing, and affect that offset on x and y, and redefine the set position with that).

for example, if you look at my previous answer, you can easily right align your text exactly where you want once you know the x offset to apply. and for the left align / and center align, it's the same.

msqar commented 7 years ago

Guys, any solution for this? Even when I try to get the width of the text so I can manually calculate it, returns 0,0 every single time.

Tried doing

this self.myLabel.frame.width

ADDED: After a few more attempts, i saw that there are 2 different alignment attributes in BMGlyph now. One is textJustify and the other one is horizontal or verticalAlignment. Now it looks like it's working fine. Tested with different values, from 0 to 123123123123 and seems to be adjusted correctly to left.

self.bestScoreLabel.horizontalAlignment = BMGlyphHorizontalAlignmentLeft