IT tutorials
 
Mobile
 

Java ME on Symbian OS : Building a Simple MIDP Game

11/26/2011 5:24:50 PM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
The point to be demonstrated here is that the MIDP Game API was developed for the feature phone market. In this market, there are limits on heap size, thread pools, JAR size, few or no optional JSRs and minimal multimedia capabilities. Given all of that it is amazing what can be done in a very short space of time. So let's hook straight in with a simple game that we can throw away just as a quick demonstration of how much the MIDP 2.0 Game API does for you.

Figure 1 shows a screen shot for a demo game, called GhostPong in honor of the original arcade game Pong.It's less than 150 lines in length and took just over an hour to write (it should have taken less but I kept making stupid typing mistakes...).

The point of the game is that basically the little ghost sprites fall from the top of the screen and you have to smash them out of existence with the brick paddle at the bottom which moves left and right. You get 10 points for each one you get and you lose 10 points for each ghost you miss. And as for the name – well there's no excuse for that.

What this shows is that you don't need complex architectures and reusable libraries in order to build effective games. With MIDP 2.0 and the Game API you can jump straight in and just get on with it. The game is only 6 KB in size and has no external requirements other than a dependency on MIDP 2.0. This means that it would quite possibly run today on almost a billion mobile phones.

Figure 1. GhostPong

The code below shows the game canvas – for the full application please refer to the code on the book's website.

1 public class GhostPongCanvas extends GameCanvas implements Runnable{
2
3 GhostPongMIDlet midlet;
4 boolean running;
5 final Graphics graphics;
6 int pongX, pongY, pongWidth, pongHeight;
7 Image ghostImage;
8 Sprite pong;
9 LayerManager layerManager;
10 Random random = new Random();
11 long startTime, lastGenTime;
12 int points = 0;
13 Font font;
14 Player tonePlayer;
15 ToneControl toneControl;
16 byte G = (byte)(ToneControl.C4 + 7);
17 byte[] soundSeq = {
ToneControl.VERSION,1,ToneControl.TEMPO,127,G,8,G,8};
18
19 public GhostPongCanvas(GhostPongMIDlet midlet) throws Exception{
20 super(true);
21 setFullScreenMode(true);
22 this.midlet = midlet;
23 graphics = getGraphics();
24 font = Font.getFont(Font.FACE_PROPORTIONAL,
Font.STYLE_BOLD,...SIZE_LARGE);
25 ghostImage = Image.createImage("/ghost.png");

26    pong = new Sprite(Image.createImage("/pong.png"));
27 pongWidth = pong.getWidth();
28 pongHeight = pong.getHeight();
29 pongX = (getWidth() - pongWidth) >> 1;
30 pongY = getHeight() - 20 - pongHeight;
31 pong.setPosition(pongX, pongY);
32 tonePlayer = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
33 tonePlayer.realize();
34 toneControl = (ToneControl) tonePlayer.getControl("ToneControl");
35 toneControl.setSequence(soundSeq);
36 layerManager = new LayerManager();
37 layerManager.append(pong);
38 }
39
40 public void start(){
41 Thread thread = new Thread(this);
42 thread.start();
43 running = true;
44 startTime = System.currentTimeMillis();
45 lastGenTime = startTime;
46 }
47
48 public void run(){ // the game loop
49 while(running){
50 try{
51 generateGhosts();
52 movePong();
53 moveGhosts();
54 draw();
55 flushGraphics();
56 Thread.sleep(50);
57 }
58 catch(Exception e){}
59 }
60 }
61
62 private void draw(){
63 graphics.setColor(0x000000);
64 graphics.fillRect(0, 0, getWidth(), getHeight());
65 graphics.setColor(0xff0000);
66 graphics.setFont(font);
67 graphics.drawString("" + points, getWidth()>>1, 10,
Graphics.HCENTER | Graphics.TOP);
68 layerManager.paint(graphics, 0, 0);
69 }
70
71 // every second there's an 80% chance of generating new ghosts
72 private void generateGhosts(){
73 long elapsed = System.currentTimeMillis() - lastGenTime;
74 if(elapsed > 1000L){
75 int i = genRandom(1, 10);
76 if(i <= 8){
77 int numGhosts = genRandom(1, 4);
78 for(int g = 0; g < numGhosts; g++){
79 Sprite ghost = new Sprite(ghostImage);
80 int ghostX = genRandom(0, getWidth() - ghost.getWidth());
81 ghost.setPosition(ghostX, 0);


82          if(i < 4) // occasionally flip the sprite for variety
83 ghost.setTransform(Sprite.TRANS_MIRROR);
84 layerManager.append(ghost);
85 }
86 }
87 lastGenTime = System.currentTimeMillis();
88 }
89 }
90
91 private void moveGhosts(){
92 int fallSpeed = 10;
93 int numGhosts = layerManager.getSize() − 1; // exclude the pong
94 for(int i = numGhosts; i >= 1; i--){
95 Sprite ghost = (Sprite) layerManager.getLayerAt(i);
96 ghost.move(0, fallSpeed);
97 if(ghost.collidesWith(pong, true)){
98 points += 10;
99 layerManager.remove(ghost);
100 try {
101 tonePlayer.start();
102 }
103 catch(Exception e){
104 e.printStackTrace();
105 }
106 }
107 else if(ghost.getY() > this.getHeight()){
108 points -= 10;
109 layerManager.remove(ghost);
110 }
111 }
112 }
113
114 private void movePong(){
115 int keyState = getKeyStates();
116 int dx = 15;
117 if( (keyState & GameCanvas.LEFT_PRESSED) != 0){
118 pongX -= dx;
119 if(pongX < 0) pongX = 0;
120 }
121 else if( (keyState & GameCanvas.RIGHT_PRESSED) != 0){
122 pongX += dx;
123 if(pongX > (getWidth() - pongWidth))
124 pongX = getWidth() - pongWidth;
125 }
126 pong.setPosition(pongX, pongY);
127 }
128
129 // shut down on right softkey (S60 & UIQ)
130 public void keyPressed(int keyCode){
131 if(keyCode == −7 || keyCode == −20){
132 midlet.die();
133 }
134 }
135
136 public int genRandom(int min, int max){
137 return (Math.abs(random.nextInt()) % max) + min;
138 }
139 }


