Gamua / Starling-Framework

The Cross Platform Game Engine
http://www.starling-framework.org
Other
2.86k stars 822 forks source link

Expose BitmapFont.arrangeChars? #996

Closed tconkling closed 6 years ago

tconkling commented 7 years ago

I need multi-color bitmap-based TextFields in my game, which (I think?) means I need to write my own TextField alternative that can support style runs.

BitmapFont.arrangeChars() and its associated CharLocation type are private. It's not a huge deal, because I can just copy-paste that logic into my own subclass, but it'd be handy if the function were made public, to assist in text layout for non-TextField situations?

PrimaryFeather commented 7 years ago

Makes a lot of sense, thanks for the suggestion! I'll see what could be done to allow multi-colored bitmap fonts and will get back to you.

tconkling commented 7 years ago

Thanks. Also, I'm happy to share my implementation of multi-colored fonts. It's pretty straight-forward.

PrimaryFeather commented 7 years ago

Oh yes, that would be very helpful! Thanks a lot in advance, Tim. :smile:

tconkling commented 7 years ago

Here's the code: https://gist.github.com/tconkling/5e920fb2e1c32babfe7cbe3b924c58ac

Some notes:

If this were being added to Starling itself, I think it would make most sense to add a BitmapFont.fillStyledMeshBatch function, and add a "styled" property to TextField.

If it were a standalone extension, it would be useful to have BitmapFont.arrangeChars be a public (static?) method, and have CharLocation be non-internal.

tconkling commented 7 years ago

