|
Features

Mobilecore: A Cross-Platform Framework For ARM-Based Mobile Games
As
a programmer at Overloaded, a Dutch developer and distributor of
games for mobile devices, I have developed several games for Pocket
PC and Symbian, like Fantom Overdrive, a 3D arcade racer,
and Resistance, a voxel-based shoot-'em-up. Overloaded launched
at the end of 2001, initially developing games for Pocket PCs. Shortly
after that, we switched to Symbian-based smartphones. Now we primarily
target the Symbian OS (series 60) and J2ME. Overloaded games are
small -- typically less than 300KB -- as we distribute them "over
the air". This makes it challenging to create fun titles, complete
with state-of-the-art technology like Bluetooth, multiplayer, and
textured 3D graphics. To support 3D graphics of our games, I developed
a 3D engine, "NGine3D," specifically designed to be used
in small games.
Having
developed several games for mobile devices, I understand that the
market for mobile games is a tough. There are several advanced platforms,
like Microsoft's Pocket PC and Smartphone, and the Symbian OS. However,
the individual markets for these platforms are small, and the only
way to make profitable quality games is by porting early PC games
- work that's typically outsourced to countries where labor is cheap.
Another
option is to develop a game for multiple devices simultaneously.
Unfortunately, this option requires technical knowledge of multiple
operating systems, which (especially in the case of Symbian) can
be problematic. But there is a solution to this: using a library
that hides platform differences and offers a common application
development base. Such a library would help build a multiple platforms
and frees the developer of OS specific issues. In this article I
explain how to build such a library.
Brothers
in ARMs
The
devices that Overloaded has developed for so far have much in common.
The Pocket PC, Smartphone, and Symbian series 60 and 70 all use
ARM or ARM-based processors, clocked somewhere between 100-400MHz.
As the devices with smaller screens tend to use the lower clock
speeds, the performance levels of the devices are comparable. Most
devices use portrait displays. All displays are capable of high-color
graphics (12- or 16-bit color), none of them has an FPU, and of
course, there's no hard drive, limiting available storage.
There
are differences too. Most notable are the varying resolutions: e.g.,
176x208 for Symbian, 240x320 for the Pocket PC and 176x220 for Microsoft
Smartphones. Pocket PCs use a stylus in addition to the buttons,
whereas the Sony P800 (Symbian series 70) also uses a stylus but
has virtually no buttons. The most important difference, of course,
is the operating system. The Pocket PC operating system closely
resembles Windows, but the Symbian OS is quite different.
I
included the GP32 in the list because it is quite close to the other
devices, although its screen is oriented in the landscape direction.
The Zodiac has hardware-accelerated 2D functions, which makes it
slightly different from the other devices.
If
we ignore the operating system differences for a moment, what we
have is an extensive list of devices with similar capabilities.
And this represents lots of small markets with consumers waiting
for cool games. If you could serve Symbian users, Pocket PC users,
and Palm Tungsten-T users the same game, and get the same title
working on the GP32 with minimal effort, that would expand your
game's market without too much porting effort required.
Mobilecore
Before
I started working for Overloaded, I created games for the Pocket
PC in my spare time. As I was struggling to get easy access to the
frame buffer and other hardware of the devices, I developed a small
library called EasyCE. This library served as an abstraction layer
on top of the operating system: It initialized the OS, prepared
a linear 16-bit frame buffer, and after starting the game, it passed
mouse positions and button states to it. To be able to develop and
debug under Windows, I also implemented the HAL for windows, using
the same API.
EasyCE
became quite popular among Pocket PC developers. Since the library
and its source code are free, it served as a "knowledge base"
for Pocket PC issues, as it was frequently updated to cope with
various quirks of newly released devices. These days there are better
libraries for the Pocket PC, especially since Pocket PC coders are
more frequently coding in assembler to achieve faster screen rotation
(not all Pocket PC displays are oriented in portrait mode internally)
and direct access to the LCD controller. Nobody dared to follow
my "business model" though.
When
Overloaded started developing for Symbian, I followed the same approach.
The new library, "Easym-b", again allows us to develop
for Windows CE and Symbian simultaneously. With it we bypass the
ugly Symbian emulator, improve debugging, and generally cut down
development time tremendously. Theoretically, you could develop
a game for Symbian without actually owning the device.
Obviously,
it would be nice if this could be taken a couple of steps further.
First, it must be possible to write a game for Symbian and Pocket
PC simultaneously. Second, there are some other new devices that
look promising as mobile game platforms. The Zodiac, the GP32, and
some of the latest Palm devices also happen to use ARM or ARM-based
processors. So, ideally, the new library should not only support
Symbian and Pocket PC, but also allow new platforms to be plugged
in. Once the "OS abstraction layer" for the new device
is written, existing games built on the library will at the very
least compile without modifications. The bottom line is that game
coders can concentrate on what they do best: Game coding, without
spending endless hours researching the OS and device vagaries.
"Mobilecore"
is a library that aims to do just that. It does nothing more than
abstract the absolute core of each supported OS: it initializes
the OS (WinMain),
and handling hardware (mouse/stylus, buttons, vibration, sound,
and perhaps communications). Currently, Mobilecore supports Win32,
Pocket PC, and Symbian.
Mobilecore
consists of a small set of sources: One source and one header for
each supported platform, plus a surface class. The platform-specific
source implements a class named Core,
from which a game can be derived. The Core
class is the interface for the game to the OS, and vice versa.
Abstracting
the Symbian OS
As
the Symbian OS is the most limiting of the supported operating systems
so far, I will use it to describe the structure of the library.
When
developing an application for Symbian, you can choose between an
"exe" and an "app." The "exe" form
is used to build applications that do not need a user interface,
so it is primarily used for services. Exe programs do not appear
in the menu of the phone, although you could start them using a
small launcher app. They also have limited access to other processes
and to the Symbian's GUI. It is hard to get to system events such
as incoming phone calls. As we want a game to behave as nicely as
possible when running on a phone, the preferred type of application
is the "app" form.
The
"app" is basically a DLL. The "app" must supply
a method named NewApplication
that returns a pointer to a CEikApplication.
From here, a typical Symbian application instantiates a "document"
(derived from CEikDocument)
which has one or more "controls" (derived from CCoeControl).
To behave nicely under the Symbian OS, we will implement our game
as a control.
Perhaps
you are beginning to understand what the main problem is with Symbian:
It's a bit strange Symbian series 60 is notorious for its large
number of frameworks, classes, and templates and general unforgiving
nature to programmers. Debugging is hard, while information is sparse
and often inaccurate or incomplete. And the OS simply does not appear
to be made for games. If you thought Windows was terrible after
you coded in mode 13h for years, prepare for your next nightmare.
Here lies a great task for Mobilecore: Let's get it over with once
and for all.
|
|
void Core::ConstructL(const TRect& aRect)
{
CreateWindowL();
SetRect( aRect );
ActivateL();
m_OSBitmap.Create( TSize( SCRWIDTH,
SCRHEIGHT ), EColor4K );
m_Timer = CPeriodic::NewL( CActive::EPriorityStandard
);
m_Timer->Start( 25000, 25000,
TCallBack( Core::Period, this ) );
...
}
|
 |
 |
 |
