package com.thecherno.cherno.engine; import com.thecherno.cherno.engine.graphics.Color; import com.thecherno.cherno.engine.graphics.Display; import com.thecherno.cherno.engine.graphics.Screen; import com.thecherno.cherno.engine.graphics.Texture; import com.thecherno.cherno.engine.graphics.Window; import com.thecherno.cherno.engine.input.Keyboard; import com.thecherno.cherno.engine.input.Mouse; import com.thecherno.cherno.engine.interfaces.Renderable; /** * {@code Cherno} engine template class. This class should be the superclass of your main game class. All of the methods * required for the main game loop, rendering and creating graphics, as well as creating a context for the graphics to * run inside of are in this class. * * <p> * * Methods to override: <br> * <ul> * <li>{@code init()}</li> * <li>{@code update()}</li> * <li>{@code render()}</li> * </ul> * Make sure you call {@code show()} at the end of your {@code render()} method to push the graphics to the screen! * <p> * Your implementation of the {@code init()} method should contain the following calls, in the specified order: * <ol> * <li>{@code createDisplay(title, width, height)}</li> * <li>{@code setInput(device(s))}</li> * <li>{@code start()}</li> * </ol> * This will create the display with the appropriate input, and start the main game loop. * * @version 0.1a * @author Yan Chernikov * */ public abstract class Cherno implements Runnable { // Whether or not the game is running, which dictates whether the main game loop runs or not. private boolean running = false; // The Screen object, for rendering. public Screen screen; // The Display object, for pushing Screen elements to the Window. private Display display; // The Thread object for handling the main game loop thread. private Thread thread; // A long to track how long it takes to load the engine when the game starts. private long startTimer = 0L; // The byte code for the keyboard. protected final byte KEYBOARD = Keyboard.CODE; // The byte code for the mouse. protected final byte MOUSE = Mouse.CODE; /** * Starts the main game loop on a new thread, called "Cherno". */ protected final void start() { running = true; thread = new Thread(this, "Cherno"); thread.start(); } /** * Creates a new Display and Window with the given name and size. * * @param name * The title of the window. * @param width * The width of the display (and window) in pixels. * @param height * The height of the display (and window) in pixels. */ protected final void createDisplay(String name, int width, int height) { startTimer = System.currentTimeMillis(); display = new Display(new Window(name, width, height)); screen = new Screen(width, height, 1.0); } protected final void createDisplay(String name, int width, int height, double scale) { startTimer = System.currentTimeMillis(); display = new Display(new Window(name, width, height)); display.setScale(scale); screen = new Screen(width, height, scale); } /** * Sets which devices the engine will use for input. Rather than running this method twice, you can specify two (or * more) devices, separated by a bitwise or ("|"). * * <p> * <strong>eg.</strong> {@code setInput(KEYBOARD | MOUSE);} * * @param device * The device(s) code (grab this from the {@link Cherno} or relevant device class. * */ protected final void setInput(int device) { display.enable((byte) device); } /** * The {@code run()} method: this contains the main game loop (and various timers) of the engine. */ public final void run() { long lastTime = System.nanoTime(); double delta = 0; double ns = 1000000000.0 / 60.0; long timer = System.currentTimeMillis(); int frames = 0; int updates = 0; System.out.println("Took " + ((System.currentTimeMillis() - startTimer) / 1000.0) + " seconds to load!"); while (running) { long now = System.nanoTime(); delta += (now - lastTime) / ns; lastTime = now; if (delta >= 1) { update(); updates++; delta--; } render(); frames++; if (System.currentTimeMillis() - timer > 1000) { timer += 1000; System.out.println(updates + "ups, " + frames + " fps"); updates = 0; frames = 0; } } } /** * Returns the {@code Screen}'s pixel array. * * @return The array of pixels that get rendered onto the screen. */ protected final int[] getPixels() { return screen.getPixels(); } /** * Pushes the pixel array to the screen and swaps buffers. Use this at the <strong>end</strong> of your * {@code render()} method to show what you've rendered on the screen! */ protected final void show() { screen.copy(); display.drawImage(screen.getImage()); display.drawBufferedObjects(screen.getBufferedObjects()); display.show(); } protected Screen getScreen() { return screen; } /** * Clears the pixel array (and thus the screen). This method will clear the screen to black; use the * {@link #clear(Color) clear(Color)} method to specify a custom color to clear to. */ protected final void clear() { screen.clear(); } /** * Clears the pixel array (and thus the screen) to the specified {@link Color}. * * @param col */ protected final void clear(Color col) { screen.clear(col); } protected final void render(int x, int y, Renderable renderable) { renderable.render(x, y, screen); } /** * Renders a texture onto the screen. * * @param texture * The {@link Texture} to render. * @param x * The x-coordinate (in pixels) of where to start rendering the texture (top left corner). * @param y * The y-coordinate (in pixels) of where to start rendering the texture (top left corner). */ protected final void renderTexture(Texture texture, int x, int y) { screen.renderTexture(texture, x, y); } /** * Fills a rectangle of pixels onto the screen, at the specified position with the specified size and color. * * @param x * The x-coordinate (in pixels) of the rectangle. * @param y * The y-coordinate (in pixels) of the rectangle. * @param width * The width of the rectangle. * @param height * The height of the rectangle. * @param color * The {@link Color} of the rectangle. */ protected final void fillRect(int x, int y, int width, int height, Color color) { screen.fillRect(x, y, width, height, color); } /** * Override this method to initialise the game. */ protected abstract void init(); /** * Override this method to update the game. */ protected abstract void update(); /** * Override this method to render the game. */ protected abstract void render(); }