Chapter 5-The Window Class

For further housekeeping on our Boot Class, we'll be moving the code for our GLFW window into its own class

Alright gents, First Sergeant's doing code reviews at 1400 hours. We need to make your Boot class clean and organized, and all of your render and window code packaged up. Hooah?

If that last sentence confused you, don't worry, it took me 20 minutes to find a good way to start this chapter and that was the best I could come up with. Anyways, like the title says, we're going to be cleaning up our Boot class some more to make it nice and pretty. Now, if you're wondering why it's important to keep everything in separate classes, it greatly improves readability. Since the Boot class is where our game starts up and most of our code will be running, it's vital that you be able to find everything clearly scrolling through it.

During my time with a certain Social Media giant, I would often find large chunks of code which were ultimately unreadable due to how poorly organized it was. Keep in mind, this was a website that hundreds of millions of people used, and dozens of teams of engineers keeping it running. In one case, a simple set of values for post metrics could be initialized 300 times due to things not being properly organized. Simply put, the developers that re-initialize these variables had no idea where they were being stored or what they were called, so it wound up being easier for them to create a new array than find where it's supposed to be.

These people had years of education and experience.

By organizing your codebase early on, you ultimately save development time and decrease the odds of your code turning into an unreadable mess.

So, in our tutorials.render package, we'll be creating a new class called Window. Starting off, we'll introduce the constructor and the following methods:

Window.java
public long window;
	
public Window(int width, int height) {
	init(width, height);
}

private void init(int width, int height){

}

public void update() {
	GLFW.glfwSwapBuffers(window);
	GLFW.glfwPollEvents();
}
	
public boolean shouldClose() {
	return GLFW.glfwWindowShouldClose(window);
}
	
public void terminate() {
	Callbacks.glfwFreeCallbacks(window);
	GLFW.glfwDestroyWindow(window);
		
	GLFW.glfwTerminate();
	GLFW.glfwSetErrorCallback(null).free();
}

A lot of this is looking familiar, right? So, as a brief rundown, update() runs the GLFW SwapBuffer and PollEvents methods, which are instrumental for updating the window. shouldClose() shortens things for our condition to continue our game loop. terminate() runs all the shutdown code for terminating GLFW. And the init(...) method is where we'll copy/paste the init() code from our Boot class, with one minor change.

Window.java
private void init(int width, int height){
		GLFWErrorCallback.createPrint(System.err).set();
		
	if(!GLFW.glfwInit()) {
		throw new IllegalStateException("Unable to initialize GLFW");
	}
		
	GLFW.glfwDefaultWindowHints();
	GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
	GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE);
		
	window = GLFW.glfwCreateWindow(width, height, "LWJGL Bootcamp", NULL, NULL);
	if(window == NULL) {
		throw new IllegalStateException("Unable to create GLFW Window");
	}
		
	GLFW.glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {});
		
	try(MemoryStack stack = stackPush()){
		IntBuffer pWidth = stack.mallocInt(1);
		IntBuffer pHeight = stack.mallocInt(1);
			
		GLFW.glfwGetWindowSize(window, pWidth, pHeight);
			
		GLFWVidMode vidmode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
		
		GLFW.glfwSetWindowPos(
			window,
			(vidmode.width() - pWidth.get(0)) / 2,
			(vidmode.height() - pHeight.get(0)) / 2
		);
			
		GLFW.glfwMakeContextCurrent(window);
		GLFW.glfwSwapInterval(1);
		GLFW.glfwShowWindow(window);
	}
		
	GL.createCapabilities();
}

The main changes is now instead of setting the window size in glfwCreateWindow(...), we put the width and height arguments in our method, as well as including the GL.createCapabilities() method in our initialization.

So, with that, we're done with our Window class, and can clean things up in our Boot class.

In the Boot class, we'll be removing the init() method, and adding a new static instance of the Window class.

Boot.java
public static Window window;

public void run(){
    window = new window(640,480);
    loop();
    window.terminate();
}

As mentioned before, the new arguments in our window initialization allow us to change the initial resolution of our window. Once the loop() method is done, we'll be calling window.terminate() to end all of our GLFW processes, and the game will be done and ready to shut down.

We're not quite done yet, though, so if you'll follow me into the loop() method, we'll be removing the GL.createCapabilities() line at the beginning and changing the last two lines of the while loop:

Boot.java
while(!window.shouldClose()) {
	render.cleanUp();
	render.render(meshmeyek);
			
	window.update();
}

As mentioned before, window.shouldClose() returns a test of our shutdown conditions, whereas window.update() runs the SwapBuffer and PollEvent methods for GLFW.

Now that you've all of the direct GLFW calls moved out of the Boot class, you can remove the unused imports, and your class will now be somewhere around 41 lines, and a LOT more readable.

At this point, we've reached an interesting point with our project. We can choose to focus on things in a 2D environment, or we can expand into the Z-axis and experience 3D. Next chapter is when you'll be making your choice. Until then!

Last updated