Now we are going to have a quick look at what we have managed to cover in under 150 lines of Java ME code.

1. What Has It Got?

No argument – this game won't be on any best-seller list but let's see some of the things this simple game demonstrates:

  • Loading and displaying an image from a JAR file (lines 25 and 26)

  • Creating a Sprite instance from an image file (line 26)

  • A basic game loop using a thread (lines 48–60)

  • Using a full screen GameCanvas (lines 20 and 21)

  • Examining key states to respond to user input (lines 115, 117 and 121)

  • Moving and rotating Sprites (lines 79–83)

  • A simple way to generate random numbers over a fixed interval (lines 136–138)

  • Basic layer management using the LayerManager class (lines 95, 99–109)

  • Basic MIDP 2.0 Media API tone sequences (lines 14–17, 32–35 and 100–105)

  • Responding to softkey events (lines 130–134)

  • Collision detection between Sprites (line 97).

It has to be said that, once you get used to the Game API, you will see that almost all of these features came for free. The bulk of this game is made up of simple code snippets that glue together pre-packaged MIDP 2.0 game functionality.

2. What Has It Not?

At first glance it isn't too bad for a first pass. However there are a number of things that this game lacks. Some of the missing features can be classed as 'nice to have' but quite a few are core and represent common errors made in mobile game development.

