Main class - Fish-In-A-Suit/Conquest GitHub Wiki

Class layout

static imports

  • static org.lwjgl.glfw.GLFW.*;

regular imports

  • renderEngine.Mesh;
  • renderEngine.Renderer;
  • renderEngine.Window;
  • utils.Timer;

private instance fields

  • boolean running;

  • boolean assertions;

  • float[] positions;

  • int[] indices;

  • Thread renderingThread;

  • Window window;

  • Renderer renderer;

  • Mesh mesh;

  • Timer timer;

public methods

  • void run();
  • void update();
  • void render();
  • setupMesh(float[] vertices, int[] indices)
  • static void main(String[] args);

private methods

  • void start();
  • void init();
  • void gameLoop();
  • runAssertions()

Explanation

This is the class from where the whole of the program starts.

It implements the Runnable interface. This interface should be implemented by any class whose instances are intended to be executed by a thread. That class MUST define a method with no arguments run().

The Runnable interface provides only one method, and that is run(). Starting the thread (executing the start() method on a Thread object) causes the object's run() method to be called in a separately executing thread.

In the main() method, a new instance of class Main is created and then immediately called it's start() method.

void start()

The start() method first sets the running variable to true (which will be used later by the run() method). Then, it creates a reference to an object of type Thread called renderingThread. This is done through the following constructor for Thread class: Thread(Runnable target, String name)

  • The target parameter must be an object which implements the Runnable interface - in our example; target is set to this, which corresponds to the Main object which calls the method.
  • name: the name of the new thread

Then, the start() method of renderingThread is called, which "indirectly" calls the run() method. The run() method is provided by the Runnable interface. All of the code that resides within the run() method will be executed on the renderingThread.

run()

void init()

Inside the run() method, first the init() method is called inside a try - catch clause, which is responsible for initializing all of the components which are cruicial for the game to run successfully:

try {
	init();
} catch (Exception e) {
	e.printStackTrace();
}

When the init() method is called, the following calls are made:

private void init() throws Exception {
	window.init();
	renderer.init();
	timer.init();
	setupMesh(positions, indices);
}

The window.init() is responsible for creating the window and it's associated context. render.init() is responsible for creating and linking a new shader program. timer.init() calls the init() method of the Timer class, which initializes the lastLoopTime instance field of the Timer class - this is used by the game loop. setupMesh(positions, indices) creates a new instance of Mesh class, which can be rendered.

void gameLoop()

If the init() method succeeds, the gameLoop() method is called, which starts the game loop.

First, the time-related variables are declared and initialized: long lastTime is assigned the current value of the running JVM's high.resolution time source in nanoseconds.

As long as the boolean running remains true, the following will be repeated over and over again:

  • processInput
  • update()
  • renderer.render(window, mesh)
  • runAssertions()

Inside processInput, all of the input events are collected by glfwPollEvents() and responed to:

glfwPollEvents();
		
if (window.keys[GLFW_KEY_SPACE] == true) {
	System.out.println("SPACEBAR was pressed");
}

If a key is pressed, the invoke(...) which is "hidden" in the second parameter of glfwSetKeyCallback(...) is activated and the code inside it executed, which sets the element of the keys array at index of the keyboard key (every keyboard key is represented with a number) is set to false. When the key is released, that element is set to true.

         glfwSetKeyCallback(windowHandle, (window, key, scancode, action, mods) -> {
             if (action != GLFW_RELEASE) {
		keys[key] = true;
	     } else {
		keys[key] = false;
	     }
	 });

Then, the program checks whether the statement window.keys[GLFW_KEY_SPACE == true] evaluates to true. Here, the value of the GLFW_KEY_SPACE (which is respresented as an int) plays as an index in the array keys[], and we are checking the value of the element at that index. If it is true (meaning that the key was released), the code inside the if-clause is executed.

Inside update(), the front buffer is first swapped with the back buffer: glfwSwapBuffers(window.windowHandle);.

renderer.render(window, mesh) first takes care of window-resize events and then renders to the window.

References