Getting japanese fonts working with an Adobe AIR game for free

Instructions on how to get Japanese (or any language) fixed-width bitmap fonts working, assuming you have the TTF file and uses UTF-8 encoding.

I am likely going to be releasing a Japanese version of Anodyne in September for Windows (Mac, Linux and iOS if I can compile by then), working with a localization team Kakehashi Games – who have localized many things in the AAA sphere, as well as the indie – they did Superbrothers . Which is quite cool!

Anyways, this is a pretty straightforward task programmatically, there are just a few hiccups. For some reason there is no easily available program that does exactly this, but there are lots of pre-existing tools that you can cobble together.

Anodyne uses the FlxBitmapFont.as class. It is a great bitmap font class that works with fixed-width bitmap fonts as PNGs, and uses a string to map character codes to the actual symbols.

So I was recommended a font that would work, and I set out to find it  – which was easy – I found it in PNG form. Unfortunately, the .png that was provided did not match up with the data of the actual .TTF , so I couldn’t get a character string out of the .png which I would use for the bitmap font class. So that wouldn’t work.

And typing out the string myself is not feasible, because, well…

…yeah.

So the TTF MUST contain character codes, right? I found a nice python program – http://sourceforge.net/projects/fonttools/ – which, once installed has a command-line utility, ttx.py .

If you run

python ttx.py -t cmap font_file.ttf

It produces the file “font_file.ttx” . It’s just an XML file though, and contains all of the character codes in hexadecimal format. Depending on the file there could be a few different character sets in there, but it’s easy enough to cut out the ones you don’t want via inspection and playing with the TTF in a text editor. Once you’ve done that, it’s straightforward to convert the XML file into a UTF-8 string that will be used in your game to map the character codes to symbols in your font. I just wrote this quick python script, which just extracts the character codes from every “<map>” node and writes it out to a file. If you uncomment the “out_s += “\n”” line, you can make it print a certain number of characters per line, which is useful for making a .png that you can read better. However, you will want to comment it out when generating the string you use for mapping characters with FlxBitmapFont.

import sys
f = open(sys.argv[1],”r”)

out_s = “”
ct = 0
for line in f:
    if “<map” in line:
        code = line.split(“\””)[1]
        code = unichr(int(code,16)).encode(“utf-8”)
        out_s += code
        ct += 1
        if ct == 50:
            ct = 0
#            out_s += “\n”
    if “</cmap” in line:
        break
f.close()
f = open(“test.txt”,”w”)
f.write(out_s)
f.close()

Now, we just need to make the actual font picture.

I downloaded and installed the free ImageMagick

convert -background “transparent” -fill “#000″ -pointsize 8 -font misaki_gothic.ttf label:”@test.txt” ~test.png

