start.PlayLoop.java Source code

Java tutorial

Introduction

Here is the source code for start.PlayLoop.java

Source

/**  
  *  Written by Morgan Allen.
  *  I intend to slap on some kind of open-source license here in a while, but
  *  for now, feel free to poke around for non-commercial purposes.
  */
package start;

import graphics.common.*;
import graphics.widgets.*;
import util.*;

import com.badlogic.gdx.*;
import com.badlogic.gdx.graphics.GL20;

public final class PlayLoop implements ApplicationListener {

    /**  Fields and constant definitions-
      */
    private static boolean verbose = false;

    final public static String DEFAULT_INIT_PACKAGES[] = { "game", "gameUI", "content" };

    public final static int UPDATES_PER_SECOND = 10, FRAMES_PER_SECOND = 60,

            MIN_SLEEP = 10, SLEEP_MARGIN = 2;

    private static String initPackages[] = DEFAULT_INIT_PACKAGES;

    private static Rendering rendering;
    private static Playable playing;
    private static Playable prepared;
    private static Thread gdxThread;
    private static boolean loopChanged = false;

    private static long lastFrame, lastUpdate;
    private static float frameTime;
    private static long numStateUpdates = 0, numFrameUpdates = 0;
    private static float gameSpeed = 1.0f;

    private static boolean initDone = false, shouldLoop = false, paused = false, background = false,
            noInput = false;

    /**  Returns the components of the current game state-
      */
    public static HUD currentUI() {
        if (playing == null)
            return null;
        return playing.UI(playing.isLoading());
    }

    public static Rendering rendering() {
        return rendering;
    }

    public static Playable played() {
        return playing;
    }

    public static boolean onMainThread() {
        return Thread.currentThread() == gdxThread;
    }

    public static boolean mainThreadBegun() {
        return gdxThread != null;
    }

    /**  New GDX method overrides-
      */
    public void create() {
        //
        //  NOTE:  We perform some extra diagnostic printouts here, since the
        //  GL context wasn't obtainable earlier:
        I.say("Please send me this info" + "\n--- GL INFO -----------" + "\n   GL_VENDOR: "
                + Gdx.gl.glGetString(GL20.GL_VENDOR) + "\n GL_RENDERER: " + Gdx.gl.glGetString(GL20.GL_RENDERER)
                + "\n  GL_VERSION: " + Gdx.gl.glGetString(GL20.GL_VERSION) + "\nGLSL_VERSION: "
                + Gdx.gl.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION) + "\n-----------------------\n");
        shouldLoop = true;

