Create empty game window
This commit is contained in:
@@ -3,20 +3,25 @@
|
||||
*/
|
||||
package com.bartlomiejpluta.samplegame;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.engine.GameEngine;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class App implements ApplicationRunner {
|
||||
private final GameEngine gameEngine;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
gameEngine.start();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(App.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.bartlomiejpluta.samplegame.core.engine;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.thread.ThreadManager;
|
||||
import com.bartlomiejpluta.samplegame.core.time.ChronoMeter;
|
||||
import com.bartlomiejpluta.samplegame.core.ui.Window;
|
||||
import com.bartlomiejpluta.samplegame.core.ui.WindowManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DefaultGameEngine implements GameEngine {
|
||||
private final WindowManager windowManager;
|
||||
private final ThreadManager threadManager;
|
||||
|
||||
private final Thread thread;
|
||||
private final Window window;
|
||||
private final ChronoMeter chrono;
|
||||
private final int targetUps;
|
||||
|
||||
private boolean running = false;
|
||||
|
||||
@Autowired
|
||||
public DefaultGameEngine(WindowManager windowManager,
|
||||
ThreadManager threadManager,
|
||||
@Value("${app.window.title}") String title,
|
||||
@Value("${app.window.width}") int width,
|
||||
@Value("${app.window.height}") int height,
|
||||
@Value("${app.core.targetUps}") int targetUps) {
|
||||
this.windowManager = windowManager;
|
||||
this.threadManager = threadManager;
|
||||
|
||||
|
||||
this.window = windowManager.createWindow(title, width, height);
|
||||
this.thread = threadManager.createThread(this::run);
|
||||
this.chrono = new ChronoMeter();
|
||||
this.targetUps = targetUps;
|
||||
}
|
||||
|
||||
private void run() {
|
||||
try {
|
||||
init();
|
||||
loop();
|
||||
} finally {
|
||||
cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
window.init();
|
||||
chrono.init();
|
||||
}
|
||||
|
||||
private void loop() {
|
||||
running = true;
|
||||
var dt = 0.0f;
|
||||
var accumulator = 0.0f;
|
||||
var step = 1.0f / targetUps;
|
||||
|
||||
while (running && !window.shouldClose()) {
|
||||
dt = chrono.getElapsedTime();
|
||||
accumulator += dt;
|
||||
|
||||
input();
|
||||
|
||||
while (accumulator >= step) {
|
||||
update(dt);
|
||||
accumulator -= step;
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
private void input() {
|
||||
|
||||
}
|
||||
|
||||
private void update(float dt) {
|
||||
|
||||
}
|
||||
|
||||
private void render() {
|
||||
window.update();
|
||||
}
|
||||
|
||||
private void cleanUp() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.bartlomiejpluta.samplegame.core.engine;
|
||||
|
||||
public interface GameEngine {
|
||||
void start();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.bartlomiejpluta.samplegame.core.error;
|
||||
|
||||
public class AppException extends RuntimeException {
|
||||
public AppException() {
|
||||
}
|
||||
|
||||
public AppException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AppException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AppException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public AppException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.bartlomiejpluta.samplegame.core.thread;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ThreadManager {
|
||||
public Thread createThread(Runnable runnable) {
|
||||
return new Thread(runnable);
|
||||
}
|
||||
}
|
||||
20
app/src/main/java/com/bartlomiejpluta/samplegame/core/time/ChronoMeter.java
Executable file
20
app/src/main/java/com/bartlomiejpluta/samplegame/core/time/ChronoMeter.java
Executable file
@@ -0,0 +1,20 @@
|
||||
package com.bartlomiejpluta.samplegame.core.time;
|
||||
|
||||
public class ChronoMeter {
|
||||
private double latchedTime;
|
||||
|
||||
public void init() {
|
||||
latchedTime = getTime();
|
||||
}
|
||||
|
||||
private double getTime() {
|
||||
return System.nanoTime() / 1_000_000_000.0;
|
||||
}
|
||||
|
||||
public float getElapsedTime() {
|
||||
double time = getTime();
|
||||
float elapsedTime = (float) (time - latchedTime);
|
||||
latchedTime = time;
|
||||
return elapsedTime;
|
||||
}
|
||||
}
|
||||
107
app/src/main/java/com/bartlomiejpluta/samplegame/core/ui/Window.java
Executable file
107
app/src/main/java/com/bartlomiejpluta/samplegame/core/ui/Window.java
Executable file
@@ -0,0 +1,107 @@
|
||||
package com.bartlomiejpluta.samplegame.core.ui;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.error.AppException;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
|
||||
import static org.lwjgl.system.MemoryUtil.NULL;
|
||||
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Window {
|
||||
private final String title;
|
||||
private int width;
|
||||
private int height;
|
||||
private long windowHandle;
|
||||
private boolean resized;
|
||||
|
||||
public void init() {
|
||||
// Setup an error callback. The default implementation
|
||||
// will print the error message in System.err.
|
||||
GLFWErrorCallback.createPrint(System.err).set();
|
||||
|
||||
// Initialize GLFW. Most GLFW functions will not work before doing this.
|
||||
if (!glfwInit()) {
|
||||
throw new AppException("Unable to initialize GLFW");
|
||||
}
|
||||
|
||||
glfwDefaultWindowHints(); // optional, the current window hints are already the default
|
||||
glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
|
||||
// Create the window
|
||||
windowHandle = glfwCreateWindow(width, height, title, NULL, NULL);
|
||||
if (windowHandle == NULL) {
|
||||
throw new AppException("Failed to create the GLFW window");
|
||||
}
|
||||
|
||||
// Setup resize callback
|
||||
glfwSetFramebufferSizeCallback(windowHandle, (window, width, height) -> {
|
||||
Window.this.width = width;
|
||||
Window.this.height = height;
|
||||
Window.this.resized = true;
|
||||
});
|
||||
|
||||
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
|
||||
glfwSetKeyCallback(windowHandle, (window, key, scancode, action, mods) -> {
|
||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
|
||||
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
|
||||
}
|
||||
});
|
||||
|
||||
// Get the resolution of the primary monitor
|
||||
GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||
|
||||
// Center our window
|
||||
glfwSetWindowPos(windowHandle, (videoMode.width() - width) / 2, (videoMode.height() - height) / 2);
|
||||
|
||||
// Make the OpenGL context current
|
||||
glfwMakeContextCurrent(windowHandle);
|
||||
|
||||
// Enable V-Sync
|
||||
glfwSwapInterval(1);
|
||||
|
||||
// Make the window visible
|
||||
glfwShowWindow(windowHandle);
|
||||
|
||||
GL.createCapabilities();
|
||||
|
||||
// Support for transparencies
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Set the clear color
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
glfwSwapBuffers(windowHandle);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
public boolean isKeyPressed(int keyCode) {
|
||||
return glfwGetKey(windowHandle, keyCode) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
public void clear(float r, float g, float b, float alpha) {
|
||||
glClearColor(r, g, b, alpha);
|
||||
}
|
||||
|
||||
public boolean shouldClose() {
|
||||
return glfwWindowShouldClose(windowHandle);
|
||||
}
|
||||
|
||||
public static Window create(String title, int width, int height) {
|
||||
return new Window(title, width, height, -1, false);
|
||||
}
|
||||
}
|
||||
10
app/src/main/java/com/bartlomiejpluta/samplegame/core/ui/WindowManager.java
Executable file
10
app/src/main/java/com/bartlomiejpluta/samplegame/core/ui/WindowManager.java
Executable file
@@ -0,0 +1,10 @@
|
||||
package com.bartlomiejpluta.samplegame.core.ui;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WindowManager {
|
||||
public Window createWindow(String title, int width, int height) {
|
||||
return Window.create(title, width, height);
|
||||
}
|
||||
}
|
||||
8
app/src/main/resources/application.yml
Executable file
8
app/src/main/resources/application.yml
Executable file
@@ -0,0 +1,8 @@
|
||||
app:
|
||||
window:
|
||||
title: "Simple Game"
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
core:
|
||||
targetUps: 50 # Updates per second
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* This Java source file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
package com.bartlomiejpluta.samplegame;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class AppTest {
|
||||
@Test public void testAppHasAGreeting() {
|
||||
App classUnderTest = new App();
|
||||
assertNotNull("app should have a greeting", classUnderTest.getGreeting());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user