
If you're writing a game with ActionScript3 you've basically two options: either use the scene graph or handle it yourself. I.e. extend Sprite, create some Bitmap, attach it to your Sprite, and blit onto its BitmapData via copyPixels.
Skipping the scene graph is certainly more fun. If you wrote some games in other languages, you'll quickly feel at home. Apart from that external main loop it's basically the same deal. Additionally, if your game requires full redraws either way (i.e. dirty rectangles won't help), using copyPixels is the fastest option.
However, if you take a look at BitmapData's documentation you might be in for a big surprise — there is no drawString function. How can you draw some text on top of your entities for quick debugging now?
Well, you can create a TextField, set its text, transform it around, and finally draw it with draw. Wrapped up in a utility class it would be pretty decent. But since I need something for debugging right now, I decided to use an image based approach instead.
There are a few actually:
The texture is based on one of the Proggy Clean fonts (slashed zero, bold punctuation). A 1px outline with 33% opacity and a non-blurred drop shadow with 100% opacity were added to improve the legibility. This is similar to the styling which is typically applied to subtitles. As you can see this approach still works pretty well at this size — even if the background is a mess:
The texture uses the so-called NeHe charset. It starts at character 32 (space) and ends at character 160 (non-breaking space). Using these 128 characters instead of the "full" 0-255 range has the advantage that a lot of control characters are completely skipped and the texture is apparently only half as big. The big downside is that it only covers the absolute minimum, but that's enough for English.
Since only white, black and translucent black were used the texture could be tinted pretty well via ColorTransform by the way.
Using the DText class is (again) very easy. Just hand over your buffer (BitmapData) together with the text, position, and alignment:
DText.draw(buffer, "left aligned"); //the stage has a width of 400 DText.draw(buffer, "centered\nthis is the second line", 400 / 2, 0, DText.CENTER); //a width of 400 means the last pixel is at 399 (and the first one is at 0) DText.draw(buffer, "right alined", 400 - 1 , 0, DText.RIGHT);
package {
import flash.display.*;
import flash.geom.*;
public class DText {
public static const LEFT:int = 0;
public static const RIGHT:int = 1;
public static const CENTER:int = 2;
/* You need this image: http://kaioa.com/b/0808/console.png */
[Embed(source = '../images/console.png')]
private static var CSheet:Class;
private static var chars:Bitmap = new CSheet();
private static const DEFAULT_CHAR:Number = '?'.charCodeAt(0);
private static const NEW_LINE:Number = '\n'.charCodeAt(0);
private static const CHAR_RECTS:Object = new Object();
//static
{
(function():void {
var i:int;
for (i = 32; i < 161; i++){
CHAR_RECTS[i] = new Rectangle(((i - 32) % 16) * 16, (int)((i - 32) / 16) * 16, 9, 16);
}
}());
}
public static function draw(buffer:BitmapData, text:String, x:int = 0, y:int = 0, align:int = DText.LEFT):void {
var lines:Array = text.split('\n');
for each(var line:String in lines) {
if (align == LEFT)
drawLine(buffer, line, x, y);
else if (align == RIGHT)
drawLine(buffer, line, x - line.length * 8, y);
else // CENTER
drawLine(buffer, line, x - line.length * 8 / 2, y);
y += 13;
}
}
private static function drawLine(buffer:BitmapData, text:String, x:int, y:int):void {
var p:Point = new Point(x, y);
var len:int = text.length;
for (var n:int = 0; n < len; n++) {
var c:Number = text.charCodeAt(n);
if (c > 160 || c < 32)
c = DEFAULT_CHAR;
buffer.copyPixels(chars.bitmapData, CHAR_RECTS[c], p);
p.x += 8;
}
}
}
}This part is executed as soon as the class is accessed in any way. In other languages static initializers usually look a lot nicer. You may have wondered about the different indention style there. Well, this bracket style is required. Hugging brackets won't work with static initializers!
Leaving those brackets empty also won't work. You can probably imagine how much fun this is if you try hugging brackets first… and then comment the body out in order to reduce the number of possible error sources… oh and the error messages didn't help of course. Fun. :)
Check Static Initializers in AS3 if you want to know more.
If you paid close attention, you might have noticed that the rectangles have a width of 9 pixels, but during the printing the cursor only advances by 8 pixels. Well, the width of the widest characters is 9 pixels. If the cursor only advances by 8 pixels the outlines of some character pairs can overlap. This is the intended behavior — it uses less screen estate (~89%) without scarifying readability.
Take a look at this zoomed-in part:

The outlines of 'O' and 'W' overlap. There are also a few other pairs over in figure 2. If you want to take a closer look it's the "UM" in "JUMP", the "OV" in "OVER", and the "ZY" in "LAZY".
Comments
Yeah
super!
WillF
It sounds interesting, but I'm still struggling to understand how/why to apply it... this is to avoid the high memory usage/inconvenience of working with text fields?
"why"
It's small, simple, fast, and easy to read.
Post new comment