Path: blob/master/SLICK_HOME/src/org/newdawn/slick/GameContainer.java
1456 views
package org.newdawn.slick;12import java.io.IOException;3import java.util.Properties;45import org.lwjgl.LWJGLException;6import org.lwjgl.Sys;7import org.lwjgl.input.Cursor;8import org.lwjgl.opengl.Display;9import org.lwjgl.opengl.Drawable;10import org.lwjgl.opengl.Pbuffer;11import org.lwjgl.opengl.PixelFormat;12import org.newdawn.slick.gui.GUIContext;13import org.newdawn.slick.openal.SoundStore;14import org.newdawn.slick.opengl.CursorLoader;15import org.newdawn.slick.opengl.ImageData;16import org.newdawn.slick.opengl.renderer.Renderer;17import org.newdawn.slick.opengl.renderer.SGL;18import org.newdawn.slick.util.Log;19import org.newdawn.slick.util.ResourceLoader;2021/**22* A generic game container that handles the game loop, fps recording and23* managing the input system24*25* @author kevin26*/27public abstract class GameContainer implements GUIContext {28/** The renderer to use for all GL operations */29protected static SGL GL = Renderer.get();30/** The shared drawable if any */31protected static Drawable SHARED_DRAWABLE;3233/** The time the last frame was rendered */34protected long lastFrame;35/** The last time the FPS recorded */36protected long lastFPS;37/** The last recorded FPS */38protected int recordedFPS;39/** The current count of FPS */40protected int fps;41/** True if we're currently running the game loop */42protected boolean running = true;4344/** The width of the display */45protected int width;46/** The height of the display */47protected int height;48/** The game being managed */49protected Game game;5051/** The default font to use in the graphics context */52private Font defaultFont;53/** The graphics context to be passed to the game */54private Graphics graphics;5556/** The input system to pass to the game */57protected Input input;58/** The FPS we want to lock to */59protected int targetFPS = -1;60/** True if we should show the fps */61private boolean showFPS = true;62/** The minimum logic update interval */63protected long minimumLogicInterval = 1;64/** The stored delta */65protected long storedDelta;66/** The maximum logic update interval */67protected long maximumLogicInterval = 0;68/** The last game started */69protected Game lastGame;70/** True if we should clear the screen each frame */71protected boolean clearEachFrame = true;7273/** True if the game is paused */74protected boolean paused;75/** True if we should force exit */76protected boolean forceExit = true;77/** True if vsync has been requested */78protected boolean vsync;79/** Smoothed deltas requested */80protected boolean smoothDeltas;81/** The number of samples we'll attempt through hardware */82protected int samples;8384/** True if this context supports multisample */85protected boolean supportsMultiSample;8687/** True if we should render when not focused */88protected boolean alwaysRender;8990/**91* Create a new game container wrapping a given game92*93* @param game The game to be wrapped94*/95protected GameContainer(Game game) {96this.game = game;97lastFrame = getTime();9899getBuildVersion();100Log.checkVerboseLogSetting();101}102103/**104* Set the default font that will be intialised in the graphics held in this container105*106* @param font The font to use as default107*/108public void setDefaultFont(Font font) {109if (font != null) {110this.defaultFont = font;111} else {112Log.warn("Please provide a non null font");113}114}115116/**117* Indicate whether we want to try to use fullscreen multisampling. This will118* give antialiasing across the whole scene using a hardware feature.119*120* @param samples The number of samples to attempt (2 is safe)121*/122public void setMultiSample(int samples) {123this.samples = samples;124}125126/**127* Check if this hardware can support multi-sampling128*129* @return True if the hardware supports multi-sampling130*/131public boolean supportsMultiSample() {132return supportsMultiSample;133}134135/**136* The number of samples we're attempting to performing using137* hardware multisampling138*139* @return The number of samples requested140*/141public int getSamples() {142return samples;143}144145/**146* Indicate if we should force exitting the VM at the end147* of the game (default = true)148*149* @param forceExit True if we should force the VM exit150*/151public void setForceExit(boolean forceExit) {152this.forceExit = forceExit;153}154155/**156* Indicate if we want to smooth deltas. This feature will report157* a delta based on the FPS not the time passed. This works well with158* vsync.159*160* @param smoothDeltas True if we should report smooth deltas161*/162public void setSmoothDeltas(boolean smoothDeltas) {163this.smoothDeltas = smoothDeltas;164}165166/**167* Check if the display is in fullscreen mode168*169* @return True if the display is in fullscreen mode170*/171public boolean isFullscreen() {172return false;173}174175/**176* Get the aspect ratio of the screen177*178* @return The aspect ratio of the display179*/180public float getAspectRatio() {181return getWidth() / getHeight();182}183184/**185* Indicate whether we want to be in fullscreen mode. Note that the current186* display mode must be valid as a fullscreen mode for this to work187*188* @param fullscreen True if we want to be in fullscreen mode189* @throws SlickException Indicates we failed to change the display mode190*/191public void setFullscreen(boolean fullscreen) throws SlickException {192}193194/**195* Enable shared OpenGL context. After calling this all containers created196* will shared a single parent context197*198* @throws SlickException Indicates a failure to create the shared drawable199*/200public static void enableSharedContext() throws SlickException {201try {202SHARED_DRAWABLE = new Pbuffer(64, 64, new PixelFormat(8, 0, 0), null);203} catch (LWJGLException e) {204throw new SlickException("Unable to create the pbuffer used for shard context, buffers not supported", e);205}206}207208/**209* Get the context shared by all containers210*211* @return The context shared by all the containers or null if shared context isn't enabled212*/213public static Drawable getSharedContext() {214return SHARED_DRAWABLE;215}216217/**218* Indicate if we should clear the screen at the beginning of each frame. If you're219* rendering to the whole screen each frame then setting this to false can give220* some performance improvements221*222* @param clear True if the the screen should be cleared each frame223*/224public void setClearEachFrame(boolean clear) {225this.clearEachFrame = clear;226}227228/**229* Renitialise the game and the context in which it's being rendered230*231* @throws SlickException Indicates a failure rerun initialisation routines232*/233public void reinit() throws SlickException {234}235236/**237* Pause the game - i.e. suspend updates238*/239public void pause()240{241setPaused(true);242}243244/**245* Resumt the game - i.e. continue updates246*/247public void resume()248{249setPaused(false);250}251252/**253* Check if the container is currently paused.254*255* @return True if the container is paused256*/257public boolean isPaused() {258return paused;259}260261/**262* Indicates if the game should be paused, i.e. if updates263* should be propogated through to the game.264*265* @param paused True if the game should be paused266*/267public void setPaused(boolean paused)268{269this.paused = paused;270}271272/**273* True if this container should render when it has focus274*275* @return True if this container should render when it has focus276*/277public boolean getAlwaysRender () {278return alwaysRender;279}280281/**282* Indicate whether we want this container to render when it has focus283*284* @param alwaysRender True if this container should render when it has focus285*/286public void setAlwaysRender (boolean alwaysRender) {287this.alwaysRender = alwaysRender;288}289290/**291* Get the build number of slick292*293* @return The build number of slick294*/295public static int getBuildVersion() {296try {297Properties props = new Properties();298props.load(ResourceLoader.getResourceAsStream("version"));299300int build = Integer.parseInt(props.getProperty("build"));301Log.info("Slick Build #"+build);302303return build;304} catch (Exception e) {305Log.error("Unable to determine Slick build number");306return -1;307}308}309310/**311* Get the default system font312*313* @return The default system font314*/315public Font getDefaultFont() {316return defaultFont;317}318319/**320* Check if sound effects are enabled321*322* @return True if sound effects are enabled323*/324public boolean isSoundOn() {325return SoundStore.get().soundsOn();326}327328/**329* Check if music is enabled330*331* @return True if music is enabled332*/333public boolean isMusicOn() {334return SoundStore.get().musicOn();335}336337/**338* Indicate whether music should be enabled339*340* @param on True if music should be enabled341*/342public void setMusicOn(boolean on) {343SoundStore.get().setMusicOn(on);344}345346/**347* Indicate whether sound effects should be enabled348*349* @param on True if sound effects should be enabled350*/351public void setSoundOn(boolean on) {352SoundStore.get().setSoundsOn(on);353}354355/**356* Retrieve the current default volume for music357* @return the current default volume for music358*/359public float getMusicVolume() {360return SoundStore.get().getMusicVolume();361}362363/**364* Retrieve the current default volume for sound fx365* @return the current default volume for sound fx366*/367public float getSoundVolume() {368return SoundStore.get().getSoundVolume();369}370371/**372* Set the default volume for sound fx373* @param volume the new default value for sound fx volume374*/375public void setSoundVolume(float volume) {376SoundStore.get().setSoundVolume(volume);377}378379/**380* Set the default volume for music381* @param volume the new default value for music volume382*/383public void setMusicVolume(float volume) {384SoundStore.get().setMusicVolume(volume);385}386387/**388* Get the width of the standard screen resolution389*390* @return The screen width391*/392public abstract int getScreenWidth();393394/**395* Get the height of the standard screen resolution396*397* @return The screen height398*/399public abstract int getScreenHeight();400401/**402* Get the width of the game canvas403*404* @return The width of the game canvas405*/406public int getWidth() {407return width;408}409410/**411* Get the height of the game canvas412*413* @return The height of the game canvas414*/415public int getHeight() {416return height;417}418419/**420* Set the icon to be displayed if possible in this type of421* container422*423* @param ref The reference to the icon to be displayed424* @throws SlickException Indicates a failure to load the icon425*/426public abstract void setIcon(String ref) throws SlickException;427428/**429* Set the icons to be used for this application. Note that the size of the icon430* defines how it will be used. Important ones to note431*432* Windows window icon must be 16x16433* Windows alt-tab icon must be 24x24 or 32x32 depending on Windows version (XP=32)434*435* @param refs The reference to the icon to be displayed436* @throws SlickException Indicates a failure to load the icon437*/438public abstract void setIcons(String[] refs) throws SlickException;439440/**441* Get the accurate system time442*443* @return The system time in milliseconds444*/445public long getTime() {446return (Sys.getTime() * 1000) / Sys.getTimerResolution();447}448449/**450* Sleep for a given period451*452* @param milliseconds The period to sleep for in milliseconds453*/454public void sleep(int milliseconds) {455long target = getTime()+milliseconds;456while (getTime() < target) {457try { Thread.sleep(1); } catch (Exception e) {}458}459}460461/**462* Set the mouse cursor to be displayed - this is a hardware cursor and hence463* shouldn't have any impact on FPS.464*465* @param ref The location of the image to be loaded for the cursor466* @param hotSpotX The x coordinate of the hotspot within the cursor image467* @param hotSpotY The y coordinate of the hotspot within the cursor image468* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor469*/470public abstract void setMouseCursor(String ref, int hotSpotX, int hotSpotY) throws SlickException;471472/**473* Set the mouse cursor to be displayed - this is a hardware cursor and hence474* shouldn't have any impact on FPS.475*476* @param data The image data from which the cursor can be construted477* @param hotSpotX The x coordinate of the hotspot within the cursor image478* @param hotSpotY The y coordinate of the hotspot within the cursor image479* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor480*/481public abstract void setMouseCursor(ImageData data, int hotSpotX, int hotSpotY) throws SlickException;482483/**484* Set the mouse cursor based on the contents of the image. Note that this will not take485* account of render state type changes to images (rotation and such). If these effects486* are required it is recommended that an offscreen buffer be used to produce an appropriate487* image. An offscreen buffer will always be used to produce the new cursor and as such488* this operation an be very expensive489*490* @param image The image to use as the cursor491* @param hotSpotX The x coordinate of the hotspot within the cursor image492* @param hotSpotY The y coordinate of the hotspot within the cursor image493* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor494*/495public abstract void setMouseCursor(Image image, int hotSpotX, int hotSpotY) throws SlickException;496497/**498* Set the mouse cursor to be displayed - this is a hardware cursor and hence499* shouldn't have any impact on FPS.500*501* @param cursor The cursor to use502* @param hotSpotX The x coordinate of the hotspot within the cursor image503* @param hotSpotY The y coordinate of the hotspot within the cursor image504* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor505*/506public abstract void setMouseCursor(Cursor cursor, int hotSpotX, int hotSpotY) throws SlickException;507508/**509* Get a cursor based on a image reference on the classpath. The image510* is assumed to be a set/strip of cursor animation frames running from top to511* bottom.512*513* @param ref The reference to the image to be loaded514* @param x The x-coordinate of the cursor hotspot (left -> right)515* @param y The y-coordinate of the cursor hotspot (bottom -> top)516* @param width The x width of the cursor517* @param height The y height of the cursor518* @param cursorDelays image delays between changing frames in animation519*520* @throws SlickException Indicates a failure to load the image or a failure to create the hardware cursor521*/522public void setAnimatedMouseCursor(String ref, int x, int y, int width, int height, int[] cursorDelays) throws SlickException523{524try {525Cursor cursor;526cursor = CursorLoader.get().getAnimatedCursor(ref, x, y, width, height, cursorDelays);527setMouseCursor(cursor, x, y);528} catch (IOException e) {529throw new SlickException("Failed to set mouse cursor", e);530} catch (LWJGLException e) {531throw new SlickException("Failed to set mouse cursor", e);532}533}534535/**536* Set the default mouse cursor - i.e. the original cursor before any native537* cursor was set538*/539public abstract void setDefaultMouseCursor();540541/**542* Get the input system543*544* @return The input system available to this game container545*/546public Input getInput() {547return input;548}549550/**551* Get the current recorded FPS (frames per second)552*553* @return The current FPS554*/555public int getFPS() {556return recordedFPS;557}558559/**560* Indicate whether mouse cursor should be grabbed or not561*562* @param grabbed True if mouse cursor should be grabbed563*/564public abstract void setMouseGrabbed(boolean grabbed);565566/**567* Check if the mouse cursor is current grabbed. This will cause it not568* to be seen.569*570* @return True if the mouse is currently grabbed571*/572public abstract boolean isMouseGrabbed();573574/**575* Retrieve the time taken to render the last frame, i.e. the change in time - delta.576*577* @return The time taken to render the last frame578*/579protected int getDelta() {580long time = getTime();581int delta = (int) (time - lastFrame);582lastFrame = time;583584return delta;585}586587/**588* Updated the FPS counter589*/590protected void updateFPS() {591if (getTime() - lastFPS > 1000) {592lastFPS = getTime();593recordedFPS = fps;594fps = 0;595}596fps++;597}598599/**600* Set the minimum amount of time in milliseonds that has to601* pass before update() is called on the container game. This gives602* a way to limit logic updates compared to renders.603*604* @param interval The minimum interval between logic updates605*/606public void setMinimumLogicUpdateInterval(int interval) {607minimumLogicInterval = interval;608}609610/**611* Set the maximum amount of time in milliseconds that can passed612* into the update method. Useful for collision detection without613* sweeping.614*615* @param interval The maximum interval between logic updates616*/617public void setMaximumLogicUpdateInterval(int interval) {618maximumLogicInterval = interval;619}620621/**622* Update and render the game623*624* @param delta The change in time since last update and render625* @throws SlickException Indicates an internal fault to the game.626*/627protected void updateAndRender(int delta) throws SlickException {628if (smoothDeltas) {629if (getFPS() != 0) {630delta = 1000 / getFPS();631}632}633634input.poll(width, height);635636Music.poll(delta);637if (!paused) {638storedDelta += delta;639640if (storedDelta >= minimumLogicInterval) {641try {642if (maximumLogicInterval != 0) {643long cycles = storedDelta / maximumLogicInterval;644for (int i=0;i<cycles;i++) {645game.update(this, (int) maximumLogicInterval);646}647648int remainder = (int) (delta % maximumLogicInterval);649if (remainder > minimumLogicInterval) {650game.update(this, (int) (delta % maximumLogicInterval));651storedDelta = 0;652} else {653storedDelta = remainder;654}655} else {656game.update(this, (int) storedDelta);657storedDelta = 0;658}659660} catch (Throwable e) {661Log.error(e);662throw new SlickException("Game.update() failure - check the game code.");663}664}665} else {666game.update(this, 0);667}668669if (hasFocus() || getAlwaysRender()) {670if (clearEachFrame) {671GL.glClear(SGL.GL_COLOR_BUFFER_BIT | SGL.GL_DEPTH_BUFFER_BIT);672}673674GL.glLoadIdentity();675676graphics.resetFont();677graphics.resetLineWidth();678graphics.setAntiAlias(false);679try {680game.render(this, graphics);681} catch (Throwable e) {682Log.error(e);683throw new SlickException("Game.render() failure - check the game code.");684}685graphics.resetTransform();686687if (showFPS) {688defaultFont.drawString(10, 10, "FPS: "+recordedFPS);689}690691GL.flush();692}693694if (targetFPS != -1) {695Display.sync(targetFPS);696}697}698699/**700* Indicate if the display should update only when the game is visible701* (the default is true)702*703* @param updateOnlyWhenVisible True if we should updated only when the display is visible704*/705public void setUpdateOnlyWhenVisible(boolean updateOnlyWhenVisible) {706}707708/**709* Check if this game is only updating when visible to the user (default = true)710*711* @return True if the game is only updated when the display is visible712*/713public boolean isUpdatingOnlyWhenVisible() {714return true;715}716717/**718* Initialise the GL context719*/720protected void initGL() {721Log.info("Starting display "+width+"x"+height);722GL.initDisplay(width, height);723724if (input == null) {725input = new Input(height);726}727input.init(height);728// no need to remove listeners?729//input.removeAllListeners();730if (game instanceof InputListener) {731input.removeListener((InputListener) game);732input.addListener((InputListener) game);733}734735if (graphics != null) {736graphics.setDimensions(getWidth(), getHeight());737}738lastGame = game;739}740741/**742* Initialise the system components, OpenGL and OpenAL.743*744* @throws SlickException Indicates a failure to create a native handler745*/746protected void initSystem() throws SlickException {747initGL();748setMusicVolume(1.0f);749setSoundVolume(1.0f);750751graphics = new Graphics(width, height);752defaultFont = graphics.getFont();753}754755/**756* Enter the orthographic mode757*/758protected void enterOrtho() {759enterOrtho(width, height);760}761762/**763* Indicate whether the container should show the FPS764*765* @param show True if the container should show the FPS766*/767public void setShowFPS(boolean show) {768showFPS = show;769}770771/**772* Check if the FPS is currently showing773*774* @return True if the FPS is showing775*/776public boolean isShowingFPS() {777return showFPS;778}779780/**781* Set the target fps we're hoping to get782*783* @param fps The target fps we're hoping to get784*/785public void setTargetFrameRate(int fps) {786targetFPS = fps;787}788789/**790* Indicate whether the display should be synced to the791* vertical refresh (stops tearing)792*793* @param vsync True if we want to sync to vertical refresh794*/795public void setVSync(boolean vsync) {796this.vsync = vsync;797Display.setVSyncEnabled(vsync);798}799800/**801* True if vsync is requested802*803* @return True if vsync is requested804*/805public boolean isVSyncRequested() {806return vsync;807}808809/**810* True if the game is running811*812* @return True if the game is running813*/814protected boolean running() {815return running;816}817818/**819* Inidcate we want verbose logging820*821* @param verbose True if we want verbose logging (INFO and DEBUG)822*/823public void setVerbose(boolean verbose) {824Log.setVerbose(verbose);825}826827/**828* Cause the game to exit and shutdown cleanly829*/830public void exit() {831running = false;832}833834/**835* Check if the game currently has focus836*837* @return True if the game currently has focus838*/839public abstract boolean hasFocus();840841/**842* Get the graphics context used by this container. Note that this843* value may vary over the life time of the game.844*845* @return The graphics context used by this container846*/847public Graphics getGraphics() {848return graphics;849}850851/**852* Enter the orthographic mode853*854* @param xsize The size of the panel being used855* @param ysize The size of the panel being used856*/857protected void enterOrtho(int xsize, int ysize) {858GL.enterOrtho(xsize, ysize);859}860}861862863