220 likes | 333 Views
Lecture20. Java Game Programming III – Rate Control, Simple Animation and Display Mode. Timing Issues. In Java 1.4.2 the simplest way to get hold of the time is to use System.currentTimeMillis(). The resolution of this timer is sometimes not good enough to get a consistent frame rate.
E N D
Lecture20 Java Game Programming III – Rate Control, Simple Animation and Display Mode
Timing Issues • In Java 1.4.2 the simplest way to get hold of the time is to use System.currentTimeMillis(). • The resolution of this timer is sometimes not good enough to get a consistent frame rate. • Timer resolution, or granularity, is the amount of time that must separate two timer calls so that different values are returned. For instance, what is the value of diff in the code fragment below? long t1 = System.currentTimeMillis(); long t2 = System.currentTimeMillis(); long diff = t2 – t1; // in ms
Timing Issues • In Windows 95/98, the resolution is 55ms, which means that repeated calls to currentTimeMillis() will only return different values roughly every 55ms. • In the animation loop, the overall effect of poor resolution is to cause the animation to run slower than intended, The elapsed value between iterations will be set to 0 if the game update and rendering time is less than 55ms. • This causes each iteration to sleep longer than necessary. • To try to combat this, the minimum iteration period in Game loop should be greater than 55ms, indicating an upper limit of about 18 FPS. • On Windows 2000, NT, and XP, currentTimeMillis() has a resolution of 10-15ms, making it possible to obtain 67-100 FPS. • The Mac OS X and Linux have timer resolutions of 1ms, which is excellent.
Timing Issues • Java 1.5 has a better timer • System.nanoTime() • Third party timer libraries are also available. • Most of these timing problems are to do with Java on windows platforms. • One of the things directly dependent upon accurate timing is any form of animation. • An upper bound for a good FPS value is the monitor refresh rate. This is typically 70-90Hz (i.e. 70-90 FPS).
Rate Control • One way to deal with the inconsistency of timing on Windows is to average the change in time between frames. • Each game loop record the render time between frames. • Average the last 5 frame times • Use the average to move and update game elements
Animation • Animation adds the dimension of time to computer graphics. • To animate something, the animator has to be able to control, either directly or indirectly, how the thing is to move through time and space as well as how it might change its own shape or appearance over time.
Animating the Aliens • Instead of the entity maintaining just a single sprite we'll add a few sprites and flip between them over time, i.e. Animation. • We need additional variables to our AlienEntity: /** The animation frames */ private Sprite[] frames = new Sprite[4]; /** The time since the last frame change took place */ private long lastFrameChange; /** The frame duration in milliseconds, i.e. how long any given frame of animation lasts */ private long frameDuration = 250; /** The current frame of animation being displayed */ private int frameNumber;
AlienEntity Class – Constructor public AlienEntity(Game game,int x,int y) { super("sprites/alien.gif",x,y); // setup the animatin frames frames[0] = sprite; frames[1] = SpriteStore.get().getSprite("sprites/alien2.gif"); frames[2] = sprite; frames[3] = SpriteStore.get().getSprite("sprites/alien3.gif"); this.game = game; dx = -moveSpeed; }
AlienEntity Class – move() method public void move(long delta) { // since the move tells us how much time has passed // by we can use it to drive the animation lastFrameChange += delta; // if we need to change the frame, update the frame number // and flip over the sprite in use if (lastFrameChange > frameDuration) { // reset our frame change time counter lastFrameChange = 0; // update the frame frameNumber++; if (frameNumber >= frames.length) { frameNumber = 0; } sprite = frames[frameNumber]; } ... }
Full-Screen Exclusive Mode (FSEM) • Full-screen exclusive mode (FSEM) suspends most of Java's windowing environment, bypassing the Swing and AWT graphics layers to offer almost direct access to the screen. • It allows graphics card features, such as page flipping, to be exploited, and permits the screen’s resolution and bit depth to be adjusted. • The graphics hardware acceleration used by FSEM has a disadvantage: • it utilizes video memory (VRAM) which may be 'grabbed back’ by the OS when, for example, it needs to draw another window, display a screensaver, or change the screen's resolution. The application's image buffer, which is stored in the VRAM, will then have to be reconstructed from scratch. • A related issue is that VRAM is not an infinite resource, and trying to place too many images there may cause the OS to start swapping them in and out of memory, causing a slowdown in the rendering.
Setting up JFrame to Full-Screen Exclusive Mode (FSEM) GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice gd = ge.getDefaultScreenDevice(); setUndecorated(true); // no menu bar, borders, etc. setIgnoreRepaint(true); // turn off paint events since doing active rendering setResizable(false); if (!gd.isFullScreenSupported()) { System.exit(0); } gd.setFullScreenWindow(this); // switch on FSEM
BufferStrategy try { EventQueue.invokeAndWait( new Runnable() { public void run() { createBufferStrategy(2); } }); } catch (Exception e) { System.out.println("Error while creating buffer strategy"); System.exit(0); } try { // sleep to give time for buffer strategy to be done Thread.sleep(500); // 0.5 sec } catch (InterruptedException ex){} bufferStrategy = getBufferStrategy(); // store for later
BufferStrategy • invokeAndWait() is employed to avoid a possible deadlock between the createBufferStrategy() call and the event dispatcher thread (this issue should be fixed in J2SE 1.5) • createBufferStrategy() is an asynchronous operation, so the sleep() call delays execution a little time so that the getBufferStrategy() call will get the correct details. • Other asynchronous methods include setDisplayMode(), show(), and setFullScreenWindow().
Rendering onto Frame Buffers Graphics g = bufferStrategy.getDrawGraphics(); // Write rendering commands with g // … g.dispose();
Page Flipping if (!bufferStrategy.contentsLost()) bufferStrategy.show(); else System.out.println("Contents Lost");
Page Flipping • The amount of copying required to display even a single frame is substantial. • For example, a display of 1024 by 768 pixels, with 32 bit depth, will need a 3MB sized copy. • Page flipping avoids these overheads by using a video pointer (if one is available). • The video pointer tells the graphics card where to look in VRAM for the image to be displayed during the next refresh. • Page flipping involves two buffers, which are used alternatively as the primary surface for the screen. While the video pointer is pointing at one buffer, the other is updated. • When the next refresh cycle comes around, the pointer is changed to refer to the second buffer, and the first is updated. • With more than two frame buffers, a flip chain is created. The video pointer cycles through the buffers while rendering is carried out to the other buffers in the chain.
Setting the display mode • A smaller screen resolution and bit depth reduces the amount of data transferred when the back buffer is copied to the screen. • For example, a display of 1024 by 768 pixels, with 32 bit depth, will need a 3MB sized copy. • However, this advantage is irrelevant if the rendering is carried out by page flipping with video pointer manipulation. • A game can run more quickly if its images share the same bit depth as the screen. This is easier to do if we fix the bit depth inside the application. • A known screen size may make drawing operations simpler, especially for images which would normally have to be scaled to fit different display sizes.
Setting the display mode GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice gd = ge.getDefaultScreenDevice(); if (gd.isDisplayChangeSupported()) { DisplayMode dm = new DisplayMode(width, height, bitDepth, DisplayMode.REFRESH_RATE_UNKNOWN); // any refresh rate try { gd.setDisplayMode(dm); } catch (IllegalArgumentException e) { System.out.println("Error setting Display mode"); } try { // sleep to give time for the display to be changed Thread.sleep(1000); // 1 sec } catch(InterruptedException ex){} }
Checking whether a display mode is available private boolean isDisplayModeAvailable( GraphicsDevice gd, int width, int height, int bitDepth) DisplayMode[] modes = gd.getDisplayModes(); for(int i = 0; i < modes.length; i++) { if ( width == modes[i].getWidth() && height == modes[i].getHeight() && bitDepth == modes[i].getBitDepth() ) return true; } return false; }
Accessing the video memory for images • Use the VolatileImage class. E.g. // image creation VolatileImage vImg = createVolatileImage(w, h); // rendering to the image void renderOffscreen() { do { if (vImg.validate(getGraphicsConfiguration()) == VolatileImage.IMAGE_INCOMPATIBLE) { // old vImg doesn't work with new GraphicsConfig; re-create it vImg = createVolatileImage(w, h); } Graphics2D g = vImg.createGraphics(); // // miscellaneous rendering commands... // g.dispose(); } while (vImg.contentsLost()); } Note: createVolatileImage() is a method of Component class or otherwise you may want to use createCompatibleVolatileImage() method of a GraphicsConfiguration object.
VolatileImage – Restoring the contents // copying from the image (here, ghs is a Graphics // object for a frame buffer possibly in the video memory) do { int returnCode = vImg.validate(getGraphicsConfiguration()); if (returnCode == VolatileImage.IMAGE_RESTORED) { // Contents need to be restored renderOffscreen(); // restore contents } else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) { // old vImg doesn't work with new GraphicsConfig; re-create it vImg = createVolatileImage(w, h); renderOffscreen(); } ghs.drawImage(vImg, 0, 0, this); } while (vImg.contentsLost());
References • Space invaders tutorial available at http://www.codebeach.com/