(PS: if you are interested in something like this within Starling itself, I'm happy to turn this into a proper pull request.)

PrimaryFeather commented 7 years ago

Thanks a lot for sending me the code! I'll review it as soon as possible. :smile: Don't worry, there's no need to make a proper pull request. I want to play around with it anyway, and then I can check it in or create an extension — whatever seems appropriate then. :wink:

PrimaryFeather commented 7 years ago

Sorry for the long delay — but I've got good news for you! I just made that method protected and made a small extension that uses exactly the syntax you were proposing.

I haven't made a wiki-page for it yet, but maybe you could try it out? With the arrangeChars method available, it's almost trivial to add that feature. Here's the code: StyledBitmapFont. Such a font can be registered as a compositor on standard TextFields, and it even works with the new DistanceFieldStyle logic.

I'm also considering making use of the "isHtmlText" property and parse the CSS-style syntax instead. Then there wouldn't even be the need for an extension.

But I thought you might like to just remove your custom code from your project and still have your strings working. :wink:

Cheers!

PrimaryFeather commented 6 years ago

Did you have the time to look into some of my changes, Tim? Is that what you were looking for? Cheers!

tconkling commented 6 years ago

Ack! I am also sorry for the super-long delay. I'm finally out of shipping-mode and will take a look shortly!

PrimaryFeather commented 6 years ago

No worries! Looking forward to your feedback, though. I'm curious — is there a game announcement I overlooked, as for being out of shipping-mode ...? :smile:

PrimaryFeather commented 6 years ago

I just finalized the extension and created a small wiki page for it:

http://wiki.starling-framework.org/extensions/styledbitmapfont

I also enhanced the parsing function so that it can cope with nested tags.

PrimaryFeather commented 6 years ago

Since the original request (exposing BitmapFont.arrangeChars) was implemented, I'm declaring this issue as "closed". I'm still happy about any feedback whatsoever, though. :wink:

tconkling commented 6 years ago

Hey Daniel, I'm really sorry I took so long with this feedback! (Re: game announcement - I'm prepping my already-on-Steam game, Antihero (http://store.steampowered.com/app/505640/Antihero/), for iOS and Android -- it hasn't yet shipped for mobile.)

The approach you've taken here works, but having styled text be a property of a font as opposed to an individual TextField instance feels undesirable to me.

In my game, I use several fonts, and I'm usually drawing unstyled text with them. If I were to use the StyledBitmapFont extension, I think I'd either need to have two versions of each of these fonts, one styled and one unstyled, or I'd need to make my fonts styled only, and incur the cost of style parsing for every TextField, styled or not? These aren't huge concessions, but they add complexity and a bit of unnecessary overhead that my StyledTextField implementation doesn't have.

(The StyledBitmapFont implementation also precludes a more robust styled text implementation with multiple fonts and sizes supported in the same TextField - my implementation was imagining a future where the {style} tag was expanded to support additional properties, some of which don't make sense if BitmapFont is the place where style gets applied.)

If BitmapFont.arrangeChars were public instead of protected, my StyledTextField class could use it instead of its own copy-pasted copy of the same. As it stands, this doesn't get my specific implementation any closer to my goal of not having its own copies of protected/private Starling internals. This is certainly not a big deal for me, as I can continue using my implementation -- but it's why, unless I'm misunderstanding something in the patch, this solution doesn't quite work for my particular needs.

PrimaryFeather commented 6 years ago

Thanks a lot for the feedback, Tim! No worries about the delay — shipping goes first, always.

BTW, I'm looking forward to "Antihero" on mobile!! Definitely give me a shout when it's ready. :smile:

I've got good news — I think all of the problems that you've got with the current implementation can easily be solved. Well, most of them, at least. :wink:

First, I just made arrangeChars public. Thinking it through again, I don't see a reason why it shouldn't be — except for the pooling gotchas, but there's no way to avoid those, anyway.

Second, Starling provides a mechanism allowing you to send a bunch of options to the compositor. That's with the help of the TextOptions class. That way, it's possible to make styles optional on the standard TextField class.

Add a new class like this one:

class StyledTextOptions extends TextOptions
{
    private var _stylesEnabled:Boolean;

    override public function copyFrom(options:TextOptions):void
    {
        if (options is StyledTextOptions)
            _stylesEnabled = (options as StyledTextOptions)._stylesEnabled;

        super.copyFrom(options);
    }

    public function get stylesEnabled():Boolean { return _stylesEnabled; }
    public function set stylesEnabled(value:Boolean):void { _stylesEnabled = value; }
}

Now, inside the StyledBitmapFont class, you need to read that stylesEnabled value and ignore any custom styling if it's disabled.

override public function fillMeshBatch(meshBatch:MeshBatch, width:Number, height:Number,
                                       text:String, format:TextFormat,
                                       options:TextOptions = null):void
{
    if (options["stylesEnabled"] == false)
        return super.fillMeshBatch(meshBatch, width, height, text, format, options);

    // ...
}

(Feel free to make a true type-safe check instead.)

That's it with the preparations! Now, to disable styling on a specific TextField, simply pass such a StyledTextOptions instances to its constructor:

var textField:TextField = new TextField(300, 100, "Text", null, new StyledTextOptions());

This instance (or a copy of it) will be passed to the fillMeshBatch method whenever the text is composed. Since we let stylesEnabled default to false, styling will be disabled as soon as that StyledTextOptions instance is passed along.

Of course, you can also make this a little more comfortable by subclassing TextField, too:

class StyledTextField extends TextField
{
    public function StyledTextField(width:int, height:int, text:String="",
                                    format:TextFormat=null)
    {
        var options:StyledTextOptions = new StyledTextOptions();
        super(width, height, text, format, options);
    }

    public function get stylesEnabled():Boolean { return (options as StyledTextOptions).stylesEnabled; }
    public function set stylesEnabled(value:Boolean):void
    {
        if (value != stylesEnabled)
        {
            (options as StyledTextOptions).stylesEnabled = value;
            setRequiresRecomposition();
        }
    }
}

Which would give you a nice property directly on the TextField. That should go a long way!

What this doesn't provide, of course, is mixing several bitmap fonts inside one TextField. That would require are more complex "TextCompositor" that references multiple bitmap fonts and has more complex parsing and composition logic included. Still: it should not be necessary to copy/paste any Starling code — I hope. 😛

Please let me know if that simplifies things for you! Cheers!

tconkling commented 6 years ago

This is great, and works perfectly for my needs. Thanks Daniel!