Novice learning 'Starling' discussion

From the bitmap font making tool, you should export it as white. Then set the font colour with Starling.

With respect to per-character colour, remember you can always extend Starling’s capabilities yourself. I’ve created two wrapper classes below for starling.text.BitmapFont and starling.text.TextField, which allow you to set per-character colour for bitmap fonts.

With these, instead of using BitmapFont and TextField, you’d use ExcuisiteBitmapFont and ExcuisiteTextField

ExquisiteBitmapFont.hx

package;

import starling.display.MeshBatch;
import starling.text.BitmapFont;
import starling.text.ITextCompositor;
import starling.text.TextFormat;
import starling.text.TextOptions;
import starling.styles.MeshStyle;

/**
 * Wrapper for BitmapFont that adds per-character color support.
 * Set the `charColors` field before rendering.
 */
class ExquisiteBitmapFont implements ITextCompositor
{
    private var base:BitmapFont;
    public var charColors:Map<Int, UInt>; // Set this before calling fillMeshBatch

    public function new(base:BitmapFont)
    {
        this.base = base;
        this.charColors = null;
    }

    /**
     * Fills the mesh batch with per-character color support.
     * If charColors is provided, it will override the color for the character at that index.
     */
    public function fillMeshBatch(
        meshBatch:MeshBatch,
        width:Float,
        height:Float,
        text:String,
        format:TextFormat,
        options:TextOptions = null
    ):Void
    {
        var charLocations = base.arrangeChars(width, height, text, format, options);
        var charColors = this.charColors;
        var defaultColor = format.color;

        // Use our own Image for batching
        var helperImage:starling.display.Image = null;
        if (charLocations.length > 0) {
            helperImage = new starling.display.Image(charLocations[0].char.texture);
        } else {
            return; // nothing to render
        }
        helperImage.color = defaultColor;

        for (i in 0...charLocations.length)
        {
            var charLocation = charLocations[i];
            var charIdx = charLocation.index; // original string index
            helperImage.texture = charLocation.char.texture;
            helperImage.readjustSize();
            helperImage.x = charLocation.x;
            helperImage.y = charLocation.y;
            helperImage.scale = charLocation.scale;

            // Per-char color if present
            if (charColors != null && charColors.exists(charIdx))
                helperImage.color = charColors[charIdx];
            else
                helperImage.color = defaultColor;

            meshBatch.addMeshAt(helperImage);
        }
        starling.text.BitmapCharLocation.rechargePool();
    }

    /**
     * Sets character color for a specified range.
     */
    public function setCharColorRange(startIndex:Int, endIndex:Int, color:UInt):Void {
        if (charColors == null) charColors = new Map();
        for (i in startIndex...endIndex) {
            charColors.set(i, color);
        }
    }

    public function clearMeshBatch(meshBatch:MeshBatch):Void
        base.clearMeshBatch(meshBatch);

    public function getDefaultMeshStyle(style:MeshStyle, format:TextFormat, options:TextOptions):MeshStyle
        return base.getDefaultMeshStyle(style, format, options);

    public function dispose():Void
        base.dispose();
}

ExquisiteTextField.hx

package;

import starling.text.TextField;
import starling.text.TextFieldAutoSize;
import starling.text.TextFormat;
import starling.text.TextOptions;

/**
 * Wrapper for Starling's TextField, adding per-character color support with ExquisiteBitmapFont.
 * Use setCharColor() to override the color of a specific character.
 * Use with a font registered as ExquisiteBitmapFont for effect.
 */
class ExquisiteTextField extends TextField {
	private var _charColors:Map<Int, UInt>;

	public function new(width:Int, height:Int, text:String = "", format:TextFormat = null, options:TextOptions = null) {
		super(width, height, text, format, options);
		_charColors = new Map();
	}

	/**
	 * Sets the color for a specific character index.
	 */
	public function setCharColor(idx:Int, color:UInt):Void {
		_charColors[idx] = color;
		setRequiresRecomposition();
	}

	/**
	 * Clears all per-character color overrides.
	 */
	public function clearCharColors():Void {
		_charColors = new Map();
		setRequiresRecomposition();
	}

