Programming Java 2 Micro Edition for Symbian OS 2004 (779882), страница 66
Текст из файла (страница 66)
We could have subclassed this class and saved ourselvessome code, though this approach is less flexible as it becomes harder tochange the two classes independently:public void move(){int absXDelta = Math.abs(xDelta);xPosOld = xPos;xPos += xDelta;if((xPos <= 0) || (xPos > xMax)) xDelta = -xDelta;repaint(xPos - absXDelta, Y_POS, PUCK_SIZE + 2*absXDelta,PUCK_SIZE);serviceRepaints();}}7.13.1.4 Comparison of the Painting StrategiesHaving gone through the details of the Popcorn MIDlet, it is time to lookat the results:Full repaintClip regionBufferedWTK nobufferWTKbuffered76506600P900(fullscreen)P900(withkeypad)36.51 s35.24 s0.90 s25.70 s22.74 s0.80 s38.97 s16.58 s2.88 s23.81 s8.06 s4.56 s30.74 s12.52 s4.53 s21.67 s9.95 s4.56 sWe have deliberately chosen a complex background that takes a longtime to draw.
On Symbian OS, we can see the benefit of clipping: theless background we have to draw, the faster we can redraw successivescreens. Using off-screen images together with a clipping region furtherimproves performance. Indeed, with complex background scenes, thisis the only option. The downside is the memory required to store thebackground image.GRAPHICS365The very fast buffered performance of the Nokia 7650 compared to theNokia 6600 or Sony Ericsson P900 is at least in part because the 7650 hasonly 4096 colors, whilst the other devices have 65 536 colors. Withoutbuffering on the WTK, our first two options flickered badly.7.13.1.5 Using GameCanvas in MIDP 2.0The Popcorn MIDlet was written for MIDP 1.0.
However, it is a simplematter to rewrite it for MIDP2.0 to take advantage of the GameCanvasclass. Only a few changes are needed to PlayingField, which wehave renamed PlayingFieldGameCanvas. We extend GameCanvas,not Canvas, and we need an instance variable for the Graphics object:public class PlayingFieldGameCanvas extends GameCanvasimplements Runnable, CommandListener {Graphics graphics;...Then we change move() so that it calls the paint() method directly,rather than via repaint() (we should really rename the paint()method to save confusion).
paint() updates the Graphics object,then we call flushGraphics() to render it to the screen:public void move(){xPosOld = xPos;xPos += xDelta;if((xPos <= 0) || (xPos > xMax)) xDelta = -xDelta;paint(graphics);flushGraphics();}Even though in this case we have not saved any code, the logic is nowsimpler. For instance, I could easily check when the puck hits the edge ofthe screen, perhaps to change its color or shape, or to add another itemto the Graphics object.However, whether using Canvas or GameCanvas, the implementation has to do broadly the same amount of work, so using GameCanvasmade no significant difference to the execution time.7.13.2Collision DetectionFigure 7.3 shows a rocket about to collide with an asteroid. How canwe tell if they do indeed collide? The MIDP 2.0 Sprite class provides366WRITING OPTIMIZED CODEFigure 7.3 Sprites overlapping, but not colliding.methods for converting an Image to a Sprite and for detecting whethertwo sprites are in collision.In this case the bounding boxes for the sprites overlap.
However, theimages have not actually collided.boolean Sprite.collidesWith(Sprite s, boolean pixelLevel) returns true if the sprites overlap. If pixelLevel is false,only the bounding boxes are checked. This is fast, but not accurate. IfpixelLevel is true, first the bounding boxes are checked, then if theseoverlap, the method checks to see if any non-transparent pixels overlap.There is no need for you to do a quick check with pixelLevel set tofalse followed, if necessary, by an accurate check with pixelLeveltrue; just use collidesWith() with pixelLevel set to true. If youneed to check collisions between lots of simple sprites, pixelLevelshould probably be false; if you need to check collisions between a fewcomplex objects, pixelLevel should probably be true.
On the otherhand, if you have lots of complex objects things can get very complicated.The number of potential collisions increases with the square of thenumber of sprites (n sprites means n(n-1)/2 possible collisions). A way tocut this down is to divide the screen into tiles. For each tile you only needto go through all the sprites that are completely enclosed by the tile, orthat cross the boundary of the tile.7.14LifeTime Case StudyThe LifeTime MIDlet is an implementation of Conway’s Game of Life.The action takes place on an unlimited field of squares, or cells.LIFETIME CASE STUDY367From generation to generation, cells live or die according to threesimple rules:• a cell is created (from nowhere!) if it has 3 neighbors• a cell stays alive if it has 2 or 3 neighbors• otherwise a cell dies (of overcrowding or loneliness!) or remainsempty.Near the bottom of Figure 7.4, you can see a block of four cells,a column of three cells and a block of two cells.
You might like toconvince yourself that in the next generation, using the rules above,the block of four cells remains unchanged because each cell has threeneighbors, the column of three changes to a row of three, and the blockof two disappears. If you want to know more, The Recursive Universe byPoundstone gives an excellent insight into the Game of Life as well asbeing a thoroughly good read.Probably the most famous pattern is the r Pentomino. This starts asin Figure 7.5 but explodes into the most amazing patterns.
Figure 7.4 isa detail from this pattern after some 70 generations. After 1103 generations, it stabilizes into a predictable, dynamic pattern. Figure 7.6shows the pattern after 1103 generations at the four zoom levelsoffered by the program. In the first we see the entire pattern, exceptfor four ”gliders” which have traveled beyond the screen and will continue to move out indefinitely. Each cell is represented by a single pixel.At the next zoom level, each cell is represented by a 2 × 2 block ofFigure 7.4Detail of LifeTime.368WRITING OPTIMIZED CODEFigure 7.5 Start of r Pentomino.Figure 7.6Views of the LifeTime population at four different zooms.pixels, then by a 3 × 3 block, then by a 5 × 5 block at the greatestzoom level.Figure 7.7 shows the UML class diagram for the LifeTime MIDlet. Theclasses of particular interest to us are:• LifeCanvas: renders the state of the game to the screen and isresponsible for housekeeping functions such as loading and savinggames, pan and zoom, and editing functions• LifeEngine: holds the algorithm that interprets Conway’s rules toconstruct a new generation from the old generation• GenerationMap: defines an interface for storing and accessing thedata for each generation.LIFETIME CASE STUDYMIDletLifeTimedisplay:Display+LifeTime()~startApp()~pauseApp()~destoryApp()369GameCanvascanvasLifeCanvas~paint(Graphics)1 (plus UI andrecord storestuff)LifeEngine~thisGeneration:GenerationMap~lastGeneration:GenerationMap11 -testedCells:GenerationMap+run()-createNewCell(Cell)CommandListenercommandAction(Command, Displayable)RunnableOutputTextBoxTextBoxLoadDeleteListListSaveGameFormFormSetupFormLinkedListGMGenerationMapcreate(cell)kill(cell)clear()isAlive(cell):booleangetCount():intgetNeighbourCount(cell):intgetEnumeration():EnumerationHashGMVectorGMBinaryTreeGMTwoDByteArrayCursorCellCell(x, y)Cell(position: int)getX():intgetY():intequals(obj): booleanhashcode():intFigure 7.7 UML class diagram for the LifeTime MIDlet.Four different implementations were tested (the TwoDByteArray wasnot a strict implementation of the interface).
In general the location of acell was stored as one 32-bit integer value, with the top 16 bits holdingthe y-coordinate and the bottom 16 bits holding the x-coordinate. Thismeant that the field was limited to 64 K by 64 K, but still much biggerthan any screen!Design choices are driven by the requirements of the game itself,in particular:• an unlimited playing field; this means that a fixed-size two-dimensionalarray is not a real option• maximizing performance• minimizing memory requirements.All three classes have to be optimized. LifeCanvas needs to paintas fast as possible, not just during a run but also at different zooms,while panning and during editing. The LifeEngine algorithm needsto be as quick as possible, at the same time ensuring that the codeis straightforward and understandable (a search on the Internet willreveal some very complex, and very fast, Game of Life algorithms). The370WRITING OPTIMIZED CODEGenerationMap implementation needs to provide fast access to thegame within a reasonable footprint.Various auxiliary classes manage loading, saving and deleting games,displaying status information and setting up parameters.