        for (String assetPackage : initPackages) {
            Assets.compileAssetList(assetPackage);
        }
        rendering = new Rendering();
    }

    public void resize(int width, int height) {
    }

    public void dispose() {
        disposeLoop();
    }

    public void pause() {
        background = true;
    }

    public void resume() {
        background = false;
    }

    public void render() {
        gdxThread = Thread.currentThread();
        if (!shouldLoop) {
            if (verbose)
                I.say("should not be looping...");
            return;
        }

        final boolean okay = advanceLoop();

        if (!okay) {
            if (verbose)
                I.say("Loop does not want to advance!");
            exitLoop();
        }
    }

    /**  The big static setup, run and exit methods-
      */
    public static void setupAndLoop(Playable scenario) {
        setupAndLoop(scenario, DEFAULT_INIT_PACKAGES);
    }

    public static void setupAndLoop(Playable scenario, String... initPackages) {
        PlayLoop.initPackages = initPackages;
        PlayLoop.loopChanged = true;
        PlayLoop.prepared = scenario;
        PlayLoop.numStateUpdates = 0;
        PlayLoop.numFrameUpdates = 0;
        PlayLoop.gameSpeed = 1.0f;

        if (verbose) {
            I.say("ASSIGNED NEW PLAYABLE: " + scenario);
            I.reportStackTrace();
        }

        if (!initDone) {
            initDone = true;

            //  TODO:  You'll have to pipe this through to the DesktopLauncher (or
            //  something else platform-appropriate) before something can happen!

            /*
            new LwjglApplication(new ApplicationListener() {
            }, getConfig());
            //*/
        }
    }

    public static void sessionStateWipe() {
        I.talkAbout = null;
        playing = null;
        Assets.disposeSessionAssets();

        if (rendering != null)
            rendering.clearAll();
    }

    public static void exitLoop() {
        if (verbose)
            I.say("EXITING PLAY LOOP");
        shouldLoop = false;
        Gdx.app.exit();
    }

    private static void disposeLoop() {
        rendering.dispose();
        Assets.disposeGameAssets();
    }

    private static boolean advanceLoop() {
        final long time = timeMS(), frameGap = time - lastFrame, updateGap;
        final int FRAME_INTERVAL = 1000 / FRAMES_PER_SECOND;
        final int UPDATE_INTERVAL = (int) (1000 / (UPDATES_PER_SECOND * gameSpeed));
        final boolean freeze = paused || background;

        if (freeze || (time - lastUpdate) > UPDATE_INTERVAL * 10) {
            lastUpdate = time;
            updateGap = 0;
        } else {
            updateGap = time - lastUpdate;
            frameTime = (updateGap - 0) * 1.0f / UPDATE_INTERVAL;
            frameTime = Nums.clamp(frameTime, 0, 1);
        }

        if (playing != prepared) {
            if (playing != null && playing.wipeAssetsOnExit()) {
                PlayLoop.sessionStateWipe();
            }
            playing = prepared;
        }
        loopChanged = false;
        float worldTime = (numStateUpdates + frameTime) / UPDATES_PER_SECOND;
        rendering.updateViews(worldTime, frameTime);

        if (verbose) {
            I.say("\nAdvancing play loop, time: " + time);
            I.say("  Last frame/last update: " + lastFrame + "/" + lastUpdate);
            I.say("  Frame/update gap: " + frameGap + "/" + updateGap);
            I.say("  FRAME/UPDATE INTERVAL: " + FRAME_INTERVAL + "/" + UPDATE_INTERVAL);
        }

        if (Assets.loadProgress() < 1) {
            if (verbose) {
                I.say("  Loading assets!");
                I.say("  Loading progress: " + Assets.loadProgress());
            }

            Assets.advanceAssetLoading(FRAME_INTERVAL - (SLEEP_MARGIN * 2));

            rendering.renderDisplay(FRAMES_PER_SECOND);
            rendering.renderUI(null);
            return true;
        }

        if (loopChanged) {
            if (verbose)
                I.say("  Loop changed!  Will return");
            return true;
        }
        if (playing != null && playing.loadProgress() < 1) {
            if (verbose) {
                I.say("  Loading simulation: " + playing);
                I.say("  Is loading?         " + playing.isLoading());
                I.say("  Loading progress:   " + playing.loadProgress());
            }
            if (playing.shouldExitLoop()) {
                if (verbose)
                    I.say("  Exiting loop!  Will return");
                return false;
            }

            if (!playing.isLoading()) {
                if (verbose)
                    I.say("  Beginning simulation setup...");
                playing.beginGameSetup();
            }

            rendering.renderDisplay(FRAMES_PER_SECOND);
            rendering.renderUI(playing.UI(true));
            lastUpdate = lastFrame = time;
            return true;
        }

        //  TODO:  I'm updating graphics as fast as possible for the moment, since
        //  I get occasional flicker problems otherwise.  Still seems wasteful,
        //  mind...
        if (loopChanged) {
            if (verbose)
                I.say("  Loop changed!  Will return");
            return true;
        }
        if (frameGap >= FRAME_INTERVAL || true) {
            if (verbose)
                I.say("  Rendering graphics.");

            if (playing != null) {
                playing.renderVisuals(rendering);
            }
            final HUD UI = playing.UI(false);
            if (UI != null) {
                UI.updateInput();
                UI.renderWorldFX();
            }
            rendering.renderDisplay(FRAMES_PER_SECOND);
            rendering.renderUI(UI);
            KeyInput.updateInputs();
            lastFrame = time;
            numFrameUpdates++;

            I.used60Frames = numFrameUpdates % 60 == 0;
        }

        //  Now we essentially 'pretend' that updates were occurring once every
        //  UPDATE_INTERVAL milliseconds:
        if (playing != null) {
            final int numUpdates = Nums.min((int) (updateGap / UPDATE_INTERVAL),
                    (1 + (FRAME_INTERVAL / UPDATE_INTERVAL)));
            if (playing.shouldExitLoop()) {
                if (verbose)
                    I.say("  Exiting loop!  Will return");
                return false;
            }

            if (verbose)
                I.say("  No. of updates: " + numUpdates);
            if (!freeze)
                for (int n = numUpdates; n-- > 0;) {

                    if (loopChanged) {
                        if (verbose)
                            I.say("  Loop changed!  Will return");
                        return true;
                    }
                    if (verbose)
                        I.say("  Updating simulation.");
                    playing.updateGameState();
                    numStateUpdates++;
                    lastUpdate += UPDATE_INTERVAL;
                }
        }
        return true;
    }

    private static long timeMS() {
        return java.lang.System.nanoTime() / 1000000;
    }

    /**  Pausing the loop, exiting the loop, and setting simulation speed and
      *  frame rate.
      */
    public static float frameTime() {
        return frameTime;
    }

    public static long frameUpdates() {
        return numFrameUpdates;
    }

    public static long stateUpdates() {
        return numStateUpdates;
    }

    public static boolean isFrameIncrement(int unit) {
        return (numFrameUpdates % unit) == 0;
    }

    public static boolean paused() {
        return paused;
    }

    public static float gameSpeed() {
        return gameSpeed;
    }

    public static void setGameSpeed(float mult) {
        gameSpeed = Nums.max(0, mult);
    }

    public static void setPaused(boolean p) {
        paused = p;
    }

    public static void setNoInput(boolean n) {
        noInput = n;
    }
}