	/**
	 * Returns the color for a given character index, or null if not overridden.
	 */
	public function getCharColor(idx:Int):Null<UInt> {
		return _charColors.exists(idx) ? _charColors[idx] : null;
	}

	/**
	 * Override updateText to pass charColors to the compositor if possible.
	 */
	override private function updateText():Void {
		var width:Float = _hitArea.width;
		var height:Float = _hitArea.height;

		// Horizontal autoSize does not work for HTML text, since it supports custom alignment.
		// What should we do if one line is aligned to the left, another to the right?

		if (isHorizontalAutoSize && !_options.isHtmlText)
			width = 100000;
		if (isVerticalAutoSize)
			height = 100000;

		_meshBatch.x = _meshBatch.y = 0;
		_options.textureScale = starling.core.Starling.current.contentScaleFactor;

		// Use our ExquisiteBitmapFont API if compositor supports charColors
		var exq:Dynamic = _compositor;
		if (Reflect.hasField(exq, "fillMeshBatch")) {
			try {
				exq.fillMeshBatch(_meshBatch, width, height, _text, _format, _options, _charColors);
			} catch (e:Dynamic) {
				// fallback
				_compositor.fillMeshBatch(_meshBatch, width, height, _text, _format, _options);
			}
		} else {
			_compositor.fillMeshBatch(_meshBatch, width, height, _text, _format, _options);
		}

		if (_customStyle != null)
			_meshBatch.style = _customStyle;
		else {
			_defaultStyle = _compositor.getDefaultMeshStyle(_defaultStyle, _format, _options);
			if (_defaultStyle != null)
				_meshBatch.style = _defaultStyle;
		}

		if (_options.autoSize != TextFieldAutoSize.NONE) {
			_textBounds = _meshBatch.getBounds(_meshBatch, _textBounds);

			if (isHorizontalAutoSize) {
				_meshBatch.x = _textBounds.x = -_textBounds.x;
				_hitArea.width = _textBounds.width;
				_textBounds.x = 0;
			}

			if (isVerticalAutoSize) {
				_meshBatch.y = _textBounds.y = -_textBounds.y;
				_hitArea.height = _textBounds.height;
				_textBounds.y = 0;
			}
		} else {
			// hit area doesn't change, and text bounds can be created on demand
			_textBounds = null;
		}
	}
}

Then to use these, for example:

Usage example:

import ExcuisiteBitmapFont;
import ExcuisiteTextField;
import starling.text.BitmapFont;
import starling.text.TextField;
import starling.text.TextFieldAutoSize;
import starling.textures.Texture;

//...

// Step 1: Load the font assets
var fontTexture:Texture = Texture.fromBitmapData(Assets.getBitmapData("img/some-font.png"));
var fontXml:Xml = Xml.parse(Assets.getText("xml/some-font.xml"));

// Step 2: Create and register the custom ExquisiteBitmapFont
var bitmapFont = new ExquisiteBitmapFont(new BitmapFont(fontTexture, fontXml));
TextField.registerCompositor(bitmapFont, "SomeFont");
bitmapFont.charColors = new Map<Int, UInt>();
bitmapFont.charColors.set(0, 0xFF0000); // Red
bitmapFont.charColors.set(1, 0xFF0000); // Green
bitmapFont.charColors.set(2, 0x0000FF); // Blue
bitmapFont.charColors.set(3, 0xFF0000); // Red
bitmapFont.charColors.set(4, 0xFF0000); // Green
bitmapFont.charColors.set(5, 0x0000FF); // Blue
bitmapFont.charColors.set(6, 0xFF0000); // Red
bitmapFont.charColors.set(7, 0xFF0000); // Green
bitmapFont.charColors.set(8, 0x0000FF); // Blue
bitmapFont.charColors.set(9, 0xFF0000); // Red
bitmapFont.charColors.set(10, 0xFF0000); // Green
bitmapFont.setCharColorRange(11, 15, 0xFF00FF); // Pink

// Step 3: Use the ExquisiteBitmapFont in a custom ExquisiteTextField
var textField = new ExquisiteTextField(400, 100, "Hello, Starling!", new TextFormat("SomeFont", 70));
textField.autoSize = TextFieldAutoSize.BOTH_DIRECTIONS;
addChild(textField);

image

I provide these as examples only. They have not been heavily scrutinised, but it works in my testing.