Create a gold bitmap font

In this post I'll take you through the process of producing a gold colored font similar to what I've used in my own apps.

First the alphabet:

We need a string of characters separated by one or more spaces (I used 4 in the example below to properly separate the characters) to use in Gimp to create the font string.

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

As in my other post I'll just create a truncated alphabet for simplicity.

Using Gimp to create an alphabet image:

Open up Gimp and create a blank image of size 500 x 20,000. You might have to experiment here to get an image large enough for your complete alphabet. MENU/File/New… and set the image size and press OK.

Now we need to color the background black using the Bucket-Fill tool from the Gimp toolbox.

Next, switch the Gimp Foreground/Background colors around so we get white as the foreground color (use the curved double arrow near the black/white color boxes on the Gimp toolbox.

Select the 'Text' tool from the Gimp toolbox and choose the font you want to use, font size etc. I chose 'Tiranti Solid LET' 200 pixels for this example. I turned on Bold to give the character strokes some width. Finally I flattened the image (MENU/Image/Flatten Image).

This is approximately what you should have now (I've adjusted the image size for this post):

White letters on a black background.

Create the bump-map:

Next we blur the letters slightly (MENU/Filters/Blur/Gaussian Blur, size=5). This will help to give the letters a 3D shape later.

Now we want to create a bump-map for the letters to generate a 3D shape: MENU/Select/By Colour, now click on the black background. MENU/Select/Invert

Go to the Layers dialog (MENU/Windows/Dockable-Dialogs/Layers) click on the "Create a new layer" button at the bottom left, select Transparency, name the layer bump-map and click OK. Next click the eye-symbol on the Background layer to hide it.

Select BlendTool on the Gimp toolbox (square with L-R black to white gradient) and set, Mode:Normal, Opacity:100%, Gradient: FG to BG (RGB), Shape: Linear. Now drag out the line from the very top of the image to the very bottom in a completely vertical line. If you don't make the line vertical the gradient will be tilted. Your letters will now have a gradient from top to bottom.

Get rid of the select (MENU/Select/None), and apply the bump-map (MENU/Filters/Map/Bump Map). In the dialog set Bump Map:background image, Map Type: Sinusoidal (this is nice), and then press OK. Your lettering should appear 3D.

Make Letters Metalic:

Next we want to make the letters appear shiny like they were made from metal (MENU/Colors/Curves…). Pull out the curves so they look something like:

Metallic color curve preset for metallic look.

You can save this curve by clicking on the green+. Press OK. The lettering should appear much like the following (if you zoom in -- use mouse wheel):

Letters given a metallic look by adjusting the Color Curves.

Apply the Gold Color:

First we need to set a fore-ground color. Click on the foreground color blot(white block on Gimp toolbox) and set a yellow color: fff556 and press OK. In the Layers dialog make sure the visible layer (letters) is selected and apply that color to the image (MENU/Colors/Map/Gradient Map). This gives the object a nice gold color. NOTE: You can choose other colors and produce metallic lettering to suit your needs.

Letters colored with Gradient Map

A drop-shadow can now be added. Select the letters as we did above (MENU/Select/By Color, now click on the black background. MENU/Select/Invert) and generate a drop-shadow (MENU/Filters/Light and Shadow/Drop Shadow, Opacity: 85%) and press OK. Un-select the characters (Shift-Ctrl-A). This gives each character a nice drop shadow something like the following:

Drop-Shadow added.

We can now delete the background layer (the red circle with cross in Gimp layers dialog), merge the visible layers (MENU/Image/Merge Visible Layers) and Save (Ctrl-S) and Export (Ctrl-E, uncheck the "Save Background Color" checkbox and save as PNG file) our work.

Gold letter alphabet with drop shadow.

Next:

Next we have to take our linear alphabet image, that we just created, and split each letter out into individual images.

Split Linear Font File

An image of the linear alphabet isn't going to do us much good so we need to split it down into individual letters.

Splitting Into Individual Letters:

To split this long image of the alphabet into individual characters I created a 'bash'(linux) script. It uses the Image-Magick 'convert' program to do two things. First convert will compress the MxN image into a Mx1 (single pixel column) image and then report on the colors of the pixels in this image. The characters will be non-empty and the space around them will be empty (Color #00000000 RGBA). We just use the transition from one to the other to determine where the vertical extents of all the letters are and then use 'convert' to strip the excess non-character stuff off the top and bottom of the original image.

Then we do the same, compress the vertical dimension and strip out the characters from the trimmed image above. The individual characters are then saved in a directory by the name of the Alphabet strip image.

Here is the chopimage script that I use:

#!/bin/bash

#
# Script file to chop an image of a string of characters into individual letters
# based on the uniform transparent background colour (=#00000000 none) between the letters.
#
# This script performs by taking the image and first using 'convert' to smoosh (a technical term)
# it down to a vertical image 1 pixel wide, then reporting on each of the pixels.  An AWK command
# is used to look for a transition from 'none',or 'graya(0,0)'(the background) to something
# that isn't 'none' or 'graya(0,0)':
#
#   ...
#   0,4: (0,0,0,0)  #00000000  none
#   0,5: (0,0,0,0)  #00000000  none
#   0,6: (0,0,0,0)  #00000000  none
#   0,7: (0,0,0,0)  #00000000  none
#   0,8: (3.63098e-09,1.45239e-08,0,5.62604e-15)  #00000000  srgba(0,0,0,8.58478e-20)
#   0,9: (16191,64764,0,0.469211)  #3FFC0000  srgba(63,252,0,7.15971e-06)
#   0,10: (16191,64764,0,9.36611)  #3FFC0000  srgba(63,252,0,0.000142918)
#   0,11: (16191,64764,0,35.3837)  #3FFC0000  srgba(63,252,0,0.000539921)
#   0,12: (16191,64764,0,96.4829)  #3FFC0000  srgba(63,252,0,0.00147223)
#   ...
# Or
#   ...
#   41,0: (0,0)  #00000000  graya(0,0)
#   42,0: (0,0)  #00000000  graya(0,0)
#   43,0: (0,0)  #00000000  graya(0,0)
#   44,0: (0,0)  #00000000  graya(0,0)
#   45,0: (0,0)  #00000000  graya(0,0)
#   46,0: (2.8607e-05,1.72444e-13)  #00000000  graya(0,2.63133e-18)
#   47,0: (49575.3,21.5728)  #C1C1C100  graya(193,0.000329179)
#   48,0: (49937.1,429.378)  #C2C2C202  graya(194,0.00655189)
#   49,0: (51556.9,1516.96)  #C9C9C906  graya(201,0.0231474)
#   ...
#
# Awk then reports the line numbers which we use to determine where to cut the image (to get
# rid of most of the background. We again do this with 'convert'
#
# Next we do the same thing, smooshing the image down to a 1-pixel high image and using the
# resulting list (like above -- except for columns instead of rows) to tell us were to cut
# the letters out.  We store these in a directory with the file name of the original image.
#
# The reported transitions from the background ('none') to otherwise is why we need expand the
# image in GIMP to make sure there is a boundary of blank space around the images (it will get
# trimmed off anyway).
#
# See article:
#   https://stackoverflow.com/questions/33636849/imagemagick-split-image-by-background-color
#
# USAGE:
#   chopimage <in-file.png> {"<out-file-fmt>" {<blank-width>}}
#
# Example:
#   chopimage RoyalsGold.png "Letters_%03.png"
#
# NOTE: <blank-width> defaults to 50 pixels
#
# (Script created by alan on 12/Jun/2018 21:56:51)
#
pgm=chopimage
tmp=/tmp/${pgm}_
bin=/home/alan/bin
log=${bin}/data/${pgm}.$(uname -n).log

# Get our command line arguments making sure we have the original PNG file.
cmpimg=${1}
fmt=${2:-"CharImage_%03d.png"} # Default for format string
blank=${3:-50}                 # Default for blank width in pixels
if [ "$cmpimg" == "" ]; then
    echo "USAGE: chopimage <compound-image> {<out-imgname-format>}"
    exit
fi

# Strip the extension of the in-file-name and use that as output directory
dirnam=${cmpimg%.*}

# Display the working parameters
echo "cmpimg='$cmpimg'"
echo "fmt='$fmt'"
echo "blank='$blank'"
echo "dirnam='$dirnam'"

# Get the actual size of the image.
imgsz=$( file "$cmpimg" | sed "s/^.* \([0-9][0-9]*\) x \([0-9][0-9]*\),.*$/\1,\2/g" )
echo "imgsz='$imgsz'"
iszx=$( echo "$imgsz" | awk -F, '{ print $1 }' )
iszy=$( echo "$imgsz" | awk -F, '{ print $2 }' )
#echo "isz(x,y)=($iszx,$iszy)"

# Determine how much blank space we have on the top and bottom of the PNG.  NOTE: We can't
# do this for individual characters like 'W' and '_' because our PLIST --> FNT conversion
# program isn't smart enough to orient different height characters verticaly.
# NOTE: This requires there to be blank space on the top AND bottom across the image, therefore,
#       make sure your image has blank(transparent) areas on top and bottom.
echo "Finding top/bottom empty space"
convert ${cmpimg} -resize 1x! txt: >${tmp}tb1
cat ${tmp}tb1 \
    | awk 'inside && /#00000000/{inside=0;print;next} !inside && ! /#00000000/{inside=1;print}' \
    >${tmp}tb

# Get the line numbers of the top of the character and the bottom
echo "Getting y1,y2"
y1=$( head -3 ${tmp}tb | tail -1 | sed "s/^0,\([0-9][0-9]*\): .*$/\1/g" )
y2=$( tail -1 ${tmp}tb | sed "s/^0,\([0-9][0-9]*\): .*$/\1/g" )
echo "y1='$y1'"
echo "y2='$y2'"

# Add a little bit of padding (1 pixel top and bottom)
y1=$(( $y1 - 1 ))
szy=$(( $y2 - $y1 +2 ))
#echo "y1='$y1'"
#echo "szy='$szy'"

# Now actually do the vertical trimming
echo "Trimming top and bottom"
convert "${cmpimg}" -crop x${szy}+0+${y1} "${tmp}.png"

# Now we can do the same procedure with the horizontal divisions (except there are more of them).
# NOTE: This requires there to be blank space on the Left AND Right sides of the image, therefore,
#       make sure your image has blank(transparent) areas on the sides.
echo "Finding inter-letter spacing"
convert "${tmp}.png" -resize x1! txt: >${tmp}bnd1
cat ${tmp}bnd1 \
    | awk 'inside && /#00000000/{inside=0;print;next} !inside && ! /#00000000/{inside=1;print}' \
    >${tmp}bnd

# Create the output directory
mkdir -p "${dirnam}" 2>/dev/null


imgno=1    # Counter for output image number.
gotblank=0 # Flag indicating whether we've created the blank character yet.
lastx2=0   # Counter to get inter-character spacing for blank character.

# Now do the actual cutting off of characters
cat ${tmp}bnd |\
while read line1
do
    # Look for the LHS record for the current character
    echo "$line1" | grep "rgba(\|graya([1-9]" >/dev/null
    if [ $? -eq 0 ]; then
        # We've found it so read in the next record which will be the RHS-record.
        read line2
        echo "--------------------------"
        echo "line1='$line1'"
        echo "line2='$line2'"

        # Calculate the pixel extents of the current character.
        x1=$( echo "$line1" | sed "s/,.*$//g" )
        x2=$( echo "$line2" | sed "s/,.*$//g" )

        # Check to see if we have processed the blank character yet.
        if [ $gotblank -eq 0 ]; then
            # Find the distance from the RHS of the last character to the LHS of the current char.
            dlx2x1=$(( $x1 - $lastx2 ))

            # If it is big enough for a blank then produce the file.
            if [ $dlx2x1 -ge $blank ]; then
                echo "Producing blank character, $blank pixels wide"
                # Generate the output file name and create a blank.png from this space
                fname=$( printf $fmt $imgno )
                convert "${tmp}.png" -crop ${blank}x+${lastx2}+0 "${dirnam}/${fname}"
                # Increment our image number.
                imgno=$(( $imgno + 1 ))
                gotblank=1
            fi
        else
            echo "Already got blank"
        fi
        lastx2=$x2

        # Display the coordinates of the character to output.
        echo "x1,x2='$x1,$x2'"
        #x1p=$(( $x1 - 1 ))

        # Determine the character x-metrics.
        x1p=$x1
        szx=$(( $x2 - $x1 ))
       
        # Create the character file name and extract the bitmap to the given file.
        fname=$( printf $fmt $imgno )
        convert "${tmp}.png" -crop ${szx}x+${x1p}+0 "${dirnam}/${fname}"
        echo "Producing character: $fname"

        # On to the next image ...
        imgno=$(( $imgno + 1 ))
    fi
done

#
# end of 'chopimage' script file.
#

Just run the program with the image name and if you want a different width for your space character (default=50 pixels) then specify this too. My PNG file was called GradientBevel.png, I wanted 40pixel wide spaces and I wanted the output image files to be named "LetterImage_%03d.png" so my command was:

chopimage GradientBevel.png "LetterImage_%03d.png" 40

This produced the following output:

alan@Midnight$ chopimage GradientBevel.png "LetterImage_%03d.png" 40
cmpimg='GradientBevel.png'
fmt='LetterImage_%03d.png'
blank='40'
dirnam='GradientBevel'
imgsz='1453,103'
Finding top/bottom empty space
Getting y1,y2
y1='27'
y2='89'
Trimming top and bottom
Finding inter-letter spacing
--------------------------
line1='16,0: (65535,65535,65535,0.98363)  #FFFFFF00  srgba(255,255,255,1.50092e-05)'
line2='70,0: (1.61668e-09,1.61668e-09,1.61668e-09,1.85662e-15)  #00000000  srgba(0,0,0,2.83302e-20)'
x1,x2='16,70'
Producing character: LetterImage_001.png
--------------------------
line1='76,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='122,0: (2.10696e-08,2.10696e-08,2.10696e-08,1.41067e-14)  #00000000  srgba(0,0,0,2.15254e-19)'
x1,x2='76,122'
Producing character: LetterImage_002.png
--------------------------
line1='129,0: (65535,65535,65535,4.98295)  #FFFFFF00  srgba(255,255,255,7.60349e-05)'
line2='176,0: (4.59765e-08,4.59765e-08,4.59765e-08,3.01553e-14)  #00000000  srgba(0,0,0,4.6014e-19)'
x1,x2='129,176'
Producing character: LetterImage_003.png
--------------------------
line1='185,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='235,0: (4.08064e-08,4.08064e-08,4.08064e-08,2.64424e-14)  #00000000  srgba(0,0,0,4.03485e-19)'
x1,x2='185,235'
Producing character: LetterImage_004.png
--------------------------
line1='244,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='286,0: (1.99955e-08,1.99955e-08,1.99955e-08,-1.33887e-14)  #00000000  srgba(0,0,0,-2.04299e-19)'
x1,x2='244,286'
Producing character: LetterImage_005.png
--------------------------
line1='295,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='336,0: (1.86687e-08,1.86687e-08,1.86687e-08,-1.25011e-14)  #00000000  srgba(0,0,0,-1.90755e-19)'
x1,x2='295,336'
Producing character: LetterImage_006.png
--------------------------
line1='344,0: (65535,65535,65535,4.98295)  #FFFFFF00  srgba(255,255,255,7.60349e-05)'
line2='395,0: (5.95701e-08,5.95701e-08,5.95701e-08,-3.61643e-14)  #00000000  srgba(0,0,0,-5.51831e-19)'
x1,x2='344,395'
Producing character: LetterImage_007.png
--------------------------
line1='404,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='453,0: (1.19024e-07,1.19024e-07,1.19024e-07,-7.01002e-14)  #00000000  srgba(0,0,0,-1.06966e-18)'
x1,x2='404,453'
Producing character: LetterImage_008.png
--------------------------
line1='463,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='488,0: (1.19024e-07,1.19024e-07,1.19024e-07,-7.01002e-14)  #00000000  srgba(0,0,0,-1.06966e-18)'
x1,x2='463,488'
Producing character: LetterImage_009.png
--------------------------
line1='492,0: (65535,65535,65535,3.12778)  #FFFFFF00  srgba(255,255,255,4.77269e-05)'
line2='524,0: (1.28439e-07,1.28439e-07,1.28439e-07,-7.54638e-14)  #00000000  srgba(0,0,0,-1.1515e-18)'
x1,x2='492,524'
Producing character: LetterImage_010.png
--------------------------
line1='535,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='587,0: (1.61668e-09,1.61668e-09,1.61668e-09,-1.85662e-15)  #00000000  srgba(0,0,0,-2.83302e-20)'
x1,x2='535,587'
Producing character: LetterImage_011.png
--------------------------
line1='591,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='633,0: (1.99955e-08,1.99955e-08,1.99955e-08,-1.33887e-14)  #00000000  srgba(0,0,0,-2.04299e-19)'
x1,x2='591,633'
Producing character: LetterImage_012.png
--------------------------
line1='640,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='697,0: (1.19024e-07,1.19024e-07,1.19024e-07,-7.01002e-14)  #00000000  srgba(0,0,0,-1.06966e-18)'
x1,x2='640,697'
Producing character: LetterImage_013.png
--------------------------
line1='707,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='756,0: (1.19024e-07,1.19024e-07,1.19024e-07,-7.01002e-14)  #00000000  srgba(0,0,0,-1.06966e-18)'
x1,x2='707,756'
Producing character: LetterImage_014.png
--------------------------
line1='764,0: (65535,65535,65535,4.98295)  #FFFFFF00  srgba(255,255,255,7.60349e-05)'
line2='817,0: (4.78058e-08,4.78058e-08,4.78058e-08,-3.04409e-14)  #00000000  srgba(0,0,0,-4.64499e-19)'
x1,x2='764,817'
Producing character: LetterImage_015.png
--------------------------
line1='826,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='872,0: (2.50286e-08,2.50286e-08,2.50286e-08,-1.68538e-14)  #00000000  srgba(0,0,0,-2.57173e-19)'
x1,x2='826,872'
Producing character: LetterImage_016.png
--------------------------
line1='878,0: (65535,65535,65535,4.98295)  #FFFFFF00  srgba(255,255,255,7.60349e-05)'
line2='931,0: (4.78058e-08,4.78058e-08,4.78058e-08,-3.04409e-14)  #00000000  srgba(0,0,0,-4.64499e-19)'
x1,x2='878,931'
Producing character: LetterImage_017.png
--------------------------
line1='940,0: (65535,65535,65535,17.1694)  #FFFFFF00  srgba(255,255,255,0.000261989)'
line2='989,0: (1.61668e-09,1.61668e-09,1.61668e-09,-1.85662e-15)  #00000000  srgba(0,0,0,-2.83302e-20)'
x1,x2='940,989'
Producing character: LetterImage_018.png
--------------------------
line1='995,0: (65535,65535,65535,7.3013)  #FFFFFF00  srgba(255,255,255,0.000111411)'
line2='1040,0: (7.78376e-08,7.78376e-08,7.78376e-08,-2.84178e-14)  #00000000  srgba(0,0,0,-4.33628e-19)'
x1,x2='995,1040'
Producing character: LetterImage_019.png
--------------------------
line1='1045,0: (65535,65535,65535,2.85368)  #FFFFFF00  srgba(255,255,255,4.35443e-05)'
line2='1094,0: (7.46746e-08,7.46746e-08,7.46746e-08,-2.50023e-14)  #00000000  srgba(0,0,0,-3.8151e-19)'
x1,x2='1045,1094'
Producing character: LetterImage_020.png
--------------------------
line1='1100,0: (65535,65535,65535,12.1229)  #FFFFFF00  srgba(255,255,255,0.000184983)'
line2='1147,0: (3.82292e-07,3.82292e-07,3.82292e-07,-1.13423e-13)  #00000000  srgba(0,0,0,-1.73072e-18)'
x1,x2='1100,1147'
Producing character: LetterImage_021.png
--------------------------
line1='1154,0: (65535,65535,65535,0.772458)  #FFFFFF00  srgba(255,255,255,1.17869e-05)'
line2='1208,0: (5.96768e-09,5.96768e-09,5.96768e-09,-3.42669e-15)  #00000000  srgba(0,0,0,-5.2288e-20)'
x1,x2='1154,1208'
Producing character: LetterImage_022.png
--------------------------
line1='1211,0: (65535,65535,65535,0.772458)  #FFFFFF00  srgba(255,255,255,1.17869e-05)'
line2='1279,0: (2.72452e-08,2.72452e-08,2.72452e-08,-1.21862e-14)  #00000000  srgba(0,0,0,-1.8595e-19)'
x1,x2='1211,1279'
Producing character: LetterImage_023.png
--------------------------
line1='1283,0: (65535,65535,65535,0.98363)  #FFFFFF00  srgba(255,255,255,1.50092e-05)'
line2='1335,0: (6.46671e-09,6.46671e-09,6.46671e-09,-3.71324e-15)  #00000000  srgba(0,0,0,-5.66605e-20)'
x1,x2='1283,1335'
Producing character: LetterImage_024.png
--------------------------
line1='1338,0: (65535,65535,65535,2.69423)  #FFFFFF00  srgba(255,255,255,4.11113e-05)'
line2='1390,0: (5.96768e-09,5.96768e-09,5.96768e-09,-3.42669e-15)  #00000000  srgba(0,0,0,-5.2288e-20)'
x1,x2='1338,1390'
Producing character: LetterImage_025.png
--------------------------
line1='1393,0: (65535,65535,65535,6.36067)  #FFFFFF00  srgba(255,255,255,9.70577e-05)'
line2='1440,0: (1.41726e-07,1.41726e-07,1.41726e-07,-4.80423e-14)  #00000000  srgba(0,0,0,-7.33078e-19)'
x1,x2='1393,1440'
Producing character: LetterImage_026.png

This left me with a directory full of letter images. Note: if some of your letters are packed together into one image increase the spacing between these letters in the alphabet string above and redo all the intervening work. These are the letter images in one of my font directories:

Individual letter images after 'chopimage'.

You may notice looking at your individual character files that there seems to be a lot of space surrounding certain letters. This may be due to an included underscore (or some similar character) that forces a vertical trimming that isn't optimal for all characters. However, this will make it easier to position the characters in Cocos2d-x.

Problems:

I've tripped across a few problems. Here are their solution/explanations:

  • Whitespace on individual letters seems excessive. This may be due to one or more characters in the alphabet extending beyond the average characters boundaries. For instance I found an underscore '_' character cause all my letters to have excess white-space at the bottom. Eventually I'll make my PList->Fnt translation program smart enough to adjust the position of the letters based on their actual extent ... but until then.
  • Multiple letters in individual letter files. This is due to two sequential letters coming too close together in the image (because of glow/drop-shadow or kerning) and can be solved by putting more blanks between characters in the alphabet string.
  • No results at all. I've found this is because there is not enough color #00000000 (empty) boundary around the letters. In gimp just increase the canvas size (and center the image) and this should fix the problem.

Next:

OK, now that we've split them all apart we have to put them all back together into a sprite sheet.

Bitmap Fonts in Cocos2d-x

This article describes the process of creating a bitmap'd font, using the GIMP image editor, the image-magick suite, and some home-brewed C-code. It also describes how to use the font in the Cocos2d-x/Android-Studio development environment.

For this project you will need:

  • Bash (Sorry, moved from MSWindows to Ubuntu and have never looked back - script might be translatable to MSDOS),
  • GIMP image editor (for creating the bitmap font strip),
  • Image-magick suite (splits font strip from Gimp into individual images),
  • A C++ compiler (g++, or clang are good - for compiling my code snippet),
  • TexturePacker (free version, to glue all letters into sprite sheet),
  • A Cocos2d-x project (to include your sprite-sheet into a game/app), and,
  • the Android Studio IDE (to compile the Cocos2d-x project).

I actually wrote this post some time ago but since then my OS has had to be re-imaged and surprise, surprise, that's where MySQL saves its database - fortunately my development environment was all on the /home partition. I've, in the interim, learned how to move my MySQL database to a safe location so hopefully that won't happen again. I've also gotten smarter and installed a WordPress plugin to back my system up off-site (including the MySQL database).

Now, if I can remember what I wrote before...

Let me know how this works for you. I'll try to help if you are having problems, time permitting.

If you have a bitmap font of which you are particularly proud let me know and I will link it on the example post above. Good luck!

Creating a Bitmap Font

The goal of this post is to demonstrate how to generate a bitmap font in a PNG file with a PLIST xml descriptor file for use in Cocos2d-x programs. I'll describe a no-cost method suitable for a beginning or hobby game programmer but useful across the spectrum. My system is an Ubuntu/linux system and the tools I'll use are Gimp, Image-Magick and the free version of TexturePacker

Gimp is a powerfull image editor much like Photoshop (perhaps with a little less chrome -- but very usable). The second package that you need is Image-Magick. Image-Magick has a utility that will allow us to carve the letter images out of a very long image. The last program TexturePacker has a free version that will create a sprite-sheet with all the characters and a related PLIST file that will tell cocos2d-x how to extract each letter from the sprite-sheet. Once you've gotten these programs installed we can continue.

Font:

Next you will require a font. You can just use a system font to experiment with but if you want something fancier then you'll have to look around. If you Google "Free Font" you get lots of sites giving these away. Just be careful to check out the license if you plan to publish your game or use the font in a commercial setting. Install the new fonts on your system so they can be used by Gimp. Here are a sample of "free font" sites:

There are two methods to produce a fancy bitmap font using Gimp. The first is using the built-in generator, the second is to use Gimp's powerful image manipulation functions manually to produce exactly what we are looking for.

Method 1:

Lets experiment. Start up Gimp and click on MENU/File/Create/Logos pick one of the types from the list: 3D Outline, Alien Glow, Alien Neon, Basic II, Basic I, ..., Textured. Select a logo type that has a uniform background colour because we will be removing this later and the program needs a uniform colour to determine where the letter breaks are. Type in a string in the Text field, set the Font and Font size to suit your needs and adjust any of the other parameters you desire. Then press the OK button.

Here are some examples with the Text field and font size (50) changed:

3D Outline
Bovination
Neon

This will produce an image with a fixed string in the font you want with the characteristics you want. You will need to use a selection that produces characters with a background that is completely uniform (which we will cut out of our image later).

Creating Image String:

To produce a bitmap font we will need to produce individual letters for all the letters we want to use in our program. It is probably a good idea to produce the entire ASCII printable set then you won't have to worry about changing requirements for your program. However, I'll just use the capital letters (with a space between each) to illustrate:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Now create your font image with that string. Note that the Image-Magick convert program I'll use later on uses the uniform background colour to determine where to cut the image so if your font image has glow around it or a drop-shadow you may have to space your letters more than one space-character apart.

We generate a horizontal string of characters for two reasons. First it makes splitting them apart easier and second it allows us to uniformly apply image effects that change the characters from top to bottom (for instance a gradient). In another article about producing Gold colored letters this will be important.

This is font size 50 of the Gradient Bevel selection

What you should look for here is a vertical line of uniform background colour between all the letters -- the software will use this to separate the letters. Don't worry about getting too much blank space between your characters -- this will be removed later automatically.

Prepairing Image String:

Still in Gimp we need to do four things: add a transparency layer, subtract the background, resize the image and save everything. First adjust the size of the image using your mouse roller or changing the percentage on the bottom left of the window.

To subtract the background we first have to flatten the image (it may have multiple layers): MENU/Image/Flatten Image, then we have to add a transparency channel to the image: MENU/Layer/Transparency/Add Alpha Channel. If you already have an alpha channel this option will be greyed out.

Next subtract the background by choosing the "Select by Color" tool from the toolbox. You may want to modify the behavior of this tool in the Tool Options (select Feather Edges and adjust the radius and adjust the threshold for the color selector). Then use Ctrl-X (cut) to subtract the background (you should then see the transparency checkerboard between all the characters). If you want to adjust your choices then just backout the cut and selection with a couple of Ctrl-Z's.

When you are happy with the background subtraction we should add a boundary around the image (to make sure there transparent pixels on all sides) by increasing the canvas size: MENU/Image/Canvas Size... , increase the width and height by about 20 pixels and Center the image, and press the Resize button.

Now just save your project (Ctrl-S) and export the image to a PNG file (Ctrl-E).

Method 2:

In this method I'll do the work that was done in Method 1 but do it manually which will give much more control over creating the font. There are many formulas for creating decorated fonts on the web but I'll go over one on producing gold lettering which I've used for my own projects.

Next:

OK, now that we've created a image of our entire alphabet we need to split it apart into individual letters. Or, to see how to produce a gold Bit-Mapped Font go here.

Cocos2d-x Programming

I'm fairly new to Cocos2d-x programming. This forced me spend some time pondering things that haven't seemed quite as easy as I would have expected. Therefore, I thought I'd share what I've learned in a series of posts.

There are also a number of very valuable posts in Chinese which I hope to bring to English. I need to scout these out and talk to their authors first but hopefully they will willing to have me translate them.

I've got code for most of the following topics which I will post as time permits. If there is a particular topic that interests you, or if a post needs clarification or correction, more let me know and I'll get to that sooner

A few things to understand about a Cocos2d-x program:

  • Cocos2d-x Scene Graph - The structure of a Cocos2d-x program,
  • Cocos2d-x Interface with Android - How is Cocos2d-x started,
  • Cocos2d-x Talking to Android - How to use Android services not coded in t Cocos2d-x,
  • Cocos2d-x AppDelegate - Starting up the Cocos2d-x program,
  • Cocos2d-x Talking to Linux Kernel - Socket programming,
  • Cocos2d-x Scene Hierarchy - Which objects can be used as scene root.

Some UI (User Interface) stuff:

  • Cocos2d-x UITextBMFont - Display a fast bit-map font (see below on how to create bit-mapped fonts for your own program,
  • Cocos2d-x UIWebView - Display a webpage within your scenes,
  • Cocos2d-x UIVideoPlayer - Display a video within a scene,
  • Cocos2d-x UITextField -
  • Cocos2d-x UITextAtlas -
  • Cocos2d-x UIText -
  • Cocos2d-x UISlider - Add a slider control to your form, with graduations,
  • Cocos2d-x UIScrollView -
  • Cocos2d-x UIRichText - Display font text with attributes,
  • Cocos2d-x UILoadingBar - Display a progress bar,
  • Cocos2d-x UIListView - Display a drop-down type list,
  • Cocos2d-x UILayout -
  • Cocos2d-x UIImageView - Display an image,
  • Cocos2d-x UIEditBox - Get text input from user,
  • Cocos2d-x UICheckBox - Setting up a checkbox.

Scheduling Functions:

  • Cocos2d-x Schedule Update - Asynchronous function calls to off-load GUI task and to use timing services,
  • Cocos2d-x Schedule Update - Calls the update function every frame,
  • Cocos2d-x Schedule a function once - One shot function call,
  • Cocos2d-x Schedule a function a set number of times with a set time gap between calls with an initial delay,
  • Cocos2d-x Schedule a function forever - With a set time gap between calls,
  • Cocos2d-x Schedule a custom function - To run every frame,
  • Cocos2d-x Unscheduling - Scheduled functions can be stopped very easily.

Miscellaneous Stuff:

  • Cocos2d-x Hello World - A better starting place,
  • Cocos2d-x PLIST - Reading of PLIST file from Resources, and Reading/Writing from/to Device memory,
  • Cocos2d-x Audio services both for effects and background music,
  • Cocos2d-x Accelerometer Input - Detect when the user has interacted with their device by tilting it,
  • Cocos2d-x Keyboard Input - detect when the user has interacted with their device using a keyboard,
  • Cocos2d-x Mouse Input - Detect when the user has interacted with their device using a mouse,
  • Cocos2d-x Single Touch Input - detect when the user has interacted with their device using touch primarily for mobile devices using a single finger,
  • Cocos2d-x Multi Touch Input - Detect when the user has interacted with their device using touch primarily for mobile devices using a multiple fingers,
  • Cocos2d-x Open URL - Opening up a web page from your Android app,
  • Cocos2d-x Clipping Node - Using a stencil object,
  • Cocos2d-x PageView - Scrolling pages,
  • Cocos2d-x Scene Transitions - The functions: pushScene, popScene, popToRootScene, popToSceneStackLevel,
  • Cocos2d-x Scene Transitions - Doing a scene transition with a transition effect, both push and pop.


Up and running…

Well apart from programming Android games ... at the moment I find myself learning WordPress. I used to program in raw HTML/CSS but I decided that I should proceed out of the stone age and thus ... here I am.

Obviously nothing particularly useful at the moment but I plan to add a number of posts on topics that I've learned something about that might be useful to others. Here are some of the things I've been working on that I'll start posting about (no particular order):

  • Android programming. Both in Java and through the NDK.
  • Cocos2d-x programming. I've published my first Android game created with Android-Studio and the Cocos2d-x game engine.
  • OpenCV. I've learned lots about vision software so now it's time to learn some OpenCV stuff. This will come in handy now that people are carrying around fairly powerful computers with included cameras.
    • My SFBC game tool for Android will be using OpenCV to convert camera images to electronic SSDs (Ship System Display -- schematics of the ship and its systems, weapons, engines etc). See Google Images for lots of examples.
  • Game programming. In particular some of the stuff that goes into game programming that isn't obvious (the stuff other than graphics and sound)
    • PCG (Procedural Content Generation). This is a way to generate games scenarios without going to all the trouble and space to create them individually. Pixel Dungeon and all its variants seem to use this. There seems to be a great amount of research work been done on PCG now and it looks like it is the technology to learn.
    • AI (Artificial Intelligence) makes the bots do their thing and makes them act in a way that makes the game challenging for the player. But an AI can't be to smart or the players get frustrated, or too stupid or the players get bored.
  • Compiler writing. I've got a Basic-to-C translator working already but I thought others could use some helpful notes on how to get started here.
    • A Fortran-to-C compiler is also in the back of my mind especially considering I have a few games in Fortran that really want to be converted to C.
    • BNFC is a program to convert between a BNF grammer and the Flex/Bison lexer and parser for C (and various other languages and compiler tools) and looks like a fairly quick way to implement the front end of a compiler ... I'll let you know.
    • Flex/Bison. I suspect that BNFC is a pretty good system but I'm not sure it will help with languages like Fortran with it's position specific code. As well BNFC produces Flex/Bison code so you have to know how to understand the error messages to know how adjust your BNFC code.
  • NCurses programming. Not terribly useful in the present day but some people are nostalgic for the old style games and this is a great way to build them (see Dwarf Fortress)

Combining Letter Images

At this point in the process we have a directory full of a full alphabet of letter images and we need to recombine them into a sprite sheet for use in Cocos2d-x, other game engines and elsewhere. We will use the TexturePacker program to create a sprite-sheet PNG file as well as a PLIST index file. Then we can change this PLIST file to a FNT file required by Cocos2d-x.

Creating the Sprite-Sheet:

TexturePacker will do most of the work here for us -- so this is the easy part.

Start up TexturePacker -- the free version will do all we need. Just select all our letter images and drag them to the left-hand-side window in TexturePacker and drop them there. You will see the file list with small images of each file beside their names. In the center window you will see the images arrayed into a sprite sheet.

NOTE: Record the order of the characters as they appear in the left window of TexturePacker. In my case the string is:

"A BCDEFGHIJKLMNOPQRSTUVWXYZ"

Save the sprite-sheet by pressing the "Publish Sprite Sheet" button at the top of the TexturePacker window and give the sprite-sheet a name (no extension). TexturePacker will generate the <filename>.png and <filename>.plist files.

Sprite sheet containing entire alphabet.

The following is the corresponding PLIST file which describes the locations and sizes of the individual images in the above sprite-sheet:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>frames</key>
        <dict>
            <key>CharImage_001.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{214,217}</string>
                <key>spriteSourceSize</key>
                <string>{214,217}</string>
                <key>textureRect</key>
                <string>{{1396,217},{214,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_002.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{50,217}</string>
                <key>spriteSourceSize</key>
                <string>{50,217}</string>
                <key>textureRect</key>
                <string>{{0,0},{50,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_003.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{186,217}</string>
                <key>spriteSourceSize</key>
                <string>{186,217}</string>
                <key>textureRect</key>
                <string>{{0,217},{186,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_004.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{146,217}</string>
                <key>spriteSourceSize</key>
                <string>{146,217}</string>
                <key>textureRect</key>
                <string>{{185,0},{146,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_005.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{202,217}</string>
                <key>spriteSourceSize</key>
                <string>{202,217}</string>
                <key>textureRect</key>
                <string>{{572,217},{202,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_006.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{165,217}</string>
                <key>spriteSourceSize</key>
                <string>{165,217}</string>
                <key>textureRect</key>
                <string>{{958,0},{165,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_007.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{203,217}</string>
                <key>spriteSourceSize</key>
                <string>{203,217}</string>
                <key>textureRect</key>
                <string>{{774,217},{203,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_008.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{153,217}</string>
                <key>spriteSourceSize</key>
                <string>{153,217}</string>
                <key>textureRect</key>
                <string>{{331,0},{153,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_009.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{299,217}</string>
                <key>spriteSourceSize</key>
                <string>{299,217}</string>
                <key>textureRect</key>
                <string>{{1203,434},{299,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_010.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{135,217}</string>
                <key>spriteSourceSize</key>
                <string>{135,217}</string>
                <key>textureRect</key>
                <string>{{50,0},{135,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_011.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{164,217}</string>
                <key>spriteSourceSize</key>
                <string>{164,217}</string>
                <key>textureRect</key>
                <string>{{794,0},{164,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_012.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{210,217}</string>
                <key>spriteSourceSize</key>
                <string>{210,217}</string>
                <key>textureRect</key>
                <string>{{1186,217},{210,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_013.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{182,217}</string>
                <key>spriteSourceSize</key>
                <string>{182,217}</string>
                <key>textureRect</key>
                <string>{{1296,0},{182,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_014.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{270,217}</string>
                <key>spriteSourceSize</key>
                <string>{270,217}</string>
                <key>textureRect</key>
                <string>{{933,434},{270,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_015.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{315,217}</string>
                <key>spriteSourceSize</key>
                <string>{315,217}</string>
                <key>textureRect</key>
                <string>{{1502,434},{315,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_016.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{153,217}</string>
                <key>spriteSourceSize</key>
                <string>{153,217}</string>
                <key>textureRect</key>
                <string>{{484,0},{153,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_017.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{191,217}</string>
                <key>spriteSourceSize</key>
                <string>{191,217}</string>
                <key>textureRect</key>
                <string>{{186,217},{191,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_018.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{229,217}</string>
                <key>spriteSourceSize</key>
                <string>{229,217}</string>
                <key>textureRect</key>
                <string>{{447,434},{229,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_019.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{209,217}</string>
                <key>spriteSourceSize</key>
                <string>{209,217}</string>
                <key>textureRect</key>
                <string>{{977,217},{209,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_020.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{157,217}</string>
                <key>spriteSourceSize</key>
                <string>{157,217}</string>
                <key>textureRect</key>
                <string>{{637,0},{157,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_021.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{195,217}</string>
                <key>spriteSourceSize</key>
                <string>{195,217}</string>
                <key>textureRect</key>
                <string>{{377,217},{195,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_022.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{185,217}</string>
                <key>spriteSourceSize</key>
                <string>{185,217}</string>
                <key>textureRect</key>
                <string>{{1478,0},{185,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_023.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{218,217}</string>
                <key>spriteSourceSize</key>
                <string>{218,217}</string>
                <key>textureRect</key>
                <string>{{0,434},{218,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_024.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{257,217}</string>
                <key>spriteSourceSize</key>
                <string>{257,217}</string>
                <key>textureRect</key>
                <string>{{676,434},{257,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_025.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{229,217}</string>
                <key>spriteSourceSize</key>
                <string>{229,217}</string>
                <key>textureRect</key>
                <string>{{218,434},{229,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_026.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{173,217}</string>
                <key>spriteSourceSize</key>
                <string>{173,217}</string>
                <key>textureRect</key>
                <string>{{1123,0},{173,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>CharImage_027.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{216,217}</string>
                <key>spriteSourceSize</key>
                <string>{216,217}</string>
                <key>textureRect</key>
                <string>{{1610,217},{216,217}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
        </dict>
        <key>metadata</key>
        <dict>
            <key>format</key>
            <integer>3</integer>
            <key>pixelFormat</key>
            <string>RGBA8888</string>
            <key>premultiplyAlpha</key>
            <false/>
            <key>realTextureFileName</key>
            <string>GoldLetterAlphabet.png.png</string>
            <key>size</key>
            <string>{1826,651}</string>
            <key>smartupdate</key>
            <string>$TexturePacker:SmartUpdate:fe5424a4b480ef76ea4dba00751e813d:e17928c76e957c872b5c8c905f73e949:1f6fb27ef0b0da7790b28c03eb8fa82b$</string>
            <key>textureFileName</key>
            <string>GoldLetterAlphabet.png.png</string>
        </dict>
    </dict>
</plist>

However, Cocos2d-x likes FNT files instead of PLIST files so we have to convert this. I've developed a quick and dirty little program (plist2fnt.cpp) to convert PLIST --> FNT file. This program requires the files to be named CharImage_%03d.png". Here's the program:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cassert>
#include <queue>

#include "tinyxml2.h"

using namespace tinyxml2;
using namespace std;

typedef long unsigned addr;

int chrcnt=0;
char chridx[512];
const char* indentstr="                                                  ";
int idsz=50;
int indent=0;
void ind(){ printf("%s",indentstr+idsz-indent); };

#define cp(n) printf("CheckPoint(%d)\n",n);

//-----------------------------------------------------------------------------
// BMFont routines.
//-----------------------------------------------------------------------------




typedef struct INFO{
    const char* name;
    int size;
    bool bold;
    bool italic;
}t_bmfont_info;

typedef struct COMMON{
    int lineHeight;
    int base;
    int scaleW;
    int scaleH;
}t_bmfont_common;

typedef struct PAGE{
    const char* name;
}t_bmfont_page;

typedef struct COUNT{
    int chars;
}t_bmfont_count;

typedef struct CHAR {
    char chr;
    int ofsx;
    int ofsy;
    int w;
    int h;
    int yoffset;
}t_bmfont_chr;

typedef struct KERNING{
    int dummy;
}t_bmfont_kern;

t_bmfont_info   bmfont_info;
t_bmfont_common bmfont_common;
t_bmfont_page   bmfont_page;
t_bmfont_count  bmfont_count;
t_bmfont_chr    bmfont_chr;

class mycomparison
{
    bool reverse;
  public:
    bool operator() (t_bmfont_chr& lhs, t_bmfont_chr& rhs) const
    {
        return (lhs.chr>rhs.chr);
    }
};
typedef std::priority_queue<int,std::vector<t_bmfont_chr>,mycomparison> mypq_type;
mypq_type myqueue;

void init_BMFont(){
    bmfont_info.name="Unknown";
    bmfont_info.size=0;
    bmfont_info.bold=0;
    bmfont_info.italic=0;

    bmfont_common.lineHeight=0;
    bmfont_common.base=0;
    bmfont_common.scaleW=0;
    bmfont_common.scaleH=0;

    bmfont_page.name="Unknown";

    bmfont_count.chars=0;

    bmfont_chr.chr=0;
    bmfont_chr.ofsx=0;
    bmfont_chr.ofsy=0;
    bmfont_chr.w=0;
    bmfont_chr.h=0;
    bmfont_chr.yoffset=0;
}

void put_BMFont_info(const char *face,int size,bool bold,bool italic)
{
    fprintf(stderr,"info face="%s" size=%d bold=%d italic=%d charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1\n",
        face,size,(bold)?1:0,(italic)?1:0);
}

void put_BMFont_common(int lineHeight,int base,int scaleW,int scaleH)
{
    fprintf(stderr,"common lineHeight=%d base=%d scaleW=%d scaleH=%d pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0\n",
        lineHeight,base,scaleW,scaleH);
}

void put_BMFont_page(const char* pngName)
{
    fprintf(stderr,"page id=0 file="%s"\n",pngName);
}

void put_BMFont_count(int chars)
{
    fprintf(stderr,"chars count=%d\n",chars);
}

void put_BMFont_char(char chr,int ofsx,int ofsy,int w,int h,int yoffset)
{
    fprintf(stderr,"char id=%d x=%d y=%d width=%d height=%d xoffset=0 yoffset=%d xadvance=%d page=0 chnl=0 letter="%c"\n",
        chr,ofsx,ofsy,w,h,yoffset,w+1,chr);
}

void put_BMFont_end(int kerns)
{
    fprintf(stderr,"kernings count=0\n");
}




bool iskey(XMLNode* node) { return (strncmp("key",node->Value(),3)==0)?true:false; };
bool isdict(XMLNode* node) { return (strncmp("dict",node->Value(),4)==0)?true:false; };
bool isarray(XMLNode* node) { return (strncmp("array",node->Value(),5)==0)?true:false; };
bool isstr(XMLNode* node) { return (strncmp("string",node->Value(),6)==0)?true:false; };
bool istrue(XMLNode* node) { return (strncmp("true",node->Value(),4)==0)?true:false; };
bool isfalse(XMLNode* node) { return (strncmp("false",node->Value(),5)==0)?true:false; };


const char* keytext(XMLNode* node) {
    auto elem=node->ToElement();
    assert((elem!=nullptr)||(!"ERROR: node was not XMLElement"));
    return(elem->ToElement()->GetText());
};

const char* get_keytext(XMLNode* node)
{
    const char* rtn="";
    if(iskey(node)){
        rtn=keytext(node);
    }
    return(rtn);
};

XMLNode* get_keyvalue(XMLNode* node)
{
    node=node->NextSibling();
    return(node);
};

void get_dict(XMLNode* dict)
{
    const char* lastkey="none";
    indent+=2;
    auto child=dict->FirstChildElement("key");
    while(child!=nullptr){
        auto keytxt=get_keytext(child);
        ind(); printf("key='%s'\n",keytxt);
        if(strncmp("CharImage",keytxt,6)==0){
            int idx;
            chrcnt++;
            char buff[100];
            strncpy(buff,keytxt,99);
            char* token=strtok(buff,"_.");
            printf("token='%s'\n",token);
            token=strtok(nullptr,"_.");
            printf("token='%s'(%d)\n",token,atoi(token));
           
            if(atoi(token)==90){
                bmfont_chr.chr='?';
            }else{
                bmfont_chr.chr=chridx[atoi(token)-1];
            }
            printf("bmfont_chr.chr='%c(%d)'\n",bmfont_chr.chr,bmfont_chr.chr);
        }
        lastkey=keytxt;

        auto keyvalue=get_keyvalue(child);
        if(isdict(keyvalue)){
            ind();printf("dict\n");
            get_dict(keyvalue);

        }else if(isarray(keyvalue)){
            ind();printf("array\n");

        }else if(isstr(keyvalue)){
            const char* kv=keytext(keyvalue);
            ind();printf("str:'%s'\n",kv);
            if(strncmp("textureRect",lastkey,11)==0){
                char buf[100];
                strcpy(buf,kv);
                const char* flt="{}, ";
                int x=0,y=0,w=0,h=0,yoffset=0;
                char* trp=strtok(buf,flt);
                x=atoi(trp);
                trp=strtok(nullptr,flt);
                y=atoi(trp);
                trp=strtok(nullptr,flt);
                w=atoi(trp);
                trp=strtok(nullptr,flt);
                h=atoi(trp);

                bmfont_common.lineHeight = max(bmfont_common.lineHeight,h);

                bmfont_chr.ofsx=x;
                bmfont_chr.ofsy=y;
                bmfont_chr.w=w;
                bmfont_chr.h=h;
                bmfont_chr.yoffset=yoffset;
                myqueue.push(bmfont_chr);

            }

        }else if(istrue(keyvalue)){
            ind();printf("true\n");

        }else if(isfalse(keyvalue)){
            ind();printf("false\n");

        }else{
            ind();printf("unknown\n");
        }
        child=child->NextSiblingElement("key");
    }
    indent-=2;
}


void get_alphabet(const char* fn_alphabet)
{
    FILE* fh=fopen(fn_alphabet,"r");
    if(fh==nullptr){
        perror("Opening alpahbet file");
        exit(1);
    }else{

        int i=0;
        int chr=0;
        while((chr=getc(fh))!=EOF){
            printf("chr=0x%02x ",chr);
            if(chr>=' '){
                printf("chridx[%d]='%c'\n",i,chr);
                chridx[i++]=(char)chr;
            }
        }
        chridx[i]=0;

        fclose(fh);
    }
}

void dump_alphabet()
{
    printf("Alphabet:");
    for(int i=0;(i<511)&&(chridx[i]!=0);i++){
        if((i%20)==0) printf("\n    ");
        printf("%c",chridx[i]);
    }
    printf("\n");
}


int main(int argc,char* argv[]){

    const char* FontFile=argv[1];
    const int   FontSize=atoi(argv[2]);
    const int   FontFileSzW=atoi(argv[3]);
    const int   FontFileSzH=atoi(argv[4]);
    const char* AlphaFile="alphabet.txt";

    char buff[100];
    char fontname[30];
    char pagename[35];

    XMLDocument doc;

    get_alphabet(AlphaFile);
    dump_alphabet();

    init_BMFont();

    strcpy(buff,FontFile);
    char* token=strtok(buff,".");
    sprintf(fontname,"%s",token);
    sprintf(pagename,"%s.png",token);
    bmfont_info.name=fontname;
    bmfont_page.name=pagename;

    doc.LoadFile(FontFile);
    //doc.LoadFile( "a.plist" );
    //doc.SaveFile( "a.out.xml" );


    // Get the documents first child to start things off.
    // First find the plist node.
    auto plist = doc.FirstChildElement("plist");  // At line 003 below
    if(plist!=nullptr){

        // Once we have the plist look for the dict child
        auto dict=plist->FirstChildElement("dict");  // At line 004 below
        if(dict!=nullptr){
            printf("Got dict\n");

            // Now look at all keys in that dict.
            auto child=dict->FirstChildElement("key"); // At line 005 or 023 below
            while(child!=nullptr){
                // The one labelled "frames" is the one we want.
                if(strncmp("frames",child->GetText(),5)==0){    // This is the key at line 005
                    printf("    Key='%s'\n",child->GetText());
                    auto dict=child->NextSibling();
                    if(dict!=nullptr){
                        printf("Got value='%s'\n",dict->Value());
                        get_dict(dict);                         // Process the dict(ionary) at line 008 to 022
                    }
                }
                child=child->NextSiblingElement("key");
            }
        }
    }

    put_BMFont_info(fontname,FontSize,false,false);
    put_BMFont_common(bmfont_common.lineHeight,25,FontFileSzW,FontFileSzH);
    put_BMFont_page(pagename);
    put_BMFont_count(chrcnt);
    while(!myqueue.empty()){
        t_bmfont_chr bmfont_chr=myqueue.top();
        put_BMFont_char(bmfont_chr.chr,bmfont_chr.ofsx,bmfont_chr.ofsy,bmfont_chr.w,bmfont_chr.h,bmfont_chr.yoffset);
        myqueue.pop();
    }
    put_BMFont_end(0);
}

// 000
// 001 <?xml version="1.0" encoding="UTF-8"?>
// 002 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
// 003 <plist version="1.0">
// 004     <dict>
// 005         <key>frames</key>
// 006         <dict>
// 007             <key>Letter_001.png</key>
// 008             <dict>
// 009                 <key>aliases</key>
// 010                 <array/>
// 011                 <key>spriteOffset</key>
// 012                 <string>{0,0}</string>
// 013                 <key>spriteSize</key>
// 014                 <string>{89,115}</string>
// 015                 <key>spriteSourceSize</key>
// 016                 <string>{89,115}</string>
// 017                 <key>textureRect</key>
// 018                 <string>{{82,920},{89,115}}</string>
// 019                 <key>textureRotated</key>
// 020                 <false/>
// 021             </dict>
// 022         </dict>
// 023         <key>metadata</key>
// 024         <dict>
// 025             <key>format</key>
// 026             <integer>3</integer>
// 027             <key>pixelFormat</key>
// 028             <string>RGBA8888</string>
// 029             <key>premultiplyAlpha</key>
// 030             <false/>
// 031             <key>realTextureFileName</key>
// 032             <string>RoyalsGold.png</string>
// 033             <key>size</key>
// 034             <string>{505,1495}</string>
// 035             <key>smartupdate</key>
// 036             <string>$TexturePacker:SmartUpdate:bae076a86c994950c23db26de5b903c7:bca835b522c805074e19afa28009f159:13f59389bf20a876b22003b1a56f184c$</string>
// 037             <key>textureFileName</key>
// 038             <string>RoyalsGold.png</string>
// 039         </dict>
// 040     </dict>
// 041 </plist>

The plist2fnt program can be run with the following grammar:

plist2fnt <filename>.plist <font-size> <pngfile-width> <pngfile-height>

For example for my GoldLetterAlphabet sprite sheet which is 1826 x 621 pixels in dimension (cmd: file GoldLetterAlphabet.png):

plist2fnt GoldLetterAlphabet.plist 200 1826 621 2>GoldLetterAlphabet.fnt

NOTE: The FNT file output is to stderr so that needs to be captured instead of stdout.

And this is the resultant FNT file:

info face="GoldLetterAlphabet" size=200 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
common lineHeight=217 base=25 scaleW=1826 scaleH=651 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0
page id=0 file="GoldLetterAlphabet.png"
chars count=27
char id=32 x=0 y=0 width=50 height=217 xoffset=0 yoffset=0 xadvance=51 page=0 chnl=0 letter=" "
char id=65 x=1396 y=217 width=214 height=217 xoffset=0 yoffset=0 xadvance=215 page=0 chnl=0 letter="A"
char id=66 x=0 y=217 width=186 height=217 xoffset=0 yoffset=0 xadvance=187 page=0 chnl=0 letter="B"
char id=67 x=185 y=0 width=146 height=217 xoffset=0 yoffset=0 xadvance=147 page=0 chnl=0 letter="C"
char id=68 x=572 y=217 width=202 height=217 xoffset=0 yoffset=0 xadvance=203 page=0 chnl=0 letter="D"
char id=69 x=958 y=0 width=165 height=217 xoffset=0 yoffset=0 xadvance=166 page=0 chnl=0 letter="E"
char id=70 x=774 y=217 width=203 height=217 xoffset=0 yoffset=0 xadvance=204 page=0 chnl=0 letter="F"
char id=71 x=331 y=0 width=153 height=217 xoffset=0 yoffset=0 xadvance=154 page=0 chnl=0 letter="G"
char id=72 x=1203 y=434 width=299 height=217 xoffset=0 yoffset=0 xadvance=300 page=0 chnl=0 letter="H"
char id=73 x=50 y=0 width=135 height=217 xoffset=0 yoffset=0 xadvance=136 page=0 chnl=0 letter="I"
char id=74 x=794 y=0 width=164 height=217 xoffset=0 yoffset=0 xadvance=165 page=0 chnl=0 letter="J"
char id=75 x=1186 y=217 width=210 height=217 xoffset=0 yoffset=0 xadvance=211 page=0 chnl=0 letter="K"
char id=76 x=1296 y=0 width=182 height=217 xoffset=0 yoffset=0 xadvance=183 page=0 chnl=0 letter="L"
char id=77 x=933 y=434 width=270 height=217 xoffset=0 yoffset=0 xadvance=271 page=0 chnl=0 letter="M"
char id=78 x=1502 y=434 width=315 height=217 xoffset=0 yoffset=0 xadvance=316 page=0 chnl=0 letter="N"
char id=79 x=484 y=0 width=153 height=217 xoffset=0 yoffset=0 xadvance=154 page=0 chnl=0 letter="O"
char id=80 x=186 y=217 width=191 height=217 xoffset=0 yoffset=0 xadvance=192 page=0 chnl=0 letter="P"
char id=81 x=447 y=434 width=229 height=217 xoffset=0 yoffset=0 xadvance=230 page=0 chnl=0 letter="Q"
char id=82 x=977 y=217 width=209 height=217 xoffset=0 yoffset=0 xadvance=210 page=0 chnl=0 letter="R"
char id=83 x=637 y=0 width=157 height=217 xoffset=0 yoffset=0 xadvance=158 page=0 chnl=0 letter="S"
char id=84 x=377 y=217 width=195 height=217 xoffset=0 yoffset=0 xadvance=196 page=0 chnl=0 letter="T"
char id=85 x=1478 y=0 width=185 height=217 xoffset=0 yoffset=0 xadvance=186 page=0 chnl=0 letter="U"
char id=86 x=0 y=434 width=218 height=217 xoffset=0 yoffset=0 xadvance=219 page=0 chnl=0 letter="V"
char id=87 x=676 y=434 width=257 height=217 xoffset=0 yoffset=0 xadvance=258 page=0 chnl=0 letter="W"
char id=88 x=218 y=434 width=229 height=217 xoffset=0 yoffset=0 xadvance=230 page=0 chnl=0 letter="X"
char id=89 x=1123 y=0 width=173 height=217 xoffset=0 yoffset=0 xadvance=174 page=0 chnl=0 letter="Y"
char id=90 x=1610 y=217 width=216 height=217 xoffset=0 yoffset=0 xadvance=217 page=0 chnl=0 letter="Z"
kernings count=0

Next:

Now that we have the sprite-sheet and it's corresponding FNT file generated we go on to generating a Cocos2d-x (v3.x) project that uses this bitmap file.