我是 JOGL 的新手,我正在尝试弄清楚如何用它创建渲染循环...我知道我可以使用动画师和 FPS 动画师类,但它们似乎相当有限。我过去写过一些渲染循环,我更喜欢这些循环,我宁愿使用它们,但我似乎无法使用 JOGL 的 GLEventListener 类正确实现它。
这是我的渲染循环(及其所在的类)
package com.richardkase.game;
public class Game implements Runnable {
public final String TITLE = "Test Game";
private boolean running = false;
private Thread thread;
private FPSCounter fps;
////// Constructor //////
public Game() {
fps = new FPSCounter(150, 60);
}
////// Game Loop //////
@Override
public void run() {
while (running) {
fps.findDeltas();
// this executes at "the second argument" of fps times a second
if (fps.checkTickDelta())
tick();
// this executes at "the first argument" of fps times a second
if (fps.checkFrameDelta())
render();
// this code executes once a second
fps.checkPassingSecond();
}
}
////// Tick Methods //////
private void tick() {
long before = System.nanoTime();
// code goes here
fps.tick(before);
}
////// Render Methods //////
private void render() {
long before = System.nanoTime();
// code goes here
fps.render(before);
}
////// Thread Methods //////
private synchronized void start() {
if (running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
private synchronized void stop() {
if (!running)
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
System.exit(1);
e.printStackTrace();
}
}
///////// Main Method //////////
public static void main(String[] args) {
Game game = new Game();
JFrame frame = new JFrame(game.TITLE);
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.start();
}
}
这里是 FPSCounter 类仅供引用,它所做的只是跟踪时间以确保所有内容都按预期执行
public class FPSCounter {
private long lastTime = System.nanoTime();
private double tickRate;
private double tickCheck;
private double tickDelta;
private double frameRate;
private double frameCheck;
private double frameDelta;
private int updates;
private int frames;
private long timer;
private long nanosPerFrame;
private long nanosPerUpdate;
////// Constructor ///////
public FPSCounter(double frameRate, double tickRate) {
this.frameRate = frameRate;
frameCheck = 1_000_000_000 / this.frameRate;
frameDelta = 0;
this.tickRate = tickRate;
tickCheck = 1_000_000_000 / this.tickRate;
tickDelta = 0;
updates = 0;
frames = 0;
timer = System.currentTimeMillis();
}
////// find delta //////
public void findDeltas() {
long now = System.nanoTime();
tickDelta += now - lastTime;
frameDelta += now - lastTime;
lastTime = now;
}
////// Delta Check //////
public boolean checkTickDelta() {
if (tickDelta >= tickCheck) {
tickDelta = 0;
return true;
}
return false;
}
public boolean checkFrameDelta() {
if (frameDelta >= frameCheck) {
frameDelta = 0;
return true;
}
return false;
}
////// Second Check //////
public void checkPassingSecond() {
if (System.currentTimeMillis() - timer > 1000) {
System.out.println(updates + " updates, fps is " + frames);
timer += 1000;
frames = 0;
updates = 0;
}
}
////// Game Loop Methods ///////
public void render(long before) {
long after = System.nanoTime();
nanosPerFrame = after - before;
frames++;
}
public void tick(long before) {
long after = System.nanoTime();
nanosPerUpdate = after - before;
updates++;
}
}
我应该如何将实际内容添加到这个游戏循环中?我应该让这个类也扩展 GLEventListener 还是引用扩展它的类?或者是否有一种更有效的方法来使用动画师类来实现这一点,为我提供我所忽略的这种控制?
任何帮助将不胜感激!谢谢
编辑:
我还应该补充一点,我对 JOGL 很陌生,几天前发现它,因为我试图在 3D 中渲染东西,所以我几乎不知道 JOGL 中的配置文件系统......解释非常好赞赏!
我想我应该在这里布局我要寻找的内容。我需要一个以与上面类似的方式进行渲染的渲染循环,其中游戏以一种速率更新其状态,并以另一种速率重新绘制所有图形。
最佳答案
这就是我开始使用 JOGL 的方式...它非常简单且不言自明,但如果需要,我可以详细解释代码;)
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLCanvas;
import javax.swing.JFrame;
import com.jogamp.opengl.util.FPSAnimator;
public class OpenGLMain implements GLEventListener {
private static FPSAnimator animator;
private static int width;
private static int height;
private static GL2 gl;
public static Rectangle screenSize;
public static JFrame frame;
public static void main(String[] args) {
GLProfile glprofile = GLProfile.getMaximum(true);
GLCapabilities capabilities = new GLCapabilities(glprofile);
GLCanvas canvas = new GLCanvas(capabilities);
screenSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
width = (int) screenSize.getWidth();
height = (int) screenSize.getHeight();
frame = new JFrame("Frame name");
frame.setAlwaysOnTop(false);
frame.setSize(width, height);
frame.add(canvas);
frame.setUndecorated(true);
frame.setVisible(true);
animator = new FPSAnimator(25);
animator.add(canvas);
animator.start();
canvas.addGLEventListener(new OpenGLMain());
canvas.requestFocus();
Listeners.keyClicks(canvas);
Listeners.mouseMovement(canvas);
Listeners.mouseClicks(canvas);
Listeners.mouseScrolled(canvas);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public void display(GLAutoDrawable drawable) {
update();
render(drawable);
}
public void dispose(GLAutoDrawable drawable) {
}
public void init(GLAutoDrawable drawable) {
gl = drawable.getGL().getGL2();
Scenes.init(drawable, gl);
}
public void reshape(GLAutoDrawable drawable, int arg1, int arg2, int arg3, int arg4) {
}
private void update() {
Scenes.update();
}
private void render(GLAutoDrawable drawable) {
Scenes.render(drawable);
}
}
<小时/>
public class OpenGLMain implements GLEventListener {
该行允许您实现 JOGL 附带的 GLEvents
<小时/>GLProfile glprofile = GLProfile.getMaximum(true);
GLCapabilities capabilities = new GLCapabilities(glprofile);
GLCanvas canvas = new GLCanvas(capabilities);
这将获得在 Canvas 中使用的最大可能的 openGL 上下文
<小时/>screenSize = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getMaximumWindowBounds();
width = (int) screenSize.getWidth();
height = (int) screenSize.getHeight();
最大窗口边界将为您提供可用的桌面空间,但您可以将宽度和高度设置为您想要的任何其他尺寸...
<小时/>frame = new JFrame("Frame name");
frame.setAlwaysOnTop(false);
frame.setSize(width, height);
frame.add(canvas);
frame.setUndecorated(true);
frame.setVisible(true);
这是将保存 OpenGL Canvas 的 JFrame。您可以按照自己喜欢的方式进行设置:)
<小时/>animator = new FPSAnimator(25);
animator.add(canvas);
animator.start();
这将创建帧速率为 25fps 的动画器,将其连接到 Canvas 并启动动画器线程
<小时/>canvas.addGLEventListener(new OpenGLMain());
canvas.requestFocus();
这会将 GLEvent 监听器添加到新创建的类实例中,但我猜这是您插入单独的 GLEventListener 类的点
<小时/>Listeners.keyClicks(canvas);
Listeners.mouseMovement(canvas);
Listeners.mouseClicks(canvas);
Listeners.mouseScrolled(canvas);
这是我启动按键、鼠标移动、单击和滚动监听器的方式...它位于一个单独的静态类中
<小时/>frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
这只是窗口关闭事件的监听器...
<小时/>public void display(GLAutoDrawable drawable) {
update();
render(drawable);
}
这是主循环发生的地方...我将其分为 update();
和 render();
方法,以便所有内容都得到更新在渲染之前。
public void init(GLAutoDrawable drawable) {
gl = drawable.getGL().getGL2();
Scenes.init(drawable, gl);
}
渲染之前发生的 init 方法...我有一个单独的静态类 Scenes,我在其中创建要在 Canvas 上绘制的场景的图层和逻辑结构。
关于java - JOGL高效渲染循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27671817/