package com.danitheskunk.skunkworks; import com.danitheskunk.skunkworks.audio.AudioEngine; import com.danitheskunk.skunkworks.audio.ISample; import com.danitheskunk.skunkworks.audio.nodes.Mixer; import com.danitheskunk.skunkworks.audio.nodes.Node; import com.danitheskunk.skunkworks.audio.nodes.SamplePlayer; import com.danitheskunk.skunkworks.audio.nodes.TTS; import com.danitheskunk.skunkworks.backends.gl.Gamepad; import com.danitheskunk.skunkworks.gfx.Color; import com.danitheskunk.skunkworks.gfx.IRenderContext; import com.danitheskunk.skunkworks.gfx.ITexture; import com.danitheskunk.skunkworks.gfx.font.IFont; import com.danitheskunk.skunkworks.gfx.threedee.IRenderContext3D; import com.danitheskunk.skunkworks.nodes.NodeRoot; import org.lwjgl.glfw.GLFW; /** * Main class to extend to write a game in Skunkworks *

* Extend this class and override {@link BaseGame#update update}, * {@link BaseGame#render render}, {@link BaseGame#renderPre renderPre}, * and/or {@link BaseGame#render3D render3D} to add functionality. Then instance * and call {@link BaseGame#run run}. *

* Automatic timing (by default 60fps), resizing(with various scalers), 2d * and 3d rendering, and audio playback. */ @SuppressWarnings("SameParameterValue") public abstract class BaseGame { protected final AudioEngine audioEngine; protected final IFont debugFont; protected final Engine engine; protected final Mixer mixer; protected final NodeRoot rootNode; protected final SamplePlayer samplePlayer; protected final TTS tts; protected final IWindow window; protected final BaseGamepad gamepad; /** * Create with given window size and title * * @param windowSize the size of the game's window * @param windowTitle the title of the game's window */ public BaseGame(Vec2i windowSize, String windowTitle) { audioEngine = new AudioEngine(48000, 256, 8); mixer = new Mixer(audioEngine, 2); samplePlayer = new SamplePlayer(audioEngine); tts = new TTS(audioEngine); Node.connect(samplePlayer, 0, mixer, 0); Node.connect(tts, 0, mixer, 1); audioEngine.setNode(mixer); engine = new Engine(); window = engine.openWindow(windowSize, windowTitle); gamepad = window.getGamepad(0); //todo: load from .jar debugFont = window.loadFontTileset("fonts/ega-8x14.png"); rootNode = new NodeRoot(); } /** * Load sample from path * * @param path file path to the sample * @return the loaded sample */ protected ISample loadSample(String path) { return audioEngine.loadSample(path); } /** * Load image from path and prepare as gpu texture * * @param path file path to the image file * @return the loaded texture */ protected ITexture loadTexture(String path) { return window.loadTexture(path); } /** * Play sample once * * @param sample the sample to be played */ protected void playSample(ISample sample) { samplePlayer.play(sample); } /** * Play sample once or looped * * @param sample the sample to be played * @param looping loops forever if true, otherwise play once */ protected void playSample(ISample sample, boolean looping) { samplePlayer.play(sample, looping); } /** * Render Callback *

* This method will be called after {@link BaseGame#render3D render3D}, * when the engine is ready for 2d rendering. Override this method to * render 2d things on screen. * * @param rc the render context with all the render functions */ protected void render(IRenderContext rc) { //to be overriden by child class } /** * 3D Render Callback *

* This method will be called when the engine is ready for 3d rendering. * Override this method to render 3d things on screen. * * @param rc3d the render context with all the render functions */ protected void render3D(IRenderContext3D rc3d) { //to be overriden by child class } /** * Early Render Callback *

* This method will be called before {@link BaseGame#render3D render3D}, * when the engine is ready for 2d rendering. Override this method to * render 2d things on screen underneath 3d content. * * @param rc the render context with all the render functions */ protected void renderPre(IRenderContext rc) { //to be overriden by child class } /** * Runs the game *

* This will transfer the control to Skunkworks until the game window * closes. */ public void run() { double lastTime, currentTime, delta, currentFrameTime; currentFrameTime = 0; lastTime = GLFW.glfwGetTime(); while(!window.shouldClose()) { currentTime = GLFW.glfwGetTime(); delta = currentTime - lastTime; lastTime = currentTime; currentFrameTime += delta; engine.tick(); window.tick(); gamepad.tick(); //todo: frame rate control if(currentFrameTime >= 1.0 / 60.0) { rootNode.tick(); update(1.0 / 60.0); var p3d = window.getPipeline3D(); var p2d = window.getPipeline2D(); window.startFrame(); p3d.startFrame(); //todo: should be transparent but bugged p3d.getRenderContext().clear(Color.BLACK); render3D(p3d.getRenderContext()); p3d.finishFrame(); p2d.startFrame(); render(p2d.getRenderContext()); rootNode.render(p2d.getRenderContext()); p2d.finishFrame(); window.finishFrame(); currentFrameTime -= 1.0 / 60.0; } else { try { Thread.sleep(1); } catch(InterruptedException e) { throw new RuntimeException(e); } } audioEngine.refill(); } } /** * Update Callback *

* This method will be called periodically, either in a fixed(by default) * or variable period. Can be changed by setting {@link Timestep} * * @param delta time that passed since last update in seconds */ protected void update(double delta) { } }