Obviously it doesn't have menus, a splash screen, score tracking or advanced media. It doesn't use any particular architecture, would be hard to maintain and is a little casual with its memory management. Here are some other problems:

  • It does not handle pausing of the game in any way.

  • It has no handle to the game thread so this cannot be explicitly managed.

  • The flag variable running is set after the call to Thread.start() – so if the game thread starts and is immediately pre-empted by another thread before line 43 executes, the application is left in an inconsistent state.

  • Access to the flag variable running is not synchronized across the GUI and game loop threads (e.g., by using the Java keyword volatile in its declaration).

  • It creates a large number of new object instances (Sprites in this case) instead of re-using a pool of them. The amount of heap in use at any time depends completely on how often the garbage collector runs.

  • No effort is made to manage the frame rate. The thread simply sleeps for 50 ms each time the game loop executes. Changing the sleep period speeds up or slows down the movement of the ghost sprites.

  • There is no way to turn off the tone sequence used for sounds. This is very annoying.

  • There is no concept of 'game over' – it just runs forever with no challenge.

  • It uses magic numbers (line 131) for special key codes instead of defining them as constants. Unfortunately, you also can't use the Canvas.getGameAction() method to handle softkey events because these are not part of the standard MIDP key mappings and are specific to each manufacturer.

3. Debrief Summary

So far we have discovered two important concepts regarding game development with Java ME:

  • It is really easy to write Java games.

  • It is really easy to write Java games badly.

This is not all that surprising. The vast majority of mobile phones in the mass market at the time MIDP 2.0 was designed had a number of built-in limitations. So MIDlets did not have much memory, little local storage space and were rarely paused. Few mobile-phone operating systems are multitasking and MIDlets that lose focus are often terminated outright. A direct consequence is that there is a large code base of Java ME sample code in existence that takes little or no heed of the pause–resume cycle. It is common to see (as above) a start() method on the GameCanvas that is called whenever startApp() is called. In almost all cases, this starts a new game thread without checking for an existing game thread or storing a reference to the game thread at class scope so that it can be shut down in a well-defined manner.

Always keep a class-scoped reference to the game thread to control its behavior.


To help clarify these issues, try the following exercises:

  1. Use the Sun WTK (or whatever IDE you prefer) to start the GhostPong MIDlet and let it run for a few minutes. You can quickly see the degradation in performance as more and more Sprite instances are created. Even though all references to these are removed, there are simply too many allocated too quickly for the garbage collection thread to keep up.

  2. Install the MIDlet on a Symbian OS phone (by cable, Bluetooth transfer or PC Suite software). Start the game and then move it into the background. Assuming your phone is not in silent mode, you can hear the game continuing to execute in the background, happily consuming battery power, CPU cycles, RAM and delivering a bizarre user experience. Moving the MIDlet back into the foreground in fact triggers a new game thread instance and very quickly you have a process with a number of unmanaged threads.

Where possible, try to reuse objects by using a managed pool of instances.


Symbian OS is a fully multitasking operating system which means that Java ME MIDlets need to be designed to acquire and release resources as they are moved in and out of the foreground.

 
Others
 
- jQuery 1.3 : Compound effects & Creating custom animations
- jQuery 1.3 : Effects and speed
- Mobile Web Apps : Quick Wins (part 2) - Form Field Attributes
- Mobile Web Apps : Quick Wins (part 1) - Nifty Links
- iPhone Developer : Using Creative Popping Options
- iPhone Developer : Navigating Between View Controllers
- iOS SDK : Basic SQLite Database Manipulation (part 3) - SQLite Binding, Inserting, Updating, and Deleting
- iOS SDK : Basic SQLite Database Manipulation (part 2) - Select
- iOS SDK : Basic SQLite Database Manipulation (part 1) - Opening the Database, Statements, Preparing Statements, and Executing Statements
- The Anatomy of a Mobile Site : PRIMARY SITE CONTENT (part 3) - Forms
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
Technology FAQ
- Is possible to just to use a wireless router to extend wireless access to wireless access points?
- Ruby - Insert Struct to MySql
- how to find my Symantec pcAnywhere serial number
- About direct X / Open GL issue
- How to determine eclipse version?
- What SAN cert Exchange 2010 for UM, OA?
- How do I populate a SQL Express table from Excel file?
- code for express check out with Paypal.
- Problem with Templated User Control
- ShellExecute SW_HIDE
programming4us programming4us