Listing
1: Setting up the core
control under Symbian
|
Listing
1 shows the main part of the control initialization. The control
creates a window for itself (CreateWindowL),
tells the OS how much screen real estate it will be using (SetRect),
and activates itself for drawing (ActivateL).
It then creates a bitmap and starts a timer that calls a callback
function 40 times a second. The timer is an odd thing by the way:
Symbian does not let you time things with more accuracy than 1/64th
of a second, yet you specify timer intervals in microseconds.
This timer issue is nasty: It makes it virtually impossible to determine
the frame rate of a 3D game, for example.
The
ARM processor has some onboard high-precision timers, so it's possible
to get more accurate timing. This requires some assembly coding
though.
Since
our Symbian application is now heavily based upon this timer ticking
40 times a second (or any other interval), the Mobilecore library
does the same. Therefore, the game class that we derive from the
Core class must at
least implement a Tick()
method (see code listing 2). A game based on this is going to be
state-driven.
|
|
class Core : public CCoeControl
{
public:
Core();
~Core();
virtual bool Tick() = 0;
Surface* GetSurface();
bool KeyDown( int a_Key );
int ReadKey();
int PenX();
int PenY();
bool PenDown();
virtual void ButtonDown( int
) {};
virtual void ButtonUp( int )
{};
char* FullPath( char* a_File
);
...
}
|
 |
 |
 |
Listing
2: Symbian version of the Core
class declaration
|
The
Mobilecore library tries to stick to its core task as much as possible:
Hiding OS specifics. Therefore, the other methods in the Core
class are all very simple. There are some methods for retrieving
the stylus position and whether or not it touches the screen, and
there are two callback functions for handling button state changes.
Two
methods are more noteworthy, however: GetSurface
and FullPath.
The
Symbian OS does not use relative paths. That means that opening
ERROR.LOG opens a file in the root of the file system, not a file
in the directory of the executable. FullPath
fixes this problem: It simply expands a given file name so that
it includes a full path.
The
GetSurface method
returns a pointer to a surface,
which is a simple class that manages a pixel buffer. Strictly speaking,
everything but the creation of the bitmap is platform independent,
so the surface class is not absolutely needed. However, I decided
not to be extremist here: Symbian uses a 12bit frame buffer, while
the Pocket PC uses a 16-bit buffer, and then there's the resolution
issue. The surface class is therefore the only piece of code that
is going to need a lot of OS-specific code: Loading images and other
graphics operations need to be able to handle the vagaries of the
devices.
That's
pretty much all there is to it. It's a simple frame, and it can
easily be ported to the Pocket PC.
You
can load the Symbian version of Mobilecore in Visual C++ 6.0; it's
included in the workspace file. You cannot build a final version
from the Visual Studio IDE this way, but you can check for errors.
Just hit F7, and the external compiler will be used to check for
problems, which can be clicked in the usual manner. Compiling the
final package must be done from the command line. (Check the relevant
documentation for details.) Note that we used the 0.9 SDK; later
revisions only add support for specific features of newer devices
so it is not necessary to upgrade.
______________________________________________________
|