stratos.start.PlayLoop.java Source code

Java tutorial

Introduction

Here is the source code for stratos.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 stratos.start;

import stratos.game.common.*;
import stratos.graphics.common.*;
import stratos.graphics.widgets.*;
import stratos.util.*;
import stratos.game.actors.Backgrounds;
import stratos.game.wild.Species;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.*;

import java.awt.Dimension;
import java.awt.Toolkit;

public final class PlayLoop {

    /**  Fields and constant definitions-
      */
    final public static Class DEFAULT_INIT_CLASSES[] = { Backgrounds.class, Species.class };
    final public static String DEFAULT_INIT_PACKAGE = "stratos";

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

            DEFAULT_WIDTH = 1200, DEFAULT_HEIGHT = 720, DEFAULT_HERTZ = 60,

            MIN_SLEEP = 10, SLEEP_MARGIN = 2;
    private static boolean verbose = false;

    private static String initPackage = DEFAULT_INIT_PACKAGE;
    private static Class[] initClasses = DEFAULT_INIT_CLASSES;

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

    private static long lastFrame, lastUpdate;
    private static float frameTime;
    private static long numStateUpdates = 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() {
        return played.UI();
    }

    public static Rendering rendering() {
        return rendering;
    }

    public static Playable played() {
        return played;
    }

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

    /**  The big static setup, run and exit methods-
      */
    private static LwjglApplicationConfiguration getConfig() {
        final Dimension SS = Toolkit.getDefaultToolkit().getScreenSize();
        final boolean report = true;

        final LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
        config.title = "Stratos";
        config.useGL20 = true;
        config.vSyncEnabled = true;
        config.width = Nums.min(DEFAULT_WIDTH, SS.width - 100);
        config.height = Nums.min(DEFAULT_HEIGHT, SS.height - 100);
        config.foregroundFPS = DEFAULT_HERTZ;
        config.backgroundFPS = DEFAULT_HERTZ;
        config.resizable = false;
        config.fullscreen = false;

        if (report) {
            I.say("\nSetting up screen configuration...");
            I.say("  Default width/height: " + DEFAULT_WIDTH + "/" + DEFAULT_HEIGHT);
            I.say("  Screen  width/height: " + SS.width + "/" + SS.height);
            I.say("  Window  width/height: " + config.width + "/" + config.height);
            I.say("");
        }
        return config;
    }

    public static void setupAndLoop(Playable scenario) {
        setupAndLoop(scenario, DEFAULT_INIT_PACKAGE, DEFAULT_INIT_CLASSES);
    }

    public static void setupAndLoop(Playable scenario, String initPackage, Class... initClasses) {

        PlayLoop.initPackage = initPackage;
        PlayLoop.initClasses = initClasses;

        PlayLoop.loopChanged = true;
        PlayLoop.played = scenario;
        PlayLoop.numStateUpdates = 0;
        PlayLoop.gameSpeed = 1.0f;

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

        if (!initDone) {
            initDone = true;

            new LwjglApplication(new ApplicationListener() {
                public void create() {
                    shouldLoop = true;
                    initLoop();
                }

                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();
                    }
                }
            }, getConfig());
        }
    }

    public static void sessionStateWipe() {
        I.talkAbout = null;
        played = 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 initLoop() {
        Assets.compileAssetList(initPackage, initClasses);
        for (String name : Assets.classesToLoad()) {
            Session.checkSaveable(name);
        }
        rendering = new Rendering();
    }

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

    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);
        }

        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());
            }

            LoadingScreen.update("Loading Assets", Assets.loadProgress());
            Assets.advanceAssetLoading(FRAME_INTERVAL - (SLEEP_MARGIN * 2));

            rendering.renderDisplay();
            rendering.renderUI(LoadingScreen.HUD(rendering));
            return true;
        }

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

            if (!played.isLoading()) {
                if (verbose)
                    I.say("  Beginning simulation setup...");
                played.beginGameSetup();
            }
            LoadingScreen.update("Loading Simulation", played.loadProgress());

            rendering.renderDisplay();
            rendering.renderUI(LoadingScreen.HUD(rendering));
            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 (played != null) {
                played.renderVisuals(rendering);
            }
            final HUD UI = played.UI();
            if (UI != null) {
                UI.updateInput();
                UI.renderWorldFX();
            }
            rendering.renderDisplay();
            rendering.renderUI(UI);
            KeyInput.updateInputs();
            lastFrame = time;
        }

        //  Now we essentially 'pretend' that updates were occurring once every
        //  UPDATE_INTERVAL milliseconds:
        if (played != null) {
            final int numUpdates = Nums.min((int) (updateGap / UPDATE_INTERVAL),
                    (1 + (FRAME_INTERVAL / UPDATE_INTERVAL)));
            if (played.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.");
                    played.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 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;
    }
}