Assuming the python script spat out “test.txt” , the ‘convert’ program will produce ‘~test.png’ which is a png with 8×8 boxes for each character code in test.txt , in white (#000) color, with a transparent background – which is what is needed for FlxBitmapFont and Anodyne.

Then you’re done. Just copy and paste the string into your game where you can reference it, and you should be good to go! You may need to trip the picture in some image editor.

And…

Summary:

1. Find a free TTF

2. Convert to XML with ttx.py

3. Create PNG with imagemagick, create character string with any script

4. Add to game!

Note: Friend Ethan (who ports a lot of games to Linux and is very good at it!) wrote something with SDL that may do this . I have not tried it, but it could very well be quicker than doing what I’ve outlined: http://t.co/WXHqaZMbk9

anodyne by the lines of code

Hey everyone! This isn’t quite a source code release (that won’t be for a while), but I recently had to send off the source code for a review (anodyne might be ported to 3DS!) so I thought it might be interesting to post these stats. line counts via cloc.exe. At least, I’m very interested in other peoples’ game structures myself, so I’d like to see more posts like this!This count is only what I had to write myself, it doesn’t include the flixel library (I made a few modifications to it but they are mostly minor). Doesn’t include CSV (level data), XML (entity data), or dialogue, extensions (steam code, controller extensions)And of course, doesn’t include the music, sound effects, TONS of tilesets and images, level data we created!

What is here is the names of AS3 source files (and some lua/python) 🙂 , sorted by code size. About 36k LoC in all for these files. I would say most of it comes from bad style – I hard code a lot of song/tileset data, the enemies are mostly things that probably could have been scripted in some way,e tc.

If you have any question on what the files do, feel free to ask here or ask me on Twitter! Below I included a quick overview of the files.

We used Git as version control. About 1,100 commits over the April 2012-March 2013 period, though I did a little work in March 2012. One dayI’ll post the commit logs 🙂

-sean hogan

 

File blank comment code
—————————————– ————————————- 2980 35953
src\states\PauseState.as 206 56 1591
src\states\PlayState.as 276 225 1542
src\entity\player\Player.as 207 87 1310
src\entity\interactive\NPC.as 116 53 1260
src\Intra.as 235 169 1144
src\data\TileData.as 65 56 1121
src\entity\enemy\etc\Briar_Boss.as 142 71 1052
src\entity\enemy\circus\Circus_Folks.as 101 42 862
src\entity\interactive\npc\Mitra.as 89 32 784
src\states\TitleState.as 141 38 741
src\entity\enemy\crowd\WallBoss.as 105 65 728
src\entity\enemy\redcave\Red_Boss.as 115 38 700
src\entity\enemy\etc\Sage_Boss.as 116 85 680
src\entity\interactive\npc\Trade_NPC.as 93 22 673
src\entity\interactive\npc\Sage.as 75 46 654
src\data\SoundData.as 167 56 634
src\entity\enemy\hotel\Eye_Boss.as 112 32 609
src\entity\enemy\apartment\Splitboss.as 65 15 575
src\entity\gadget\Door.as 95 69 573
src\states\EndingState.as 56 30 567
src\global\Registry.as 89 101 566
src\entity\enemy\bedroom\Sun_Guy.as 74 37 526
src\helper\SpriteFactory.as 25 19 496
src\entity\enemy\circus\Lion.as 61 14 468
src\states\RoamState.as 116 41 458
src\helper\Cutscene.as 50 19 400
src\global\Keys.as 53 21 373
src\entity\gadget\KeyBlock.as 39 17 368
src\helper\EventScripts.as 68 119 358
src\entity\enemy\redcave\Slasher.as 63 40 347
src\entity\gadget\Treasure.as 40 6 343
src\entity\interactive\npc\Shadow_Briar.a s                                  42 103 329
src\helper\DH.as 81 101 327
src\states\DialogueState.as 51 34 316
src\entity\enemy\bedroom\Slime.as 40 13 304
src\Save.as 47 53 285
src\entity\player\Broom.as 44 18 277
src\entity\enemy\bedroom\Annoyer.as 52 9 265
src\entity\enemy\suburb\Suburb_Walker.as 35 3 265
src\entity\enemy\crowd\Frog.as 43 12 256
src\entity\interactive\Health_Cicada.as 24 5 253
src\entity\gadget\Propelled.as 37 19 252
src\entity\enemy\apartment\Dash_Trap.as 34 9 245
src\entity\gadget\Big_Door.as 33 23 243
src\entity\enemy\crowd\Spike_Roller.as 35 10 241
src\helper\Joypad_Config_Group.as 42 5 237
src\entity\gadget\Console.as 37 6 237
src\entity\gadget\Checkpoint.as 34 19 237
src\entity\enemy\circus\Contort.as 48 13 236
src\helper\Achievements.as 50 25 236
src\data\CSV_Data.as 48 42 232
src\entity\enemy\apartment\Silverfish.as 43 15 226
src\entity\player\Foot_Overlay.as 31 11 226
src\entity\enemy\crowd\Dog.as 39 18 220
src\entity\enemy\etc\ControlsDeity.as 17 6 190
src\entity\enemy\bedroom\Shieldy.as 24 9 185
src\entity\player\Transformer.as 44 18 182
src\states\ControlsState.as 21 4 181
src\states\MinimapState.as 45 14 180
src\entity\enemy\hotel\Burst_Plant.as 31 6 177
src\entity\interactive\npc\Happy_NPC.as 17 5 177
src\entity\enemy\redcave\On_Off_Laser.as 20 15 174
src\entity\enemy\apartment\Teleguy.as 28 15 172
src\entity\enemy\apartment\Rat.as 33 11 171
src\entity\enemy\hotel\Steam_Pipe.as 28 11 170
src\entity\enemy\etc\Chaser.as 24 3 168
src\entity\enemy\redcave\Four_Shooter.as 20 6 165
src\entity\decoration\Light.as 18 22 163
src\entity\interactive\Elevator.as 45 12 160
src\entity\enemy\apartment\Gasguy.as 41 7 156
src\entity\decoration\Water_Anim.as 40 11 150
src\entity\enemy\bedroom\Pew_Laser.as 30 15 149
src\entity\decoration\Solid_Sprite.as 13 16 149
src\entity\interactive\Black_Thing.as 41 11 148
src\helper\ScreenFade.as 19 9 147
src\entity\gadget\Gate.as 20 46 147
src\entity\enemy\crowd\Person.as 23 12 140
src\entity\interactive\npc\Forest_NPC.as 11 4 137
src\entity\enemy\hotel\Dustmaid.as 21 7 135
src\entity\gadget\Dust.as 25 15 132
src\entity\gadget\SinglePushBlock.as 17 5 132
src\entity\interactive\npc\Space_NPC.as 13 4 121
src\entity\interactive\Red_Pillar.as 24 7 116
src\data\gen_npc.py 16 17 113
src\entity\gadget\Jump_Trigger.as 20 5 111
src\entity\enemy\circus\Fire_Pillar.as 17 0 109
src\states\IntroScene.as 17 4 103
src\entity\player\Miniminimap.as 21 19 101
src\entity\player\HealthBar.as 17 13 94
src\entity\interactive\npc\Redsea_NPC.as 13 6 93
src\entity\enemy\redcave\Mover.as 21 12 90
src\entity\gadget\Button.as 17 4 90
src\helper\S_NPC.as 17 24 87
src\Main.as 17 11 87
src\entity\gadget\Go_Detector.as 28 0 86
src\entity\player\HealthPickup.as 8 11 80
src\entity\enemy\etc\Follower_Bro.as 6 4 76
src\entity\enemy\etc\Wall_Laser.as 9 4 76
src\entity\interactive\Dungeon_Statue.as 11 4 75
src\entity\decoration\RetroEffect.as 21 39 71
src\entity\interactive\Fisherman.as 8 4 71
src\entity\gadget\Switch_Pillar.as 13 4 66
src\entity\gadget\Dash_Pad.as 21 0 65
src\entity\enemy\suburb\Suburb_Killer.as 19 0 64
src\data\CLASS_ID.as 7 14 62
src\entity\gadget\Pillar_Switch.as 18 4 61
src\helper\Parabola_Thing.as 17 31 60
src\entity\interactive\Terminal_Gate.as 19 6 60
src\lua\Intra.lua 26 11 56
src\entity\enemy\crowd\Rotator.as 16 9 56
src\entity\enemy\etc\Sadbro.as 11 10 55
src\entity\gadget\CrackedTile.as 9 4 53
src\data\Common_Sprites.as 10 6 52
src\entity\enemy\etc\Red_Walker.as 12 4 49
src\states\FillerTitleState.as 3 6 48
src\entity\enemy\etc\Space_Face.as 20 3 47
src\entity\gadget\Key.as 8 4 46
src\entity\gadget\Hole.as 6 4 45
src\entity\gadget\Growth_Gate.as 8 7 45
src\entity\gadget\Challenge_Gate.as 12 6 42
src\entity\decoration\Nonsolid.as 8 6 36
src\entity\decoration\Map_Preview.as 6 4 31
src\entity\gadget\Stop_Marker.as 7 4 31
src\states\PushableFlxState.as 7 10 30
src\entity\decoration\Eye_Light.as 6 4 27
src\lua\csvTilemap.lua 15 5 25
src\entity\interactive\npc\Huge_Fucking_S tag.as                              6 4 24
src\data\demo_xml_clean.py 4 0 23
src\data\remove_dialogue_for_demo.py 6 6 19
src\Preloader.as 3 4 16
src\helper\SteamThing.as 3 4 11
src\lua\csvTilemap_settings.lua 2 1 4
src\lua\Intra_settings.lua 2 1 4
src\data\make_demo_npc_data.bat 0 0 3
src\data\make_npc_data.bat 0 0 2

Inside of Intra/src

ca – MAc joystick extension code
com – Steamworks code for mac/win
csv – Tilemap data

data –
CLASS_ID.as – used for id’ing some classes, stopped using it halfway through dev but it’s still in some of the entity code
Common_Sprites.as – Embed code for overlays, backgrounds
CSV_Data.as – Embed code for CSVs, also for picking out what CSV should load in an area
NPC_Data.as – The AS3 object format of npc data with state:
SoundData.as – Handles embedding SFX, music, has some helper functions for that, also code for choosing what song plays in an area
TileData.as – Embeds the tilemap files, also has functions for tile callbacks (spikes, etc), setting bindngs (what is solid, etc), picking what tilemap to use in a map, data on animated tiles
dialogue.py – Raw dialogue stuff
gen_npc.py – turns dialogue.py into NPC_Data.as

entity – Not counting the player folder, these are source files for things you can place with the level editor (usually) – signs, rocks, NPCs, enemies, dungeon elements. mostly straightforward

extension – Joypad code for windows

global – Input handler (Keys.as) and the registry.as (global state variable). Holds a lot of random constants and state about the game. Also contains code for loading and parsing the XML

helper – Random things,
DH.as handles back-endy dialogue stuff,
Cutscene.as does some stuff for cutscenes,
EventScripts.as is helper functions,
Joypad_Config_Group.as – the joypad configuration at start of game,
Parabola_thing.as – interpolation for bullet arcs (usually),
S_NPC.as – helper functions for NPCs whose state change throughout the game,
ScreenFade.as – the downsampling effect when moving between areas,
SpriteFactory.as – called for each xml element, generates an entity

lua – Has the lua exporter, Intra.lua, for DAME (map editor)

mochi – random code for mochi ads in this online demo version

obj – ??

org – flixel code . modifications were made in parts

res – image assets. all .png

states – play states (more or less groups of objects)
ControlsState: Set keyboard controls
DialogueState: DIsplaying of dialogue
EndingState – The credits
FillerTitleState – not used
IntroScene – The “wake up!” thing int he beginning
MinimapState – The minimap in the map section of the menu
PauseState – Pause menu
PlayState – All of the overworld/dungeon screens
PushableFlxState – some unncessary abstraction I made
RoamState – not used
TitleState – title screen

xml – Intra.xml, the entity data.
Intra.as – Game loop, mostly related to resizing windows and choosing where the game starts, debug flags, different build flags, mobile GUI

Main.as – handles initalizing extensions, starting game

Preloader.as – I don’t think i use this

Save.as – Handles saving the game to the .sol file

Getting sprites and maps into Anodyne

In this short post I want to talk a bit about how my game, Anodyne loads things like the dungeon and the enemies. We’re on IndieDB now, so check that out as well.

There’s an earlier post that goes into more specifics with the XML, although this post covers it as well. , which was written when I was doing this groundwork back in March.

First we have the ideas that need to be implemented – after I design some dungeon rooms or program some enemies, it’s time to put them into the map editor, which is a way to make your levels and export them into data that the game can use to create the actual areas. The map editor I use is DAME, which works well with Flixel, the AS3 framework I use for Anodyne.

The game’s maps are tiled in the editor and export to plain text in the CSV format – just lines of comma-separated numbers which correspond to specific tiles.

The CSV is then read by the game, and in conjunction with a tileset .png image file, creates the in-game environment, and additionally sets properties for tiles – such as callbacks (for holes), or whether or not you can walk on the tile.

The entities (treasure boxes, enemies, etc.) are also placed inside of DAME, but instead export to XML. There’s an earlier post on the specifics, but each map in the game exports a bunch of sprites for that map, as well as some metadata that I use in-game to give certain behaviors, and also the x and y coordinates. In the game, there are “field” areas and “dungeon” areas.

In field areas, the camera moves with the player, much like platformers. These areas are more for transitory, open-world like places. Currently, I just spawn all the sprites at once, which obviously isn’t very efficient. I don’t want to waste time optimizing what works fine, so if I reach the point where performance takes a hit I’ll probably stick in a distance metric that determines whether I bother updating a sprite, that has some timeout to see if the sprite should be revived.

Anyways…on to the dungeon areas.

The dungeon areas are a little more interesting. Although the entire map CSV is loaded into memory, only two 10×10 chunks of tiles are actively being drawn at once, and only sprites from one 10×10 section are being updated, to increase performance.

When you first enter a dungeon area, the initial chunk is loaded, and that’s where the player is instantiated. Then, I set a few bounds where if the player crosses them, we freeze the controls, load the next chunk into memory, and pan the camera to the next room. This way, I only need to maintain 3 tilemap objects, one, which contains the entire map in memory stored as an array, which makes it easier to obtain chunks for the 10×10 rooms. And then two 10×10 chunks because we want the previous room to still show when scrolling – so when we transition rooms, the current map buffer sends its data to a previous map buffer, and the current map buffer loads the next map.

The sprites also have to be arranged a little differently to load them on a per-room basis, so once at the start of the game, we take the very wide XML tree, and determine what sprites fall into which rooms, and create subtrees for each room. This way when we enter a room, we just lookup that tree and instantiate all of its entities.

During a transition, I also move all the old sprites from the room being moved out to another array so that they still appear, and I then clear them out of memory once the map completely moves over. This way we don’t have an awkward disappearance of all the enemies in one room as we transition, and we additionally see all the entities in the next room as we transition.

The entire game’s XML is serialized, as the XML stores the state of enemies that are needed to be permanently dead (bosses), or entities that need to stay open forever once opened (gates, locked doors).

This means that updating a released version isn’t really an option, as we’d need a way to patch the game’s XML tree. Oh well. But it’s not like I want to release a game that isn’t content complete, so it’s not really an issue, I’d hope. Will just have to be careful with save bugs, which we will hopefully iron out in testing and so forth.

That’s the general idea for all of the dataflow, and there’s not much magic going on. Maybe later I’ll talk about how some of the basic entities work and communicate, which also isn’t particularly complex since only a limited set of entities actually interact with eachother, by design, for simplicity. Or, how I go about bosses, or the player interactions…hm.

 

If you’d like to know anything very specific about this process let me know and I’ll write something up about it!

follow me to see if i finish Anodyne.