Programming Java 2 Micro Edition for Symbian OS 2004 (779882), страница 29
Текст из файла (страница 29)
By renderingMIDP 2.0137Figure 3.13 A Sprite image consisting of two frames.different frames in a sequence, a Sprite provides animation. Let usconsider a simple example. Figure 3.13 consists of two frames, each ofthe same width and height.By displaying the frames in a sequence, we can produce an animation.The following code shows how this can be achieved:import javax.microedition.lcdui.*;import javax.microedition.lcdui.game.*;import java.io.*;public class SpriteCanvas extends Canvas implements Runnable {private static final int SPRITE_WIDTH = 140;private static final int SPRITE_HEIGHT = 140;privateprivateprivateprivateSprite plane;int spritePositionX;int spritePositionY;boolean running = false;public SpriteCanvas(String imageName) throwsApplicationException {try {Image image = Image.createImage(imageName);plane = new Sprite(image, SPRITE_WIDTH, SPRITE_HEIGHT);spritePositionX = (getWidth() – SPRITE_WIDTH)/2;spritePositionY = (getHeight() – SPRITE_HEIGHT)/2;plane.setPosition(spritePositionX, spritePositionY);}catch(IOException ioe) {throw new ApplicationException("Unable to create image");}}public void paint(Graphics g) {g.setColor(255, 255, 255);g.fillRect(0, 0, getWidth(), getHeight());//paint background whiteplane.paint(g);}138MIDP 2.0 AND THE JTWIpublic void run() {running = true;while (running) {repaint();plane.nextFrame();try {Thread.sleep(50);} catch(InterruptedException ie){}}}public void stop() {running = false;}}A new Sprite is created using the following constructor:public Sprite(Image image, int frameWidth, int frameHeight)The first argument is the image consisting of the sequence of frames.The second and third arguments indicate the width and height in pixelsof the individual frames within the image.
Note that the width and heightof the image in pixels must be an integer multiple of the frame widthand height.The setPosition() method of Sprite is used to position theSprite in the coordinate space of the containing object, a Canvas inthis example.plane.setPosition(spritePositionX, spritePositionY);To render the Sprite, we call the paint() method. Here this isdone within the paint() method of SpriteCanvas:public void paint(Graphics g) {g.setColor(255, 255, 255);g.fillRect(0, 0, getWidth(), getHeight());plane.paint(g);}To animate the sprite we call the nextFrame() method of Spriteafter a repaint(), as shown below:while (running) {repaint();plane.nextFrame();...}MIDP 2.0Figure 3.14139PlaneSprite running on the Nokia 6600.Figure 3.14 illustrates the PlaneSprite MIDlet running on a Nokia6600. The full source code for the PlaneSprite MIDlet can bedownloaded from Symbian’s website at www.symbian.com/books.In addition to various transformations such as rotation and mirroring,the Sprite class also provides collision detection.
Collision detectionallows the developer to detect when the sprite collides with anotherelement. The following three methods are available in the Sprite class:public final boolean collidesWith(Image image, int x, int y,boolean pixelLevel)public final boolean collidesWith(Sprite s, boolean pixelLevel)public final boolean collidesWith(TiledLayer t, boolean pixelLevel)The first method detects collisions between the invoking Sprite andthe specified Image, while the second detects collisions with anotherSprite and the last method detects collisions with the specified TiledLayer.
If pixelLevel is set to true, collision detection is performedon a pixel by pixel basis; if it is false then collision detection is performed on the basis of the bounding rectangle of the sprite. The boundingrectangle of the sprite can be set using the method:public void defineCollisionRectangle(int x, int y, int width,int height)Otherwise, the default, bounding rectangle is located at (0,0) in thecoordinate space of the sprite and is of the same dimensions as the sprite.For more information on using sprites refer to the MIDP 2.0 documentation.140MIDP 2.0 AND THE JTWI3.3.7.5 Layer ManagerAs the name implies, the LayerManager manages a series of Layerobjects.
Sprite and TiledLayer both extend Layer. More specifically a LayerManager controls the rendering of Layer objects. TheLayerManager maintains an ordered list so that they are renderedaccording to their z-values (in standard computer graphics terminology).We add a Layer to the list using the method:public void append(Layer l)The first layer appended has index zero and the lowest z-value, that is,it appears closest to the user (viewer). Subsequent layers have successivelygreater z-values and indices. Alternatively we can add a layer at a specificindex using the method:public void insert(Layer l, int index)To remove a layer from the list we use the method:public void remove(Layer l)We position a layer in the LayerManager’s coordinate system usingthe setPosition() method. The contents of LayerManager are notrendered in their entirety, instead a view window is rendered using thepaint() method of the LayerManager:public void paint(Graphics g, int x, int y)The x and y arguments are used to position the view window onthe Displayable object the Canvas or GameCanvas upon which theLayerManager is ultimately rendered and therefore the device’s screen.The size of the view window is set using the method:public void setViewWindow(int x, int y, int width, int height)The x and y values determine the position of the top left corner ofthe rectangular view window in the coordinate system of the LayerManager.
The width and height arguments determine the width andheight of the view window and are usually set to a size appropriate for thedevice’s screen. By varying the x and y coordinates we can pan throughthe contents of the LayerManager.We shall illustrate using LayerManager to display our plane spriteagainst a simple moving background. The image used to make up theMIDP 2.0141Figure 3.15 The background image for the LayerManager Demo MIDlet.background TiledLayer is shown in Figure 3.15 and consists of justone tile.The source code for the LayerManagerCanvas class is listed below.importimportimportpublicjavax.microedition.lcdui.game.*;javax.microedition.lcdui.*;java.io.*;class LayerManagerCanvas extends Canvas implements Runnable {privateprivateprivateprivateprivateprivatestaticstaticstaticstaticstaticstaticfinalfinalfinalfinalfinalfinalintintintintintintTILE_WIDTH = 140;TILE_HEIGHT = 140;SPRITE_WIDTH = 140;SPRITE_HEIGHT = 140;WINDOW_WIDTH = 140;WINDOW_HEIGHT = 140;private Sprite sprite;private TiledLayer backgroundLayer;private LayerManager layerManager;private boolean running = false;private int x;private int y;public LayerManagerCanvas(String spriteImageName,String backgroundImageName) throws ApplicationException {try {Image spriteImage = Image.createImage(spriteImageName);Image backgroundImage =Image.createImage(backgroundImageName);sprite = new Sprite(spriteImage, SPRITE_WIDTH,SPRITE_HEIGHT);backgroundLayer = new TiledLayer(2, 1, backgroundImage,TILE_WIDTH, TILE_HEIGHT);backgroundLayer.setCell(0,0,1);backgroundLayer.setCell(1,0,1);layerManager = new LayerManager();layerManager.append(sprite);layerManager.append(backgroundLayer);//set layer position relative to LayerManager origin// this is the default anywaysprite.setPosition(0, 0);142MIDP 2.0 AND THE JTWI//this is the default anywaybackgroundLayer.setPosition(0, 0);//set view window size and position relative to//LayerManager's originlayerManager.setViewWindow(0, 0, WINDOW_WIDTH,WINDOW_HEIGHT);//calculate coordinates to position view window in Canvasx = (getWidth() - WINDOW_WIDTH)/2;y = (getHeight() - WINDOW_HEIGHT)/2;}catch(IOException ioe) {throw new ApplicationException("Unable to create image");}}public void paint(Graphics g) {g.setColor(255, 255, 255);//paint Canvas background whiteg.fillRect(0, 0, getWidth(), getHeight());//position view window in Canvas and renderlayerManager.paint(g, x, y);}public void run() {running = true;int layerX = -TILE_WIDTH;while ( running) {if (layerX == 0) {layerX = -TILE_WIDTH;}backgroundLayer.setPosition(layerX, 0);sprite.nextFrame();repaint(x, y, TILE_WIDTH, TILE_HEIGHT);try {Thread.sleep(30);}catch(InterruptedException ie) {}layerX++;}}public void stop() {running = false;}}The class constructor first creates the Sprite and a TiledLayer forthe background, as shown below.Image spriteImage = Image.createImage(spriteImageName);Image backgroundImage = Image.createImage(backgroundImageName);sprite = new Sprite(spriteImage, SPRITE_WIDTH, SPRITE_HEIGHT);backgroundLayer = new TiledLayer(2, 1, backgroundImage,TILE_HEIGHT);MIDP 2.0Figure 3.16143The background TiledLayer.backgroundLayer.setCell(0,0,1);backgroundLayer.setCell(1,0,1);Our background TiledLayer consists of a 2 × 1 grid of tiles (seeFigure 3.16) sufficient to simulate an infinite scene.Next a LayerManager is created and the sprite and backgroundlayers are appended:layerManager = new LayerManager();layerManager.append(sprite);layerManager.append(backgroundLayer);//this is the default anywaysprite.setPosition(0, 0);//this is the default anywaybackgroundLayer.setPosition(0, 0);layerManager.setViewWindow(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);x = (getWidth() – WINDOW_WIDTH)/2;y = (getHeight() – WINDOW_HEIGHT)/2;The layers are positioned relative to the LayerManager’s coordinatesystem using their setPosition() methods.The LayerManager’s view window is rendered by the paint()method of Canvas as follows:public void paint(Graphics g) {g.setColor(255, 255, 255);//paint Canvas background whiteg.fillRect(0, 0, getWidth(), getHeight());//position view window in Canvas and renderlayerManager.paint(g, x, y);}The animation of the MIDlet is handled in the run() method listedbelow.public void run() {running = true;int layerX = -TILE_WIDTH;144MIDP 2.0 AND THE JTWIwhile ( running) {if (layerX == 0) {layerX = -TILE_WIDTH;}backgroundLayer.setPosition(layerX, 0);sprite.nextFrame();repaint(x, y, TILE_WIDTH, TILE_HEIGHT);try {Thread.sleep(30);}catch(InterruptedException ie) {}layerX++;}}In this example, to generate the illusion of motion, instead of movingthe sprite, we move the background layer relative to the coordinatesystem of the Canvas using the setPosition() method.
The planesprite remains stationary while alternating between its two frames on eachcycle. The background layer is initially offset in a negative direction byan amount equal to TILE_WIDTH. On each cycle we move its positionby one pixel in the positive direction until it has shifted by a total amountequal to TILE_WIDTH. We then reset the position of the backgroundlayer back to its initial position to avoid running out of scenery.