PalmOS Iceblox: Technical stuff
In my opinion, Sun made a couple of really boneheaded mistakes when they came
up with the KVM specs. Mistakes that make it terribly akwkward (though not
impossible) to develop advanced arcade games in Java for PalmOS devices:
The implications of their first mistake become apparent when you need a background
that isn't just a single color and/or you want some of the mobile objects to
occasionally overlap in a natural way, as shown to the far left of the sketch
- There is no official support for transparent pixels.
- Although an offscreen graphics buffer exists, there is no way to draw
directly on it.
But the KVM only allows black-and-white (OK, they're technically black and
green, but I'm going to use the expression B&W for simplicity)
bitmap objects, with no transparency. That means you will have to settle for
defining bitmaps like the ones in the middle, and accept the way their
enclosing rectangles mess up the background and other mobile objects (like
on the right).
(In theory, there is a way to achieve transparency. From PalmOS 3.5 and
on, the operating system itself allows both transparent pixels and greater
bit depth, but you need to build your own bitmap encoder and the result will
look awful on devices that run earlier versions of PalmOS.)
As for Sun's second mistake, well ...
Everyone who has tried to develop at least a moderately advanced arcade game
in Java knows the importance of using buffered graphics. Ideally you'd
use three buffers, but two are acceptable if you don't mind your mobile
objects flickering a bit. The KVM has two graphics buffers and a method for
copying pixels between them, but the drawing tools only work on the
visible buffer. The offscreen buffer is off-limits to all the drawLine,
drawRectangle, drawBitmap, etc.
In other words, you need to draw everything in the visible buffer first and
you can't modify graphics data once
it has been copied into the offscreen buffer. With one exception: through the
copyOffScreenRegion method itself. Rectangular blocks can be copied back
and forth or within an offscreen or visible buffer itself. With some clever planning
you can sometimes use this feature to both maintain a dynamic offscreen buffer and
"fake" transparency. I will now show you how I did this in Iceblox.
On the left is an example screenshot of the visible buffer during play. You can
see the penguin and a flame. This situation seems simple enough to handle with
just a single graphics buffer -- draw the objects, wait some milliseconds, erase
them, move them and repeat. But it becomes tricky when a bonus symbol
occasionally overlaps a stationary object (a block of ice). Then erasing it is
not just a matter of drawing a black rectangle in its place.
That's why a second buffer is used. You copy its graphics data onto the visible
buffer and in a single step you get a clean background you can draw the mobile
objects on in their new positions.
On the right is an example of the offscreen buffer I use in Iceblox. Apart from
having no mobile objects drawn on it, the main part of it is the same as what the
player sees. But the bottom row, I realized, doesn't need to be buffered. It isn't
part of the playing field. That meant I could use it as storage space for the
parts that make up the background. As you can see, the rightmost four 16x16-pixel
partitions are the building blocks of a level: empty, ice, frozen coin, rock. I
construct a level by copying those blocks onto the playing field above, and I
can easily update it later, when the player moves blocks around or crushes them.
Also important is the left half of the bottom row. On the PalmOS, when working
with B&W graphics, black pixels are "ones" and white pixels are "zeroes". By
copying a flame in "AND" mode from there to the visible buffer, and then "OR"ing
a pair of eyes onto it, I get the same effect as if the flame had had a
transparent background. Same thing with the bonus symbols, except that I
"XOR" them, to make them stand out better.
I don't do this with the penguin or the moving ice blocks. The penguin is always drawn
first, on a black background, so the player will never notice the black rectangle
surrounding it. And the blocks are already perfectly rectangular. They don't need
A few words about the applet
The Java applet that lets you play PalmOS Iceblox in your browser isn't really
an emulator. That would be an exaggeration. I prefer calling it a "mock-up" of the
PalmOS 3.5. It has a functioning clock and scrollbar, and you can click on the icons,
but only the one titled "Iceblox" does anything (yet).
If you compare the start screen of my applet (left) with that of a real Palm device
(right), you will notice that it isn't a 100% perfect imitation. A hardcore computer
geek would have copied the entire Palm system font, whereas I opted for the easy
solution and used the standard 9-point Helvetica of the client computer. They have many
similarities, but are far from identical.
PalmOS characters have been designed to take up no unnecessary space.
When you click on the Iceblox icon, the mock-up Palm screen displays and activates
a bloxCanvas object. This is an implementation of an abstract
superclass palmCanvas I built for maximum expandability, in case
I want to put in more Palm games later on. palmCanvas contains
the font f, the FontMetrics fm, the background color
avocado (yeah, but what would you have called it?) and
two methods click() and beep() for playing sounds.
If you want to see how the classes fit together, here is the source code
Ideally I would have liked to keep the application-related classes out of the
mock-up applet and only load them at runtime, when the user clicked on the
corresponding icon. Alas, for security reasons this can't be done in an applet.
All classes have to be downloaded and verified before the applet starts. But
at least I can keep the graphics data outside and don't load that
until I need it.