1 / 41

Cosc 4755

Cosc 4755. . lcdui.game package. Game package. Introduced in MIDP 2.0 Provides 5 classes to help develop games GameCanvas , which extends Canvas Layer Super class for sprite and TiledLayer Abstract class to provide visual element in a game Sprite

marisa
Download Presentation

Cosc 4755

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Cosc 4755 .lcdui.game package

  2. Game package • Introduced in MIDP 2.0 • Provides 5 classes to help develop games • GameCanvas, which extends Canvas • Layer • Super class forsprite and TiledLayer • Abstract class to provide visual element in a game • Sprite • An Image or collection of images for animation • TiledLayer • Used to great an image from a few smaller images • LayerManager • Used to manage multiple layers and render each layer in the correct order.

  3. A short game design primer. • A game or animation is built on an animation loop. • Instance variables of “objects” are updated • User interaction may change “objects” • Depends on game state (Is user playing, demo mode, etc). • Code then draws/paints/repaints the game screen • Code loop sleeps for a short period of time, which controls the game rate.

  4. Basic outline of a game • MIDlet, which declares an object. • It’s an extension of GameCanvas • And implements Runnable • A thread. • Likely has an exit command too. public class GameMIDlet extends MIDlet { MyGameCanvasgCanvas; Display display; public GameMIDlet() { gCanvas = new MyGameCanvas(); gCanvas.addCommand(new Command("Exit", Command.EXIT, 0)); gCanvas.setCommandListener(new CommandListener() { public void commandAction(Command c, Displayable s) { gCanvas.stop(); notifyDestroyed(); } }); } public void startApp() { display = Display.getDisplay(this); gCanvas.start(); display.setCurrent(gCanvas); } public void pauseApp() {} public void destroyApp(boolean unconditional) { gCanvas.playgame = false; //end Thread. } }

  5. GameCanvas class • This is the part of the code responsible for the “animation loop” • Including get user input class MyGameCanvas extends GameCanvas implements Runnable{ public booleanplaygame; private myColor Color = new myColor(); public MyGameCanvas() { super(true); //required by GameCanvas } public void run() { Graphics g = getGraphics(); while (playgame) { // Verify game state, “move” objects, etc… checkGameState(); // get user input checkUserInput(); // update screen render(g); //control refresh rate try { Thread.sleep(30); //sleep 30 Milliseconds } catch (Exception e) {} } }

  6. GameCanvas class (2) • Methods to do the work. public void render(Graphics g) { //draw in off screen buffer //draw white background g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); //this call paints off screen buffer to the screen flushGraphics(); } public void start() { //initialization playgame = true; // start animation loop thread Thread t = new Thread(this); t.start(); //calls run method } public void stop() { playgame = false; } public void checkGameState(){ } public void checkUserInput() { } }

  7. GameCanvas Detail • GameCanvas is subclass of Canvas • So all those methods are available • Provides double buffering to produce smooth transitions of objects moving on the screen. • We don’t write a paint method, instead we create a “render” or updateScreen method to draw on the off screen buffer. • flushGraphics() method moves the off screen buffer to the screen buffer • Which is why we don’t create a paint method.

  8. GameCanvas Detail (2) • Provides a method to get user input • getKeyStates() • Returns a bitwise variable of all game keys pressed since it was last called. • Based on keys defined by Canvas • 1 pressed, 0 not pressed. • DOWN_PRESSED, UP_PRESSED, RIGHT_PRESSED, LEFT_PRESSED, FIRE_PRESSED, GAME_A_PRESSED, GAME_B_PRESSED, GAME_C_PRESSED, and GAME_D_PRESSED

  9. getKeysStats() example intkeyState = getKeyStates(); if ((keyState & LEFT_PRESSED) != 0) { //left key pressed } else if ((keyState & RIGHT_PRESSED) != 0) { // right key pressed } • User pressed two keys at once? if ( ( (keyState & LEFT_PRESSED) !=0) && ((keyState & UP_PRESSED) !=0) ) { //User pressed both and left and up key. }

  10. Starting our game. • Add game constants and draw the board private myShip ship; //game images Image iShip, iShot, iAlien; //Game characteristics constants public static final int GAME_WIDTH = 192; public static final int GAME_HEIGHT = 192; //center the game on the screen. public final int GAME_ORIGIN_X = (getWidth() - GAME_WIDTH)/2; public final int GAME_ORIGIN_Y = (getHeight() - GAME_HEIGHT)/2; public final int WATER_BOUNDARY_LEFT = GAME_ORIGIN_X + 32; public final int WATER_BOUNDARY_RIGHT = GAME_ORIGIN_X + GAME_WIDTH - 32; public void render(Graphics g) { //draw in off screen buffer //draw white background g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(Color.BLACK); drawBackground(g); g.drawImage(iShip, ship.x, ship.y, 0); //this call paints off screen buffer to the screen flushGraphics(); } public void drawBackground(Graphics g){ g.drawRect(GAME_ORIGIN_X, GAME_ORIGIN_Y, GAME_WIDTH, GAME_HEIGHT); g.drawLine(WATER_BOUNDARY_LEFT, GAME_ORIGIN_Y, WATER_BOUNDARY_LEFT, GAME_ORIGIN_Y+GAME_HEIGHT); g.drawLine(WATER_BOUNDARY_RIGHT, GAME_ORIGIN_Y, WATER_BOUNDARY_RIGHT, GAME_ORIGIN_Y+GAME_HEIGHT); }

  11. Starting our game (2) • User movement public void checkUserInput() { // get the state of keys intkeyState = getKeyStates(); // determines which way to move and changes if((keyState & LEFT_PRESSED) != 0) { ship.left(); } else if((keyState & RIGHT_PRESSED) != 0) { ship.right(); } else if((keyState & UP_PRESSED) != 0) { ship.up(); } else if((keyState & DOWN_PRESSED) != 0) { ship.down(); } } • For the moment we ignore the ship class and come back to it later. • First though, we need some color on this board.

  12. Backgrounds with TiledLayer • TiledLayer always us to create backgrounds with small images, then tell it how to place them. • We can divide the board in 32x32 pixels, or 6x6 cells for 192x192 pixels. • And use only 2 small pictures. • Column 0 and 5 as first 32x32 • The middle 4 columns as the second 32x32 part.

  13. Code for TiledLayer public void createBackground() throws IOException { //get the background image backGroundimg = Image.createImage("/background1.png"); //build the tiledlayer object backGround = new TiledLayer(6,6,backGroundimg,32,32); // array that specifies what image goes where int[] cells = { 1, 2, 2, 2, 2, 1, //cliff, water, water, water, water, cliff 1, 2, 2, 2, 2, 1, //cliff, water, water, water, water, cliff 1, 2, 2, 2, 2, 1, //cliff, water, water, water, water, cliff 1, 2, 2, 2, 2, 1, //cliff, water, water, water, water, cliff 1, 2, 2, 2, 2, 1, //cliff, water, water, water, water, cliff 1, 2, 2, 2, 2, 1 //cliff, water, water, water, water, cliff }; // set the background with the images for (inti = 0; i < cells.length; i++) { int column = i % 6; int row = (i - column)/6; backGround.setCell(column, row, cells[i]); } // set the location of the background backGround.setPosition(GAME_ORIGIN_X, GAME_ORIGIN_Y); }

  14. Code for TiledLayer (2) • Add code to Start() public void start() { //initialization playgame = true; try { createBackground(); // start animation thread Thread t = new Thread(this); t.start(); } catch (IOException ex) { ex.printStackTrace(); } } • Add to drawBrackGround method public void drawBackground(Graphics g){ g.drawRect(GAME_ORIGIN_X, GAME_ORIGIN_Y, GAME_WIDTH, GAME_HEIGHT); g.drawLine(WATER_BOUNDARY_LEFT, GAME_ORIGIN_Y, WATER_BOUNDARY_LEFT, GAME_ORIGIN_Y+GAME_HEIGHT); g.drawLine(WATER_BOUNDARY_RIGHT, GAME_ORIGIN_Y, WATER_BOUNDARY_RIGHT, GAME_ORIGIN_Y+GAME_HEIGHT); //And paint in the tilelayer. backGround.paint(g); } • Except we do this easier with the layout manager class.

  15. LayoutManager • Both Sprite and TiledLayer classes extend the Layer • Instead of having to individual paint each layer, we add Sprites and TiledLayer to a LayoutManager in the correct order and then have it render the image for us. • We can add and remove items from a Layout manager, as well as access the items in the manager.

  16. LayoutManager • “Correct Order”? • Think of the layoutManager like a queue of items • Append(X), append(Y), then X will be draw “on top” of Y. • We can always append with an index, Where 0 is head of the queue. • Append(y), append(x,0). X will still be draw on of Y • Append(y), append(x,0), append(z,0), Draw z on top of x, which is drawn on top of y. • You want a background layer to be last!

  17. Sprite class • The Sprite class allows to number things • First it takes an Image and size of the image in the constructor • It then allows us to add us to add it to a Layer • Provides • CollidesWith methods • SetTransform method ( Mirror and rotate 90, 180, 270 degrees) • Animation, similar tiledLayerobjects.

  18. Change the code • Add the layermanagerand change ship to sprite. • We can now remove drawbackground() method public MyGameCanvas() { super(true); //required by GameCanvas //suppresses normal key events, for "key pressed, key released, key repeated" try { iShip = Image.createImage("/ship.png"); sShip = new Sprite(iShip,iShip.getWidth(),iShip.getHeight()); //iShot = Image.createImage("/shot.png"); } catch (IOExceptionio) { System.err.println(io.toString());} } public void start() { //initialization playgame = true; try { //create a layer manager manager = new LayerManager(); //and ship to it manager.append(sShip); //create and add the backgroud createBackground(); manager.append(backGround); // start animation thread Thread t = new Thread(this); t.start(); } catch (IOException ex) { ex.printStackTrace(); } }

  19. Change the code • Render function is very simple now. public void render(Graphics g) { //draw in off screen buffer //draw white background g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(Color.BLACK); manager.paint(g, 0, 0); //draw all the layers. //this call paints off screen buffer to the screen flushGraphics(); }

  20. Extending the sprite class • Since we are going to have the layermanager kept track of the aliens and shots, we going to make things a little simpler and extend the sprite class with info we need. • To extend the class, we’ll need to deal with the constructor we use. • Sprite(Image image, intframeWidth, intframeHeight) • Very useful for handling the aliens and firing.

  21. MyShip sprite class class myShip extends Sprite { intimageHeight, imageWidth; intboardx, boardy, boardh, boardw; myShip(Image i, intfw, intfh, intmx, int my, intbx, int by, intbw, intbh){ super(i,fw,fh); //call the sprite constructor //now the rest of our stuff setPosition(mx, my); imageHeight = i.getHeight(); imageWidth = i.getWidth(); boardx = bx; boardy = by; boardw = bw; boardh = bh; } } • Kept the ship from outside the borders. void left() { int x = Math.max(getX() -1, boardx); setPosition(x, getY()); } void right() { int x = Math.min(getX() +1, boardw - imageWidth); setPosition(x, getY()); } void up() { int y = Math.max(getY() -1,boardy); setPosition(getX(), y); } void down() { int y = Math.min(getY() +1, boardh - imageHeight); setPosition(getX(), y); }

  22. Adding aliens • First create an alien and shot class that extends the sprite class • When new aliens or firing happens we add it to the layer manager. • Both aliens and shot class have information about how the move • The checkGameState() function cycles through all the objects in the layout manager, telling them to move and then checks for collisions. • It will also decide when to create more aliens

  23. CheckGameState() if (numAliens ==0) { int x = myRandom.nextInt(WATER_BOUNDARY_RIGHT - WATER_BOUNDARY_LEFT - iAlien.getWidth()); manager.insert(new myAlien(iAlien,iAlien.getWidth()/2,iAlien.getHeight(),WATER_BOUNDARY_LEFT + x, GAME_ORIGIN_Y, this),0); numAliens++; } else if (numAliens<maxAliens) { if (myRandom.nextInt(100)>97) { int x = myRandom.nextInt(WATER_BOUNDARY_RIGHT - WATER_BOUNDARY_LEFT - iAlien.getWidth()); manager.insert(new myAlien(iAlien,iAlien.getWidth()/2,iAlien.getHeight(),WATER_BOUNDARY_LEFT + x, GAME_ORIGIN_Y, this),0); numAliens++; } } //update all movement. for(inti=0; i<(manager.getSize() -2); i++) { if (manager.getLayerAt(i) instanceofmyAlien ) { ma = (myAlien) manager.getLayerAt(i); if (ma.moveme()) {//if moved off the board. manager.remove(ma); numAliens--; } } else if (manager.getLayerAt(i) instanceofmyShot) { ms = (myShot) manager.getLayerAt(i); if (ms.moveme()) { //moved off the board. manager.remove(ms); numShots--; } } }

  24. and finally shooting • In checkUserInput() • Look for FIRE_PRESSED • When pressed, generate a “shot” and add it to the layer manager. • Layer manager now controls the shot, just like an alien class. • Hopefully we hit an alien before they collide with our ship.

  25. Firing code • In checkUserInput() add } else if ((keyState & FIRE_PRESSED) != 0){ manager.insert(new myShot(iShot,iShot.getWidth()/3,iShot.getHeight(),ship.getX()+4, ship.getY()-iShot.getHeight(), this),0); numShots ++; }

  26. Shot class class myShot extends Sprite { intimageHeight, imageWidth; intboardx, boardy, boardh, boardw; MyGameCanvas parent; myShot(Image i, intfw, intfh, intmx, int my, MyGameCanvasmyParent) { super(i,fw,fh); parent = myParent; setPosition(mx, my); imageHeight = i.getHeight(); imageWidth = i.getWidth(); boardx = parent.WATER_BOUNDARY_LEFT; boardy = parent.GAME_ORIGIN_Y; boardw = parent.WATER_BOUNDARY_RIGHT; boardh = parent.GAME_ORIGIN_Y + parent.GAME_HEIGHT; } booleanmoveme() { nextFrame(); int y = getY() -1; if (y < boardy) { //leave the screen return true; } setPosition (getX(),y); return false; } }

  27. Collisions • Sprite has collidesWidth() method. • Loop through and figure out if things have collided with each other. • CollidesWidth(Sprite S, booleanpixellevel) • Pixellevel true • By pixel if two non opaque pixels collide • false • By simple bounds checking • Collisions with the ship: for(inti=0; i<(manager.getSize() -2); i++) { if (ship.collidesWith((Sprite)manager.getLayerAt(i), true)) { manager.remove(manager.getLayerAt(i)); manager.remove(ship); gameover =true; break; } }

  28. The rest of the collisions for(inti=0; i<(manager.getSize() -2); i++) { //note last two items are the ship and background, don’t test them. obj = (Sprite) manager.getLayerAt(i); for(int j=i+1; j<(manager.getSize()-2); j++) { if (obj.collidesWith((Sprite)manager.getLayerAt(j), true)) { if (objinstanceofmyAlien) { //polymorphism, figure out if alien or shot numAliens--; } else { numShots--; } if (manager.getLayerAt(j) instanceofmyAlien){ numAliens--; } else { numShots--; } score += 1; manager.remove(manager.getLayerAt(j)); manager.remove(obj); break; } } }

  29. Scoring • Every game should have a score. • Add a new variable to called score. • Add to it every time there is a collision • Two aliens colliding only counts once • Add to render g.drawString("Score: "+score, GAME_ORIGIN_X,GAME_ORIGIN_Y-g.getFont().getHeight(),0);

  30. Game Over! • Again, add a gameoverboolean variable • Add to render if(gameover) { g.drawString("GAME OVER", GAME_ORIGIN_X + GAME_WIDTH/2, GAME_ORIGIN_Y + GAME_HEIGHT/2, g.TOP|g.HCENTER); } • Add an if statement to animation loop if (!gameover) { // Verify game state, "move" objects, etc... checkGameState(); // get user input checkUserInput(); // update screen }

  31. For lastly. • Of course, while this is a “complete” game, but • The aliens should be able to shot at the ship. • The aliens tend to collide with each other. • And randomly head down the screen. • More lives: • add a variable and in the collision code, check to see if the ship still has lived should continue. • But it was intended to show you all how to use the gameCanvas. • For the complete source code, check the example page.

  32. Blackberry notes • “Game” worked as is with blackberries with keyboard. • Left is A key, right is the S key, R is up, F is down and Fire is space on the Tour, but varied between Blackberries

  33. Blackberry notes (2) • With the Storm… • No keyboard • Need to work with touchevents • virtual key worked though, but had to move the gamescreen up.

  34. Storm & Storm 2 • To work with TouchEvents, • Use net.rim.device.api.lcdui.game, which inherits game. Declare as BlackBerryGameCanvas and use the touchEvent() method. • Valid Events • DOWN - Finger touch the screen • UP - Finger(s) lifted from screen • MOVE Finger drag or slide on screen • CANCEL - Overriding system event, interrupting touch sequence • GESTURE - Gesture detected, there is a method called getGesture() cite above • UNCLICK - Finger releases from depressed screen until feedback is felt • CLICK - Finger presses screen until feedback is felt

  35. Storm & Storm 2 (2) • But touch to move is not great. • To show the full power of this battle station…. • Accelerometer Sensor! • net.rim.device.api.system.AccelerometerSensor.* • Use click TouchEvent for firing and accelerometer to move. • A Note: Blackberry MIDLet and application are incompatible in many ways. So if this was rewritten to be a application, you would not be able to use the (BlackBerry)GameCanvas, since it won’t display. • MIDlets can call many of the applications functions though.

  36. Accelerometer Sensor • With applications, implement an interface • With MIDlet, call it directly. import net.rim.device.api.system.AccelerometerSensor.*; import net.rim.device.api.system.Application; private short[] xyz = new short[ 3 ]; //for accelerometer data private Channel accChannel; //for the accelerometer channel //open accelerometer channel accChannel = AccelerometerSensor.openRawDataChannel( Application.getApplication() ); //read the data accChannel.getLastAccelerationData( xyz ); //close the channel. accChannel.close();

  37. Accelerometer Sensor (2) • X axis runs from east (negative values) to west (positive values); • Y axis runs from north (negative values) to south (positive values); and • Z axis runs from the front of the BlackBerry device (negative values) to the back of the device (positive values). //left right first. if (xyz[0] > 0) { ship.left(); } else if(xyz[0] <0) { ship.right(); } //up and down if(xyz[1] > 600) { //allow for about a 45% tilt ship.down(); } else if(xyz[1] <400) { ship.up(); } See AccelerometerSensor API for more Information.

  38. Storm Notes. • Screen flipping between portrait and landscape is a problem. • To stop it, need to use setAcceptableDirections( int), but it’s a “privileged” command and you need a Blackberry key to sign the app in order for it to work.

  39. Andorid? • We'll leave this to another lecture.

  40. References • Java Docs for RIM api, jsr118 api’s • Useful tutorials • http://www.java.net/author/vikram-goyal • So useful information: • Math & Physics http://www.gamedev.net/reference/list.asp?categoryid=28 • Essential Math for Games Programmers • http://www.essentialmath.com/ • 2D Game Physics 101 • http://www.rodedev.com/tutorials/gamephysics/

  41. Q A &

More Related