Upload
tranhuong
View
228
Download
0
Embed Size (px)
Citation preview
Symbian OS – Workshop
© 2005 Mopius Page 2/48
Introduction
In this tutorial you will get to know some of the basics of developing for mobile
phones using C++. All the examples target Symbian OS and the Series 60 UI platform.
Instead of long, theoretical explanations, a more practical approach is used.
First, you will learn how to create your own mobile project. Then, you will add the
prewritten game logic of a small Arkanoid-like game called “Mopoid”. This provides an
interesting way to get to know important aspects of developing for Symbian OS,
including:
• defining and using menus,
• handling and displaying text,
• loading and showing images,
• writing and reading data to/from files,
• using a timer for periodic events,
• ... and many more smaller, but equally important topics.
You will see the results of your actions right away. Whenever some Symbian OS
specifics comes into sight (like memory handling), a short explanation will give you a
brief overview of why it is this way and how to work with it.
Of course, only the surface of all those topics can be scratched, as each of them would
easily fill a tutorial of their own of this size. However, this tutorial will give you a basic
understanding of how developing for mobile phones using C++ works, and it is a good
starting point for your own projects.
I hope that you have fun working your way through the tutorial and that you will get a
nice game out of it!
- Andreas Jakl
Symbian OS – Workshop
© 2005 Mopius Page 3/48
Contents
Step 0 - Preparation ........................................................................................................... 5
Getting started ................................................................................................................ 5 Choosing an IDE ........................................................................................................... 6 Choice of the Series 60 SDK........................................................................................ 7
Step 1 - Defining the Symbian OS SDK........................................................................ 8
Step 2 - Creating a new project ........................................................................................ 8
Step 3 - Testing the project .............................................................................................. 9
Troubleshooting: .......................................................................................................... 10
Step 4 - Defining strings ................................................................................................. 11
Step 5 - Defining the menu ............................................................................................ 12
Step 6 - Displaying the about box ................................................................................. 14
If something doesn’t work... ....................................................................................... 16
Step 7 - Getting the application to the mobile phone! ............................................... 17
Step 8 - Adding the game engine to your project........................................................ 18
Unzipping the file......................................................................................................... 18 Distributing the game data.......................................................................................... 18 Adding the source files to your project..................................................................... 18 Adding additional libraries to the project definition file......................................... 19 Defining new text ......................................................................................................... 20 New game...................................................................................................................... 20 Adding graphic and sound files.................................................................................. 20 Testing............................................................................................................................ 22 Troubleshooting ........................................................................................................... 22
Step 9 - Loading images .................................................................................................. 22
C and T classes ............................................................................................................. 23 ConstructL..................................................................................................................... 23 Loading bitmaps ........................................................................................................... 24 Deleting images ............................................................................................................ 26
Step 10 - Displaying the graphics..................................................................................... 27
Handling transparency................................................................................................. 28 Drawing a bitmap......................................................................................................... 28
Step 11 - Handling keys..................................................................................................... 29
Symbian OS – Workshop
© 2005 Mopius Page 4/48
Step 12 - Displaying text ................................................................................................... 31
Setting a font ................................................................................................................. 31 Reading text from the resource file ........................................................................... 32 Formatting and displaying the text ............................................................................ 32 Aligning text .................................................................................................................. 32
Step 13 - Reading and writing files .................................................................................. 34
Preparation .................................................................................................................... 34 Setting the file name..................................................................................................... 34 Opening the file ............................................................................................................ 35 Creating the stream ...................................................................................................... 35 Writing the data ............................................................................................................ 35 Closing the file .............................................................................................................. 36 The Cleanup Stack ....................................................................................................... 36 Loading .......................................................................................................................... 37 Cleanup when your application is uninstalled .......................................................... 38 Building from the command line ............................................................................... 40
Step 14 - Setting the application icon.............................................................................. 40
Step 15 - Handling being in the background ................................................................. 42
Step 16 - Periodic Events.................................................................................................. 44
Keeping the backlight on ............................................................................................ 44 Using a timer ................................................................................................................. 45 Stopping the timer........................................................................................................ 45
Step 17 - Final notes .......................................................................................................... 46
About the author .......................................................................................................... 46 Contact........................................................................................................................... 46 Copyright ....................................................................................................................... 47
Step 18 - Exercises ............................................................................................................. 48
Alternative key handling.............................................................................................. 48 Define more levels ....................................................................................................... 48 Saving the game progress ............................................................................................ 48
Symbian OS – Workshop
© 2005 Mopius Page 5/48
Step 0 - Preparation
Download the following components:
• ActivePerl: needed by the Symbian OS tool chain to compile your project –
http://www.activestate.com/
• Symbian OS SDK: this tutorial uses the Series 60 SDK 1.2 for Symbian OS,
Nokia Edition to provide the best compatibility with older Series 60 devices –
http://www.symbian.com/developer/sdks_series60.asp
• Microsoft Debugging Tools: needed to debug applications in the emulator
through Borland’s C++BuilderX –
http://www.microsoft.com/whdc/devtools/debugging/
• Visual C++ Toolkit: only needed if you don’t have Visual Studio 6 or .net
installed. It’s free and needed to compile the projects for the emulator. Go to
http://www.microsoft.com/downloads/ and search for “Visual C++ Toolkit” to
download it.
• Borland C++BuilderX Mobile Edition (v1.5): get it for free by filling out the
short survey at http://info.borland.com/survey/cbx15_mobile_edition.html
Install the components in the order listed above. Allow all installations to add the
programs to the system’s path variable, in case they ask! It’s recommended that you
install the Series 60 SDK to its default location at C:\Symbian\ !
Getting started
Should your application crash in the emulator, there is a way to make it display a more
helpful error message. To activate this feature, create an empty file called “ErrRd” in
the “C:\Symbian\6.1\Series60\Epoc32\Wins\c\system\Bootdata” directory of
your Series 60 SDK. That’s it.
When you first start your copy of C++BuilderX Mobile Edition, it needs to be
registered. Choose the activation file option, and locate the file that Borland sent you
after you filled out its survey and registered for its development network.
Symbian OS – Workshop
© 2005 Mopius Page 6/48
Choosing an IDE
The Symbian OS build tools are actually command line-based and would work without
any IDE. However, development is more comfortable if you use one. There are several
choices, each with advantages and disadvantages1:
Fast; efficient compilation and debugging; some tools for Symbian OS available
Microsoft Visual C++ 6
Old; no real support for Symbian OS code development
Fast; modern and good IDE; Widespread Microsoft Visual Studio .net
Not directly supported by the Symbian OS SDKs – makes development for Symbian OS even more complicated; expensive
Symbian OS at least partially integrated Metrowerks CodeWarrior
Not so cheap; IDE more difficult to master at the beginning
Supports visual design of menus and dialogues; Symbian OS related features; currently free!
Borland C++BuilderX Mobile Edition 2
Slow and clumsy IDE; Symbian OS integration should go farther.
Very good IDE; free! Eclipse
No support for debugging; Symbian OS not really integrated yet.
As you can see, there is no obvious ‘best’ way to develop Symbian OS C++
applications. For this tutorial, Borland’s C++BuilderX was chosen, because it is free
and it takes away much of the initial complexity when starting with Symbian OS, even
though the IDE itself still needs a lot of improvement.
Please note: The list above is based on personal impressions. CodeWarrior could also
be a very good choice, especially now that its mobile edition was bought by Nokia and
will be actively improved in the future.
1 All products are trademarks of their respective owners.
2 Will be referenced as “C++BuilderX” in this tutorial.
Symbian OS – Workshop
© 2005 Mopius Page 7/48
Choice of the Series 60 SDK
The Series 60 SDK for Symbian OS is available in higher version numbers, which target
newer Series 60 phones. Up to now, the devices have always been backward
compatible, so an application developed for Symbian OS v6.x mobile phones (using the
SDK v1.2) also works on newer Symbian OS v7.0s and v8.0 phones.
While applications compiled using the SDK v2.0 (which mainly targets the Nokia 6600
with Symbian OS v7.0s) should still work on older devices, this can be a bit
problematic. If the source code is compiled by even newer SDKs, the files are not
backward compatible on a binary level any more.
Which version of the SDK you use is entirely your choice. If you only want to develop
for newer phones, or if you want to do new things that were not possible before, go
with (at least) the SDK v2.0. Loading images and sounds has been specifically
improved. Internet (HTTP) connections are easier to handle as well.
If you want to develop a commercial application that should reach the broadest
possible audience, you shouldn’t exclude older Series 60 phones. By choosing to target
Series 60 you already restrict your potential market. If you limit your target audience
even more by making your program only compatible to the newest phones, you will
have an even harder time selling it.
Symbian OS – Workshop
Step 1 - Defining the Symbian OS SDK
It’s time to start working! Open Borland’s C++BuilderX Mobile Edition. Go to
“Tools” → “Symbian SDK Configuration”. Create a new SDK configuration, using the
SDK Template of the SDK you have installed, in this case: “Symbian Series 60 1.x
(Microsoft version)”. At least for the SDK v1.2, do not use the Borland SDK
version, otherwise compiling the project won’t work! Set the path as shown in the
screenshot, and choose an appropriate name like “Series 60 1.2”.
Fig. 1 – Defining the Symbian OS SDK in Borland's C++BuilderX.
Step 2 - Creating a new project
Create a new project: “File” → “New...” → “Series 60” → “Series 60 GUI Application”.
Fig. 2 – Creating a new Symbian C++ project.
Enter “Mopoid” as its name (don’t use a different name for this project, otherwise
some includes of the prewritten files we will include later on won’t work). Place it in the
“C:/Symbian/dev” directory and let C++BuilderX create a project subdirectory, so
that all your projects will be kept separated from each other.
© 2005 Mopius Page 8/48
Symbian OS – Workshop
Fig. 3 – Defining properties of the project (Step 1).
Go to the next step. Enter the same name for the project, “Mopoid”. As we want to do
a game, choose “Full Screen” as view type.
The default UID3 proposed by C++BuilderX should NOT be used. Change the UID
to something between 0x01000000 and 0x0FFFFFFF – Symbian reserved those IDs
for testing projects. Every application installed on a phone has to have its own unique
UID. Therefore, if you want to release your game to the public, send a mail to
[email protected] containing your name and how many UIDs you need (5
should be sufficient for the beginning). They will send you the UIDs as soon as
possible.
Fig. 4 – Defining properties of the project (Step 2).
Step 3 - Testing the project
Your workspace should look similar to the screenshot in Fig. 5. Now is the time to test
your project to see if everything is configured correctly. Press the “Run Project“ button
(F9).
© 2005 Mopius Page 9/48
Symbian OS – Workshop
Fig. 5 – This is what the default workspace of Borland's C++BuilderX looks like.
If everything works well, the emulator will show up after some time. Deactivate the
memory resident virus scanner if you have one running to make this process a bit
faster. Your own application will be located at the bottom of the menu, so move down
using the cursor keys to select it.
If you don’t want to do this every time you test your game, select it, press the left
softkey (“Options”), choose “Move” and move it to the upper left position of the
menu. It will stay there even if you close the emulator and start it again.
When you start the “Mopoid” application, you will only see a white screen. This is
because we chose to create a full screen application, which does not have a visible
status or menu bar. When you press the left softkey, a default menu will show up. In
the next steps we will adapt it to our needs.
Troubleshooting:
If Microsoft’s VisualStudio.net or Microsoft’s toolkit is installed on your PC and
Borland C++BuilderX uses this compiler for the Series 60 SDK v1.2, you may get this
error message:
LNK2019: unresolved external symbol __ftol2
© 2005 Mopius Page 10/48
Symbian OS – Workshop
© 2005 Mopius Page 11/48
To solve this problem, open the file
“C:\Symbian\6.1\Shared\EPOC32\Tools\cl_win.pm” in a text editor, search for
the line that contains “/W4” and change it to “CLFLAGS = /nologo /Zp4 /W4
/QIfist” (you’re adding the “/QIfist” parameter to the options).3
Should C++BuilderX display errors like this during the build process:
Can't locate E32env.pm in @INC [...]
one possible solution is to put the following two paths at the beginning of your path
environment variable:
C:\Symbian\6.1\Shared\epoc32\gcc\bin;C:\Symbian\6.1\Shared\epoc32\tools;
If this doesn’t help, make sure that Perl is installed and do a repair install of the
Symbian OS SDK, basically making sure that the SDK is installed after Perl on your
system.
Make sure that you close the emulator window after you are finished with testing your
project! If you want to compile and the emulator is still running in the background, you
will get several error messages!
Step 4 - Defining strings
By default, the “MopoidContainer.akn” file should be active. If it is not, open it
through the project window on the left: “Mopoid.cbx” → “Symbian project (bld.inf)”
“Mopoid.mmp” → “Designs” → “MopoidContainer.akn” (double click).
To make our own menu items, we first have to define the text that we want to use. On
Symbian OS, text is normally defined in resource files. This makes it easier to localize
applications, as mobile phones are global and you will probably want your game to be
available in more than one language. Switch to the “String Table” designer:
3Thanks to Simon Woodside for this tip – see http://simonwoodside.com/weblog/2004/07/18
Symbian OS – Workshop
Fig. 6 – switching to the string table designer.
In the game we want to have a simple menu that allows us to start a new game, display
an about box and quit the game. Therefore, define the following four strings:
Fig. 7 – Adding new strings to the resource file of the application.
For the message in the about box (“r_aboutmessage”), you will first have to increase
the “Max Length” to something like 60, so that you have enough room for your
message. Use “\n” to tell the system where to start a new line. Always press enter after
you finished writing the text to a cell, to make sure that C++BuilderX keeps your new
text.
Please note: For this tutorial, enter the names exactly as described. In a later step, you
will add some prewritten source files to your project, which assume several name
definitions.
Step 5 - Defining the menu
It’s time to use our strings in the game. Switch to the Menu designer. You will see that
C++BuilderX has already created two menu items for you. We need one more, so
select the menu item tool on the left pane, and click into the menu to add a new item at
the bottom of it:
Fig. 8 – Adding a new menu item using the design tool.
© 2005 Mopius Page 12/48
Symbian OS – Workshop
Now we’ll adapt the menu items so that they use the text we’ve just defined.
Select “iMenuItem1” by clicking on it. In the panel on the right, change its name to
“iMenuItemNewGame”. For its text, just choose our resource “r_startgame” for the
ID. Leave the command at its default value of “1001”.
Please note: It’s important that you press Enter after modifying a value. If you just
switch away, it’s possible that your input isn’t saved.
Fig. 9 – Setting the properties of a menu item.
Then, change the name of “iMenuItem2” to “iMenuItemAbout”; choose “r_about”
for its Text-ID. The command “1002” is fine. Also check the second flag,
“EEikMenuItemSeparatorAfter”. This will create a small line below this menu item,
visually separating the third (Exit) command from the other items.
Last one is the menu item to quit the game. Change its name from “iMenuItem3” to
“iMenuItemExit” and use “r_exit” for the Text ID. This time, we will not keep the
standard command ID, but use the “EAknCmdExit” command. This command will
also be sent to your application if the operating system wants to shut it down, for
example when the phone does not have enough memory left. Therefore it is required
that every application always responds to this event and instantly shuts the application
down.
If you try your game now, you will see that the “Exit” menu item will already work fine!
When testing the game in the future, always quit your application using the exit
command and don’t just close the emulator. The reason is that the environment of the
emulator will automatically do a memory check and inform you if you have a memory
leak. If you just shut down the emulator, you will only discover it many hours later,
making it a lot more difficult to find the reason.
© 2005 Mopius Page 13/48
Symbian OS – Workshop
Step 6 - Displaying the about box
Up to now, we’ve created a small application with its own menu, without writing a
single line of code. For displaying the about box, the first line of code has to be written.
Switch to the “Non-visual” design view:
Fig. 10 – Adding an about box to the application.
Select “CAknInformationNote” in the tools panel (left). This is a simple small window
that will contain your text and a small, predefined icon. Next, click anywhere in the
white middle section to create the note. In the properties window on the right, change
its name to “iAknAboutNote” and choose the “r_aboutmessage” as the text ID.
Now we’ve defined the about box, to display it we just have to connect it to the about
menu command. Switch back to the menu designer, choose the “About” menu item.
Fig. 11 – Calling the about box through selecting the menu item.
In the properties section on the right, switch to “Events”. Double click in the empty
text box next to “OnViewCommand”. C++BuilderX automatically creates a function for
you that will be called when the user selects this menu item and places you there. But
before writing any code, switch back to the “MopoidContainer.akn” and change the
name of the function the IDE has just created to
“OniMenuItemAboutViewCommandL”, basically adding an “L” at the end of the name.
© 2005 Mopius Page 14/48
Symbian OS – Workshop
Why will be explained soon. Press enter and C++BuilderX should take you to the
function again; this time it has the corrected name.
Fig. 12 – The code for displaying the about box.
Now, write the code seen in line 175 of the screenshot above in
“MopoidContainer.cpp”. This is a call the function that C++BuilderX has already
created for us, directly above.
© 2005 Mopius Page 15/48
When you try the program now, the about box will already work fine. But let’s take a
closer look at why we added the “L” at the end of the function name.
You will note that the function (“ExecuteiAknAboutNoteL()”)
also has an “L” at the end of its name. Why is this? The reason is
that memory is allocated in this function (for creating and
displaying the dialogue box – also note the “ELeave” in its c
line), and that this process can fail. Currently, mobile phones have very limited
resources. Therefore, it is very important to take care of what happens when the
operating system is unable to provide the resources that your application needs.
reation
Fig. 13 – The about box in the Series 60 emulator (SDK v1.2).
Symbian OS – Workshop
© 2005 Mopius Page 16/48
These events, as well as other errors (like file not found), are handled by a system which
is similar to try/catch of Java and modern C++. An error is passed up in the call
hierarchy, until someone takes care of it. To make the possibility of a leave visible to
the developer, it is a standard convention in Symbian OS to append an “L” at the end
of the function name.
The function that displays our about message leaves if not enough memory is available.
Normally, you don’t (and shouldn’t) take care of this problem and let the system display
the appropriate error message. Handling every error yourself would make your code
huge.
If there is an error, the event will automatically be passed up to the next function in the
call stack. This means that the “OniMenuItemAboutViewCommandL()” function can
also leave. Therefore, we added the “L” to the end of its name.
So far, so good. However, C++BuilderX also created a command dispatching function
called “DispatchViewCommandEvents()” with the note “NOTE: This routine is
managed by the C++BuilderX IDE - DO NOT MODIFY”.
Note that the “L” is missing at the end of it. If we added it manually, C++BuilderX
would no longer recognize this function. If we go another step up in the call stack,
you’ll get to the “HandleCommandL()” (“L”!) function of the “MopoidView.cpp” file.
That’s the function the operating system calls when the user selects your “About”
menu item!
So, because of the inflexibilities of the IDE, we already have a tiny bit of code that does
not conform to the Symbian OS coding conventions. In this case, we will just ignore it,
as the “L” is not for the system, but just a helpful thing for us developers. But it was a
good excuse to explain the basics of the leave system to you, and to show you the
limitations of C++Builder’s automatically generated code.
If something doesn’t work...
... and you would like to find out why, the file “Mopoid.Step7.zip” is a working
project of what you have done up to now. From now on, a zip file of the reference
project will be available after each step!
Symbian OS – Workshop
Step 7 - Getting the application to the mobile phone!
Normally the emulator works really well. But you might still get into situations where
the code works fine in the windows emulator, but simply crashes on the phone. The
most prominent examples are static member variables, which work fine in the emulator,
but do not work on the mobile phone!
As debugging directly on the mobile is difficult and doesn’t always work, it will be
difficult finding the reason for the crash when your phone just reports “System error”.
However, if you regularly try your application on your phone, it will be easier to locate
the problem.
To tell the IDE that you want to build for the device, you have to switch the target
platform of the compilation process to “ARMI” (the processor on your Series 60 phone
is an ARM processor) and the type to “UREL” (Release version). To build your project,
choose “Project” → “Make Project ‘Mopoid.cbx’” (Ctrl+F9).
Fig. 15 – Compiling for release instead of debug.
Fig. 14 – Changing the build
target of the application.
After this process has finished, a file called “Mopoid_ARMI_UREL.sis” will reside in
the directory “C:\Symbian\dev\Mopoid\group”. Transfer this file to your phone and
install it.
You can either do this using the PC Suite that came with the phone, or just send it to
the device through Bluetooth or IrDA link (if available) by right-clicking on the file,
“Send to...” → “Bluetooth device”. For more instructions on how to install a file, read
the manual that came with your phone.
If everything works fine, don’t forget to switch back to the “WINS / UDEB” build
configuration when you continue development!
© 2005 Mopius Page 17/48
Symbian OS – Workshop
© 2005 Mopius Page 18/48
Step 8 - Adding the game engine to your project
Developing a whole game takes a lot of time, and in this tutorial we want to explore the
basics of development for Symbian OS, instead of using a lot of time to create game
routines. Because of this, we will import some prewritten files into our project and add
several important and interesting features.
Please note: it is possible to create a game with a more beautiful object oriented
structure. However, the provided files were written in a way to make it easy to
understand the Symbian OS related issues, and not to provide a perfectly structured
program.
Many of the steps below will be important when you develop your own project. You
will also most likely want to add graphic and sound files, add existing source files and
so on.
Unzipping the file
Unzip the file “Mopoid.Step8.Update.zip” to your “C:\Symbian\dev\” directory.
Make sure that you keep the directory structure of the archive! Overwrite all files. If
there is no warning that files are overwritten, you didn’t extract it to the right directory.
Distributing the game data
The archive contains a new directory called “data”, which includes graphics and
sounds for our game. When you execute the application in the emulator, it will look in
an own directory for any files. Call the “.\data\dobitmaps.bat” file to automatically
create the corresponding directory and copy all data files there. If your Series 60 SDK is
not installed in the default location (“C:\Symbian”) or if you are using a different SDK
than v1.2, you will first have to adapt the directory names in the batch file.
Adding the source files to your project
Next, you have to tell the IDE that you want to add some new source files to the
project. Right-click on the “Sources” item of the project window, and choose “Add
sources...”
Symbian OS – Workshop
Fig. 16 – Adding additional source files.
Select and add all files from the “./src/” directory which are not already part of your
project:
Fig. 17 – The files to add.
Adding additional libraries to the project definition file
Your final game will do a lot of things – load and play sounds, display PNG graphics
and access files. These functions require some libraries provided by the SDK. The only
thing you have to do is to add them to the project. Double-click on “Mopoid.mmp” in
your project definition window. This is the Symbian OS project definition file that
contains references to all source files and libraries that are needed for your project (and
some other stuff).
Scroll to the bottom of the file and add the following lines below all other library
definitions:
LIBRARY efsrv.lib // For loading data files
LIBRARY bitgdi.lib // For drawing
LIBRARY mediaclientimage.lib // For loading png files
© 2005 Mopius Page 19/48
Symbian OS – Workshop
© 2005 Mopius Page 20/48
LIBRARY mediaclientaudio.lib // For loading and playing audio
LIBRARY estor.lib // For writing to & reading from files
Defining new text
A real game needs a lot of text. Define the following strings in the string table, as
described in Step 4. Add a space after the text items with a “:” at the end, as the
number will be appended to the text!
Name Text
r_score Score:
r_level Level:
r_pause Game Paused
r_gameover Game Over
r_finished You made it!
r_lifelost Life Lost!
r_lives Lives:
r_pressjoystick Press Joystick
r_highscore High Score:
r_enterlevel Entering Level:
r_title mopoid
New game
The start new game command has to be connected with the start game function of the
game engine. Create a new event function for the “Start New Game” menu item like
you did for the about box. This time, don’t change the function name to contain the
“L” suffix – the start game function doesn’t leave. Insert the following text into the
new “OniMenuItemNewGameViewCommand()” function:
iGameEngine->StartNewGame();
Adding graphic and sound files
Several .png and .wav files have been prepared so that the game has a nice look and
Symbian OS – Workshop
feel. You have to add them to the project, so that they will be copied to the mobile
phone! To do that, switch the build configuration to ARMI UREL using the blue
gearwheels. You should now see an entry called “Package File” in the project pane.
When adding the files, you have to take care, as C++BuilderX has a small bug...
Fig. 18 – Adding additional files to the package.
Right-click on “Package File” and choose “Add File to Package...”. You will get to a
dialogue window. Choose the first file of the “/data/” directory of our Mopoid
project (“ball.png”). Do not click on OK yet, or you will have to remove the file from
the package and start again.
Fig. 19 – Add the filename to the target path!
Add the file name (“\ball.png”) to the target path in the second text box! When
you’re done, click on OK.
The target path defines the location and name of the file on the device. If the file name
isn’t specified, the phone will not be able to copy the file! It would be great if the IDE
added the file name itself.
The “!” at the beginning of the line means that the user can choose where he wants to
install the application; more on that later.
The bug in C++BuilderX 1.5 is: after you have added a file, you can’t modify the target
path any more. The IDE does let you change it, but when you click on OK, it will
© 2005 Mopius Page 21/48
Symbian OS – Workshop
discard your changes without notifying you. Therefore, do not forget to add the file
name right when adding the file!
Do this for all .png and .wav files, and the levels.dat file. When you’re finished, your
project pane should look like this:
Fig. 20 – All files are now part of the project.
Testing
Switch back to “WINS / UDEB”. When you run your game now, it should still work.
However, the screen will just be black with only one line of text on it. In the next few
steps we will add all the parts that are missing!
Troubleshooting
This chapter was rather risky, combining your project with the prepared source files
only works if you really did everything exactly as described in the earlier steps. If you
have any problems and the project doesn’t work any more, use the reference project
from “Mopoid.Step8.zip”!
Step 9 - Loading images
Symbian OS C++ has built-in support for “.mbm” files. These are collections of RLE-
compressed bitmap files. Advantages are: they are fast to load and easy to handle,
however they take a lot of space on the mobile device. A game uses a lot of graphics,
with this system it would get huge.
Therefore, it’s better to use .png or .jpg files. Unfortunately, no predefined loading
routines are available, and it’s rather complicated to do it manually. Also,
decompressing those files takes some time, therefore it works asynchronously, in its
© 2005 Mopius Page 22/48
Symbian OS – Workshop
own thread. In Symbian OS v7.0s, an alternative way to load images is possible, but if
you want your game to reach the broadest possible audience, you will have to stick to
the ‘old’ functions (for now).
Developing and explaining the whole process of loading an image would be a tutorial
of its own, therefore a finished class has been provided – “PngLoader.cpp”. A second
file, provided by Nokia (“bitmapmethods.cpp”) helps with some common tasks.
Some more functions have been added to make it even more useful. You can simply
add those two files to your own future projects!
In the Mopoid project, a class is responsible for loading and storing all those images. If
an image is needed somewhere else, it can get the image by sending the ID of the
requested image to a function of the CSpriteHandler class.
C and T classes
What about the “C” at the beginning of the class name? In short,
this means that this class will always be constructed on the heap,
and it will most of the time own other (heap) objects. When the
destructor of our “C” class is called, those objects have to be destroyed.
The second most important type is “T” classes. It is basically like a simple data type, a
class of this type can’t have a destructor. For example the basic data types in Symbian
have a “T” prefix (TInt, TReal, ...)
ConstructL
Open the SpriteHandler.cpp file to see the source code of our bitmap manager
class. You will note that the constructor is empty, and you write your code in a function
called ConstructL(), which is called by NewL().
The reason for this lies in the memory situation of mobile phones. If you develop a
typical application for the PC, you will (in most cases) not have to worry about free
memory. Here, the situation is different. It can happen that the available memory runs
out, and that creating an instance of a class fails.
However, when you allocate memory in the constructor and this process fails, the
allocated memory will be orphaned. That’s bad. Therefore, code executed in a C++
© 2005 Mopius Page 23/48
Symbian OS – Workshop
© 2005 Mopius Page 24/48
constructor should never leave! However, the sprite handler should provide the
graphics right after the class is created.
The solution is simple – a two phase construction. Just move (at least) all memory
allocating code to the ConstructL() class method. You can then create the object as
usual, and then in the next line call this function. This gives you the opportunity to
handle leaves correctly.
Especially if you need to create an instance of your class more than once, you’d have to
write the code for these steps (allocation and calling ConstructL()) multiple times. To
prevent this, the two phased construction is usually handled by a static NewL() or
NewLC() function. As a general rule, NewL() has to be called when the object you want
to create is assigned to a member variable. NewLC() will leave the object on the cleanup
stack, useful for automatic variables. This will be discussed later on. For now, let’s
move on to implementing the bitmap loading!
Loading bitmaps
First, we have to define the filename of the graphic file we want to load. This is done
by:
_LIT(KBmpPanel, "panel.png");
This line creates a named object (KBmpPanel) that stores the string “panel.png”
directly into the application binary. Strings are handled differently in Symbian OS C++
compared to standard C++. Again the reason for this is the limited amount of memory
in mobile devices. In Symbian OS C++, strings are called “descriptors”, and they are
quite difficult to get used to. On the positive side, they use less memory than their C++
string counterparts.
Once we have loaded a bitmap, we need to store it somewhere. To view it, you have to
take a look at the header file of the sprite handler class. For whatever reason,
C++BuilderX doesn’t display those files in the project structure. Instead, you have to
open the source file, open its includes section in the file structure panel on the left, and
go to the include file from there:
Symbian OS – Workshop
Fig. 21 – How to get to the include file.
In the class declaration, you will find the following declaration of a private member
variable:
TFixedArray<CFbsBitmap*, MopoidShared::ENumSprites> iSprites;
Why is the variable called iSprites instead of sprites or m_sprites? This is
another Symbian OS C++ coding guideline. All member (instance) variables should
have “i” as their prefix. While we are at it, parameter variables should have “a” at their
beginning.
What about the type of the variable? This is basically a normal, fixed length C++ array
with additional range checking and some other useful functions (for navigating through
the array, getting its length, deleting each element and so on).
Go back to the Spritehandler.cpp file. Let’s fill our array with bitmaps. We do this
by calling a static method of our CPngLoader class that will do all of the work.
iSprites[ESpritePanel] = CPngLoader::LoadImageL(KBmpPanel, EColor4K);
Note that we tell the class to load the image as EColor4K. This means that the graphic
will have 4096 colours, and this equals the colour depth of earlier Series 60 devices like
the Nokia N-Gage or the Nokia 7650. Newer phones already support EColor64K (or
higher). However, for the graphics of a simple Mopoid game, we don’t need so many
colours anyway. ESpritePanel is the textual representation of an ID, called
enumeration, and is defined in “MopoidSharedData.h”.
Fig. 22 – The panel graphic
Fig. 23 – The mask of the panel.
We want our panel to have round corners, so we will also need a mask. The easiest
© 2005 Mopius Page 25/48
Symbian OS – Workshop
method to get this would be to have a second graphic file which contains the black and
white mask image. However, as said before, loading .png files takes quite some time, so
it’s better to generate the mask ourselves. Use the following method, which is also part
of the source code provided with this tutorial:
iSprites[ESpritePanelM] = NBitmapMethods::CreateMaskL(iSprites[ESpritePanel], EFalse, 0);
This function will create a mask bitmap based on the colour of the top left pixel of the
source image. If you don’t want all pixels that have the colour of the top left pixel to be
transparent, you can use the second and third parameter to specify a colour which
should be masked as transparent.
Now, load the ball image in the same way as the panel image. The file name is
“ball.png”, the enumeration names for the ids are “ESpriteBall” and
“ESpriteBallM”.
The bricks are a bit different. They have a rectangular shape and don’t require a mask.
However, we have got several different colours for the bricks. Instead of loading the
images one by one, it’s better to combine all of the graphics into one file and then split
them up after loading this image. With this method, the image files will require less disk
space (overall), and even though the application will temporarily need more memory,
it’s a lot faster.
Fig. 24 – 3 brick graphics in 1 file.
Load the brick image using the following code:
_LIT(KBmpBrick, "bricks.png");
CPngLoader::LoadAndSplitImageL(KBmpBrick, &iSprites[ESpriteBrickNormal], 3, EColor4K);
This code loads the image file, splits it up into three images and stores them in the
iSprites array, beginning at the ESpriteBrickNormal position.
Deleting images
When you try to execute your application, it should already load the images, even
though you won’t see them. We have not written the code to display them yet. Now try
© 2005 Mopius Page 26/48
Symbian OS – Workshop
to quit the application. You will see a warning like this:
Fig. 25 – Error message telling you about a memory leak.
This error message informs you about a memory leak somewhere in your application.
It’s quite difficult to find the offending code later on; because of that, remember to quit
your application in the emulator instead of just closing the emulator window.
In our case, we don’t free the memory allocated for the graphic files when the
CSpriteHandler object is destroyed. Fortunately, thanks to the TFixedArray class of
iSprites, this is easy to do. Writing the following code into the destructor of the class
will do all the work:
iSprites.DeleteAll();
Step 10 - Displaying the graphics
Just loading graphic files won’t help you much – you will also have to get them onto the
screen. In our application, the game engine class takes care of preparing the back buffer
image. This means that all graphics are drawn to an extra bitmap of the size of the
screen. When this process is finished, the whole bitmap is copied to the screen. This
prevents flickering, which would occur otherwise.
First, go to the ConstructL() method of the CGameEngine class. Uncomment the big
code block that does some size calculations with the graphics files that we have just
loaded. If you are interested, you can also take a look at the code, but it isn’t important
for us now.
The frame itself is drawn in the DrawFrame() method, which is further down in the
source code of the same class. Note that the function is defined as const, this means
that it’s not allowed to modify the member data of the game engine class.
© 2005 Mopius Page 27/48
Symbian OS – Workshop
© 2005 Mopius Page 28/48
Handling transparency
Let’s take a look at the following line:
iBackBufferBmpGc->SetBrushStyle( CGraphicsContext::ENullBrush );
When drawing to a bitmap, Symbian OS provides both a “pen” and a “brush” through
the graphic context of an image, which handles drawing operations. An example: when
you draw a rectangle, the outline will be drawn using the settings you assigned to the
pen, and it will be filled by your brush.
We don’t use those drawing functions, but we have to take care of this behaviour as
well! If we define a solid brush and want to draw a bitmap that has transparent parts,
those parts will be filled with your current brush colour and its other properties! That’s
why we set the brush to a null brush, letting the background of a bitmap shine through
its transparent parts.
Drawing a bitmap
First, we’ll draw the ball. The first line is already here, it calculates the position of the
ball and puts it into a TPoint variable. This automatically provides an x and y
coordinate and is very useful.
Write this line directly after it:
iBackBufferBmpGc->BitBltMasked(ballSpritePos, iSpriteHandler-> GetSprite(MopoidShared::ESpriteBall), iBall.iSize,iSpriteHandler-> GetSprite(MopoidShared::ESpriteBallM), EFalse);
It calls the BitBltMasked() function of the back buffer bitmap’s graphic context,
which basically copies a masked source bitmap to a target bitmap. The first parameter is
the position, the second the source image, the third the size of the image we want to
copy, the fourth the mask to use, and the fifth whether to invert the mask or not.
To look up the parameters of the functions provided by Symbian OS APIs, take a look
at the Symbian OS help. Search for the “SDK Help” in your start menu under
“Symbian 6.1 SDKs” -> “Series 60” -> “Documentation”.
Now, scroll down a bit and do the same for the panel. For its position, use the value
stored in “iPanel.iPos”. The Enumeration IDs for the sprites are “ESpritePanel”
and “ESpritePanelM”. The size is: “iPanel.iSize”.
Symbian OS – Workshop
As we have found out before, the bricks don’t need transparency. Generally you should
take care not to draw large transparent areas, as this slows performance.
The function to draw a bitmap without a mask (BitBlt()) is similar, and even a bit
easier to use. You only have two parameters, the position and the source bitmap. Put
the following line of code at the marked position inside the for loop, which loops
through the bricks grid to draw all visible bricks.
Fig. 26 – Our Mopoid game can now display graphics!
iBackBufferBmpGc->BitBlt(iGrid.ConvertGridToXY(x,y), iSpriteHandler->GetSprite(spriteId));
When you run your application now, it should already look quite decent:
However, there is no interactivity yet. To enable this, we have to handle key presses!
Step 11 - Handling keys
It’s quite easy to handle key presses in Symbian OS. The framework will automatically
call the “OfferKeyEventL()” method of your container class (CMopoidContainer).
The code, which was created by the C++BuilderX IDE, forwards this call to the
HandleKeyEvents() function (this wouldn’t be necessary in our case, as nothing else
has to handle key presses in our application).
The HandleKeyEvents() function gets two parameters – const TKeyEvent&
aKeyEvent and TEventCode aType. The first one (aKeyEvent) contains information
about the button that was pressed. The second parameter (aType) tells you what kind
of event happened.
In our Mopoid game, we want the panel to continuously move as long as the user
presses the direction key (in our case, he has to use the joystick). To get this behaviour,
© 2005 Mopius Page 29/48
Symbian OS – Workshop
© 2005 Mopius Page 30/48
we have to set a flag when the key is pressed, and unset it when the user releases the
key again.
A special class that has been prepared (TKeyHandler) stores the direction that is
currently pressed by the user. It’s owned by our game engine class, which has to take
care of sending movement events to the panel.
The task we have to do now is rather simple; therefore we’ll only write the code for the
key down event. The prewritten source code already contains the rest. Search for the
part of the “if” statement that takes care of key down events (EEventKeyDown).
Inside, you will find a switch statement that takes care of the individual keys,
differentiated by their scan codes.
Here we’ll handle two additional cases: “EStdKeyLeftArrow” that will be called when
the player moves the joystick to the left and “EStdKeyRightArrow” for the other side.
To send the event to the key handler we mentioned previously, you have to call:
iGameEngine->iKeyHandler.LeftPressed();
Of course this is slightly different for the right side. When you take a look at the
HandleKeyEvents() function, you’ll notice that it has a boolean return value. This will
tell the system if the key press has been handled. If it hasn’t, it will be sent to the next
application that might be running in the background (Symbian OS is a multitasking
operating system!).
Because of that, we have to tell the system that we took care of this key press and that it
doesn’t have to send it to anyone else. Set the flag that’s defined at the beginning of the
function accordingly:
handled = ETrue;
Note that Symbian OS C++ uses its own definition of boolean values. The data type is
TBool and can have the values ETrue and EFalse.
Don’t forget to add a break statement after each case handled in the switch statement!
After you have written the code, test it in the emulator. You should be able to move the
panel, the ball bounces and you can already play the game! The code of this step is just
standard C code, but if you have problems take a look at the MopoidContainer.cpp
file of Mopoid.Step11.zip.
Symbian OS – Workshop
Step 12 - Displaying text
The game is still lacking an important part – it doesn’t display any status information
informing the player about his points, lives or level. We’ll add this now. Go to the
DrawFrame() method of the CGameEngine class.
Setting a font
Luckily, predefined functions take care of displaying the text, and are even able to align
it. But first, we have to tell the graphics context of the bitmap which font and colour to
use. Write the following code below the “// Hud” comment.
iBackBufferBmpGc->SetPenStyle(CGraphicsContext::ESolidPen);
iBackBufferBmpGc->SetPenColor(KRgbRed);
iBackBufferBmpGc->UseFont(CEikonEnv::Static()->AnnotationFont());
TBuf<30> tempStr;
Let’s analyze it step by step. The first line will already sound familiar, it defines the pen
style. In an earlier section, we explained that this is used for outlines of boxes. It’s also
used for the font outline, which is the font. In our case, we set the pen style to solid, so
that we actually see the text that we draw.
The next line defines the color. We use one of the default values, a plain red. If you’d
like to define your own RGB color, use this:
iBackBufferBmpGc->SetPenColor(TRgb(255, 128, 0));
The next line chooses one of the standard system fonts. Symbian OS provides more
complex font mechanisms, as well as the option to create and use your own fonts. For
a simple Mopoid game, we’ll stick to the easiest method and use the AnnotationFont.
It’s a medium sized, bold font.
The forth and last line creates a descriptor, which is the Symbian
OS equivalent of a string. It has a maximum length of 30 chars. A
TBuf is derived from a class called TDes, which provides several
functions to manipulate the string data. We’ll use those to set the
contents of the text. Note that the TBuf is created on the stack, which is very limited
on mobile devices. Therefore, don’t use it for strings that are longer than 256 chars. For
everything else, a heap based descriptor (HBufC) should be used.
© 2005 Mopius Page 31/48
Symbian OS – Workshop
© 2005 Mopius Page 32/48
Reading text from the resource file
Next, we’ll read one of the strings that we have defined in the resource file (through the
string table of the C++BuilderX IDE). A static function of the CEikonEnv class takes
care of this and writes the string directly into the descriptor (which we have defined
before). The string resource got the name RS_R_SCORE. Normally it should only be
R_SCORE, however, C++BuilderX adds an additional RS_ in front of the name.
CEikonEnv::Static()->ReadResource(tempStr, RS_R_SCORE);
Formatting and displaying the text
The string we’ve read from the string table only contains a static text: “Score: ”. We
have to add the current score of the player at the end of the string. The AppendNum()
function provided by the descriptor handles this.
tempStr.AppendNum(iSettings.iScore);
Now, the text is ready to be displayed on the screen. We can print it at a specific x/y
position using the following code:
iBackBufferBmpGc->DrawText(tempStr, TPoint(5, 25));
Please note: the y coordinate of 25 specifies the lower border of the text, so 5/25 is
the lower left point of the text, not the upper left as you may have expected!
We also want to display the high score. Write the code for this yourself. Read the text
resource “RS_R_HIGHSCORE” into the same temporary descriptor. This will overwrite
the content currently stored, which we don’t need any more (it has already been printed
to the screen). Append the number that you can get through
“iSettings.iHighScore” and print the text at 5/38 (one line below the current
score).
Aligning text
Now the game will display the current score and the high score on the left side of the
screen.
We also want to display some information on the right side of it:
Symbian OS – Workshop
Level Text: RS_R_LEVEL
Value: iSettings.iLevel
Position: y: 28, x offset from the right side of the screen: 3
Lives Text: RS_R_LIVES
Value: iSettings.iLives
Position: y: 38, x offset: 3
Read and format the strings as before, only the printing is different. Luckily, Symbian
OS provides a function that aligns text:
iBackBufferBmpGc->DrawText(tempStr, TRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 25, CGraphicsContext::ERight, 3);
The TRect(...) defines a rectangle which is used to align the text. In our case, it’s the
whole screen. To optimize the program, you could define the Screen TRect only once
at the beginning of the function, and use the variable in all calls that need it.
SCREEN_WIDTH and SCREEN_HEIGHT are defines made by us. Series 60 is a
standardization of the user interface; therefore the screen size of all Series 60 phones is
the same (176x208). In the future, this will get more flexible and bigger screen sizes will
be introduced to Series 60. The third parameter specifies the y position, the fourth the
alignment and the last one the x offset.
The status messages work in a similar way. To activate them, uncomment the code
block below. If you take a quick look at it, you’ll see that it uses the TitleFont(),
which is bigger than the AnnotationFont. It also centers the status message.
When you start the game now, it should look like this:
Fig. 27 – The Mopoid game with text.
Great, isn’t it? To make the game really cool, there are still several things left to add...
© 2005 Mopius Page 33/48
Symbian OS – Workshop
© 2005 Mopius Page 34/48
Step 13 - Reading and writing files
This section will be very important for your future projects. In our Mopoid game, we
will just save the high score to demonstrate how reading and writing files works.
Preparation
Open “MopoidGameEngine.cpp” and go to the “MopoidGameEngine.h” file. Before
the class declaration starts, you will see that we have already defined the file version
number and the file name:
#define MOPOID_FILE_VERSION_NUMBER 1
_LIT(KMopoidDataFile, "settings.dat");
Symbian OS C++ already provides some APIs to access files. First, the corresponding
header file has to be included. Add the following line at the top of the other system
include lines:
#include <s32file.h>
This includes all the functionality we need. We have already added the required libraries
(estor.lib, efsrv.lib) to our project definition (.mmp) in an earlier step.
Setting the file name
In our header file, we just stored the filename of our data file – the path has to be
added dynamically by the application. This can be done by adding the application path,
as we want our data file to be in the same directory where the user installed the game.
Go back to the “MopoidGameEngine.cpp” file and scroll down until you reach
SaveGameProgressL(). This code should be executed first in this function:
TFileName fullName(KMopoidDataFile);
CompleteWithAppPath(fullName);
#ifdef __WINS__
fullName[0] = 'C';
#endif
As you can see, we have to take care when our game is running in the windows
emulator. The environment will return that the application is installed on drive Z,
which is the ROM drive (where the system itself is installed on the phone). Apparently,
it’s compiled onto this (virtual) drive. It’s not writeable, therefore we have to adapt the
Symbian OS – Workshop
© 2005 Mopius Page 35/48
drive to the standard C drive. On the device, you will get the real path and drive where
the application was installed.
Opening the file
There are multiple ways to write to a file. Here, we will use the most flexible one, which
is based on streams that are put into a store (i.e. the file). This also allows serializing
objects directly into the file and is very helpful if you need to save the game status of a
more complex game.
We use a direct file store, which doesn’t allow modifying the data. That doesn’t matter
for us, as we will replace all of its contents anyway. This code will open the file store:
CFileStore* store = CDirectFileStore::ReplaceLC( CEikonEnv::Static()->FsSession(), fullName, EFileWrite);
store->SetTypeL(KDirectFileStoreLayoutUid);
Note that we use the file server session from the CEikonEnv environment. A file server
takes care of the real writing and reading to and from files. The file server exists only
once for the whole system and you can’t create your own instance – it would just be
possible to connect to it. However, this is an expensive operation, so we will reuse the
file server session that the system uses to read our resource file.
Creating the stream
A store can contain multiple streams; one of them has to be the root stream. For saving
the high score, only one stream is needed. Multiple streams could be useful for saving
the data of individual players. The stream is created and returns its ID, which will be
used later on when we set this stream as the root of the store.
RStoreWriteStream stream;
TStreamId id = stream.CreateLC(*store);
Writing the data
Finally, we get to the point where the data is written to the file. We will write two
integer values to the stream. In Symbian OS, a TInt is defined as a 32-bit variable,
therefore we’ll use the WriteInt32L() function of the stream to write the data.
Symbian OS – Workshop
© 2005 Mopius Page 36/48
// Write file version number
stream.WriteInt32L(MOPOID_FILE_VERSION_NUMBER);
// Write game progress
stream.WriteInt32L(iSettings.iHighScore);
It is not mandatory to write the file version to the stream. However, it has proven to be
very useful. Let’s assume the following situation – your customer has installed your
Mopoid game on his mobile phone. Later on, you release a new version, which also
saves the player name. Your customer downloads it and replaces the game on his
device. The old data file will not be deleted!
Then he starts up your new game version, which finds the (old!) data file and attempts
to load it, expecting to get the score and the player name. However, as the data file is
still from the old game, it doesn’t include the player name. It’s a lot easier to compare a
version number and handle the case correctly; for example you could also do an import
function...
Closing the file
You’re nearly finished. Just make sure that the data is written and close the file:
// Commit the changes to the stream
stream.CommitL();
CleanupStack::PopAndDestroy(); // stream
// Set the stream in the store and commit the store
store->SetRootL(id);
store->CommitL();
CleanupStack::PopAndDestroy(); // store
The Cleanup Stack
In the function that we have just written, the Symbian OS cleanup
stack is used. This is a very important part of developing in C++
for Symbian OS. The full scope of it is difficult to understand, but
is already covered by lots of literature. Therefore, here’s only a short summary:
Imagine this situation. In a method, you create an object on the heap, your variable is a
pointer to it. Should some function call leave before the method is over, your method
will be left immediately. The framework will clean up all the automatic variables.
Symbian OS – Workshop
© 2005 Mopius Page 37/48
However, in this case you just have the pointer to an object, which will be destroyed,
but leave the object itself unreferenced in memory. That’s bad.
Therefore, objects that are used through automatic variables have to be pushed onto
the cleanup stack. If a leave happens, the framework will automatically clean up all
objects that are in the cleanup stack.
Normally, you’ll use the cleanup stack like this:
MyObject x* = new (ELeave) MyObject();
CleanupStack::PushL(x);
x->DoSomethingDangerousL();
CleanupStack::PopAndDestroy();
In the file example, we called two functions that had a “C” at the end of their name.
This indicates that they leave an object on the cleanup stack, which you have to clean
up when you no longer need the object. Therefore, two PopAndDestroy()s are used.
Please note that even though the framework will clean up the cleanup stack when a
leave occurs, you have to take care of correct cleanup during an error-free program
execution yourself.
Loading
Saving a file is only half of the story. Fortunately, loading works in a similar way. First,
complete the file name as seen before. Next, the store has to be opened again, this time
for reading.
CFileStore* store = CDirectFileStore::OpenLC(CEikonEnv::Static()->FsSession(), fullName, EFileRead);
After opening the root stream...
RStoreReadStream stream;
stream.OpenLC(*store, store->Root());
... we can read the data:
TInt versionNumber = stream.ReadInt32L();
if (versionNumber != MOPOID_FILE_VERSION_NUMBER) User::Leave(KErrNotFound);
iSettings.iHighScore = stream.ReadInt32L();
Symbian OS – Workshop
You could improve the handling of a wrong version number, but for the first version
of the game it’s sufficient. Here we just leave with a not found error, meaning that we
were unable to find the correct setting information in the file. The call to this function
(in the ConstructL() of the game engine) traps the exceptions of the load function
and ignores a not found error, which also occurs if no file has been created yet. In the
case of a file not found error, the game simply sets the high score to the default value
of “0”.
Don’t forget about cleaning up after loading all your data!
CleanupStack::PopAndDestroy(2);
Cleanup when your application is uninstalled
Should the user ever decide to remove the game, all files have to be deleted from the
mobile device (this is also one of the requirements to get your application “Symbian
Signed”). The uninstallation is done by a program manger, and your own application
won’t be called when it’s being uninstalled. Therefore, you have to specify which files
will need to be removed right where we define how your application has to be installed.
Fig. 28 – The uninstall application has to remove the new data file as well.
Unfortunately, C++BuilderX is quite limited when it comes to defining your
installation file for your mobile phone, and it makes the task more complicated than it
would be without C++BuilderX.
Switch to build for the target device (ARMI / UREL) first using the blue gearwheels as
we already did in an earlier step. Right-click on the “Package File” in the project pane
and choose “Export PKG File”. This will create a very minimal “Mopoid.pkg” file in
the “./dev/Mopoid/group/” folder.
© 2005 Mopius Page 38/48
Symbian OS – Workshop
Fig. 29 – Exporting the package file.
Now, open the file with a text editor. It should look similar to this:
&EN
#{"Mopoid"},(0x01000001),0,1,0
(0x101F6F88), 0, 0, 0, {"Series60ProductID"}
"C:\Symbian\6.1\Series60\epoc32\release\ARMI\UREL\Mopoid.app"-"!:\system\apps\Mopoid\Mopoid.app"
"C:\Symbian\6.1\Series60\epoc32\release\ARMI\UREL\Mopoid.r01"-"!:\system\apps\Mopoid\Mopoid.r01"
"C:\Symbian\dev\Mopoid\data\ball.png"-"!:\system\apps\Mopoid\ball.png"
"C:\Symbian\dev\Mopoid\data\bounce.wav"-"!:\system\apps\Mopoid\bounce.wav"
"C:\Symbian\dev\Mopoid\data\bricks.png"-"!:\system\apps\Mopoid\bricks.png"
"C:\Symbian\dev\Mopoid\data\hit.wav"-"!:\system\apps\Mopoid\hit.wav"
"C:\Symbian\dev\Mopoid\data\levels.dat"-"!:\system\apps\Mopoid\levels.dat"
"C:\Symbian\dev\Mopoid\data\panel.png"-"!:\system\apps\Mopoid\panel.png"
It defines that the installation file only contains one language, English. The second line
contains the name of the application that will be displayed during the installation, its
UID that we set when we created the project, and the version number.
The third line defines that your application can be installed on all Series60 devices, even
on the old Nokia 7650 (which used v0.9 of the Series 60 SDK). If you develop an
application that isn’t backward-compatible, you have to use a different Series 60
Product ID.
Then, the package file defines that both your application and the resource file (which
contains strings and menu definitions) are copied to the device into the
“\system\apps\Mopoid\” folder. The “!” at the beginning means that the user can
choose on which drive to install the application. The internal, writeable memory of
Series 60 devices is drive “C”, the optional MultiMediaCard has the drive letter “E”.
OK, our application will create a new file when it’s running. It doesn’t have to be
© 2005 Mopius Page 39/48
Symbian OS – Workshop
© 2005 Mopius Page 40/48
installed, but has to be removed when the game is uninstalled. How does it do that?
The following line does what we need:
""-"!:\system\apps\Mopoid\settings.dat",FN
FN means “FileNull”. The SDK help has a nice definition of what this line means:
A file which does not yet exist, so is not included in the sis file. It is created by the
running application and will be deleted when the application is removed. The name
assigned to the source file is unimportant and should be empty, i.e. “”. Note that such
files will not be deleted when upgrading to a later version. This ensures that such files as
.ini files, which store application preferences, are not lost in an upgrade.
Building from the command line
Unfortunately, C++BuilderX doesn’t care about the package file it has exported.
Therefore, from now on you have to make the installation file (.sis) from the command
line. First, make sure that your IDE is still set to compile for ARMI / UREL (blue
gearwheels). Compile the project using “Project” -> “Make Project ‘Mopoid.cbx’”.
Now, open a command window (“Start” -> “Execute” -> type in “cmd”) and switch to
the “C:\Symbian\dev\Mopoid\group” folder. To make this faster, maybe you should
install the “Command Window Here” PowerToy from Microsoft. Enter the following
command:
makesis Mopoid.pkg
This will assemble the .sis file using the information from your own package file, and a
“Mopoid.sis” file should now be available in the same directory. Try it on your mobile
phone! It makes sense to write a small batch file that contains the command above, so
that you don’t have to type it in every time you want to build for the device.
When you’re finished, don’t forget to switch back to compile for the windows debug
emulator again (WINS / UDEB)!
Step 14 - Setting the application icon
Some information, like the application icon and caption, are stored in an .AIF file
(Application Information File). In this step, we will create this for our project.
Symbian OS – Workshop
Go to “File” -> “New...” . Choose the category “Mobile C++”, select the “New
Symbian AIF Wizard” and click on OK. Accept the default values of the page showing
Step 1. Step two is more interesting, now you will add the icons that will appear in the
menu of your mobile phone.
Fig. 30 – Adding icon files to the game.
Add the bitmaps from the “aif” directory of the project in the order shown above –
always the icon bitmap file first, then its mask. Don’t forget to click the “Add” button
after selecting a file!
When you’re done with adding all four files, go to step three. Now you will add the title
of the game. As Mopoid only supports English, simply enter the caption “Mopoid” and
leave the language at “ELangEnglish”. Click on “Add”.
Fig. 31 – The English caption is now defined.
© 2005 Mopius Page 41/48
Symbian OS – Workshop
Fig. 32 – Opening the resource file to fix a C++BuilderX bug.
After clicking on “Finish”, everything should normally be finished. However, there is
some kind of strange bug in the current version of C++BuilderX, because you won’t
see any icons if you compile the game now. To fix this, double click on “Mopoid.rss”
(found in the “Aif” folder) in the project pane to open it.
Find the line containing num_icons and change its value from 4 to:
num_icons = 2;
We only added two icons in different sizes, and their respective masks. C++BuilderX
should actually know those requirements, but apparently it doesn’t.
Step 15 - Handling being in the background
Symbian OS is a multitasking operating system. While your application is running,
many other events could send your application to the background or draw an alert on
top of it. Examples: an incoming call or message, a low battery warning, the user
sending your application to the background by pressing the red button, etc.
Fig. 33 – The game should be paused automatically
when it gets sent to the background.
© 2005 Mopius Page 42/48
Symbian OS – Workshop
© 2005 Mopius Page 43/48
Even for a small game like Mopoid it’s very important to handle this event. The game
has to be paused so that it doesn’t continue while the user is unable to play. Also, it
should use as few system resources as possible by stopping the continuous screen
updating process. It’s also a good idea to save the game progress.
Symbian OS will send an event to your application when it gets sent to the background.
The function “HandleForegroundEventL()” of the active view is called.
C++BuilderX didn’t implement it when it created the code, so we’re going to do it now.
Open “MopoidView.cpp” and go to its header file. Note that the CMopoidView class is
derived from CAknView, which already defines HandleForegroundEventL(). As we
will implement the function, add the following definition at the end of the public
function definitions:
void HandleForegroundEventL(TBool aForeground);
The parameter aForeground specifies whether our application was sent to the
background, or if it gained the focus again. Switch back to “MopoidView.cpp” and add
the following function at the bottom of the file:
// Handle any change of focus
void CMopoidView::HandleForegroundEventL(TBool aForeground)
{
if (aForeground) // gained focus
{
// Don't resume the game - wait for user to resume it
iContainer->iGameEngine->iHaveFocus = ETrue;
}
else // lost focus
{
// Pause game
if (iContainer)
{
iContainer->iGameEngine->PauseGame();
iContainer->iGameEngine->iHaveFocus = EFalse;
}
}
// call base class event handler
CAknView::HandleForegroundEventL(aForeground);
}
Symbian OS – Workshop
© 2005 Mopius Page 44/48
As you can see, depending on whether the application got or lost the focus, a status
variable of the game engine is modified accordingly. Note that it’s not good to resume
the game right when the application gets the focus again – it’s better to let the user start
the game when he is ready.
Our own implementation overrides the base implementation, which also has to be
called at the end of our function.
Step 16 - Periodic Events
The update of the game itself is done using an “Active Object”, the corresponding class
can be found in the “UpdateAO.cpp” file. This means that the game doesn’t have a
fixed frame rate and measures the time between the frames to find out how much the
ball and the panel have moved since the last frame.
This means that all values are calculated internally using floating point numbers.
Unfortunately, the processors in mobile phones don’t support floating point values, so
all this is emulated in software, which makes those calculations much slower than their
integer counterparts.
For many games it would be sufficient to use a fixed frame rate, with periodic callbacks
from a timer. The movements could then be fixed integer numbers, without the need to
do those more accurate calculations.
Keeping the backlight on
Mopoid still uses a fixed timer (which is actually also an active object that’s provided by
Symbian OS). It’s used to decrease the score of the player every five seconds, to make
the game more challenging. Another issue is that the phones switch off their backlight
if no key has been pressed for some time. This is bad when the user is still playing, and
just waiting for the ball to come down again. Therefore, we have to reset the user
inactivity timer of the phone every few seconds. This is done using the following call,
which you should add to the timer callback function (DoCallBack()) of the game
engine class:
User::ResetInactivityTime();
Symbian OS – Workshop
© 2005 Mopius Page 45/48
Using a timer
What we have to do now is to activate the timer, so that this function gets called at all.
Scroll up a bit and you’ll find the “StartTimerL()” function of the game engine class.
The timer object has already been declared as a private member variable of the game
engine class:
CPeriodic* iPeriodicTimer;
Therefore, in the function mentioned above, we have to create the timer object and
start it:
iPeriodicTimer = CPeriodic::NewL(CActive::EPriorityLow);
iPeriodicTimer->Start(KTickInterval, KTickInterval, TCallBack(TimerCallBack, this));
Note that we use the static NewL() function of the CPeriodic object that Symbian OS
provides. We do this because iPeriodicTimer is a member variable, therefore the
object isn’t pushed onto the cleanup stack. If anything serious fails, the object will be
deleted by the destructor of the game engine.
In the next line we start the timer and tell it to do callbacks every 5 seconds
(KTickInterval is set to 5000000). The last parameter specifies the callback function
that the timer will call every five seconds. Its name is “TimerCallBack()” and it is
part of the game engine class (referenced by this). This function is already
implemented by the example code.
Stopping the timer
When the game is paused or ends, the timer has to be stopped (and deleted). This is
quite simple. The following source code goes into the StopTimer() function.
if (iPeriodicTimer)
{
iPeriodicTimer->Cancel();
delete iPeriodicTimer;
iPeriodicTimer = NULL;
}
The cancel function of the timer is called to stop it. Then the object will be deleted and
set to NULL so that everyone knows that the timer is no longer here. If we need it again,
it will just be created again.
Symbian OS – Workshop
© 2005 Mopius Page 46/48
Step 17 - Final notes
The game is finished! If you wish, you can take a look at some other parts of the source
code that are not part of this tutorial. For example the sound playing routines are quite
interesting. The game also uses an external level file. You can edit it with a normal text
editor and quickly create your own levels. This file is read and parsed by the game.
About the author
This tutorial was written by Andreas Jakl. He is the founder of Mopius, a company
which creates innovative mobile products that let users experience what they have
never seen before.
The game “The Journey” is one of the world’s first mobile, location-based adventure
game. It was released as an open source game and has already been downloaded more
than 10,000 times. Its successor, “The Journey II”, extends the concept and creates a
huge and fascinating virtual world to be explored by the player. The two games have
received several awards, including “Most Innovative Game” (Mobile Fun Awards) and
a jury award of the “Austrian State Price”. The game has even been featured on TV.
Contact
To get more information or to get in contact with the author:
Mopius
Andreas Jakl
Widerinstr. 22
3100 St. Pölten
Austria / Europe
email: [email protected]
web: http://www.mopius.com/
tel.: +43 (0)676 / 722 82 78
Symbian OS – Workshop
© 2005 Mopius Page 47/48
Copyright
This tutorial is copyrighted by Andreas Jakl. You are free to use the source code of the
Mopoid game (licensed as GPL) provided as part of this tutorial in your own projects.
If you would like to use this tutorial (or parts of it) for own courses, please contact us.
You are not allowed to republish it without explicit written permission by the author.
Republish by Symbian with permission.
Symbian OS – Workshop
© 2005 Mopius Page 48/48
Step 18 - Exercises
If you want to do some tasks on your own, below are suggestions on how the game
could be improved:
Alternative key handling
Some Series 60 phones have a joystick that’s far from perfect. Those users might prefer
to control the panel using the number keys. Add an alternative key handling to allow
controlling the panel using the 4 (left) and 6 (right) keys. You can handle them in the
same switch case as for the joystick movements. Hint: handle the number keys by using
something like “case '4':”
Define more levels
The sample game comes with five levels – enough to try it out, but there could be
more. Take a look at how the levels are defined in the “levels.dat” file, find out
what kinds of bricks the numbers in the file define, how many lines make up one level.
Then add new levels and configure the game so that it knows about your additions.
Remember to execute “dobitmaps.bat” after you changed the “levels.dat” file, so
that it’s copied to the correct directory where the emulator will find it!
Saving the game progress
Especially when the game is longer, it’s important that the user can resume a game later
on. It should therefore be possible to save (at least) the current level and the score that
the player had when he entered the level. You’d have to save the score in a new
member variable.
To save this, extend the data file by a flag signaling whether a game is/was active, the
score the player had when he entered the level and the level he was playing.
When the player leaves the game and the game is active, set the flag to true and save the
game data. The next time the game is started, those values should be restored.