如何正确有效地渲染 2D 游戏中的 Java 图形
How To Render Java Graphics in a 2D game correctly and efficiently
我正在寻找一种实现 2D 游戏渲染的有效方法。
这是我的游戏目前使用的渲染系统的示例。 (我要么不知道怎么用,要么不够灵活。)
我使用 Canvas 及其 BufferStrategies,但我不确定它们的效率如何。
感谢任何帮助。
//MY RENDERING SYSTEM EXPLAINED IN JAVA AND WITH ONLY ONE CLASS.
//DOES NOT INCLUDE TICKING JUST RENDERING, TICKING IS A DIFFERENT THREAD.
//(so they can run on different frames per second)
//main() method is at the very bottom.
/*
* @Author
* CodyOrr4
*/
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class Main implements Runnable {
public static Cache cache;
public static JFrame frame;
public static JPanel panel;
public static JViewport camera;
public static Canvas canvas;
public static BufferStrategy bufferStrategy;
public static Graphics graphics;
public static boolean rendering = false;
public static Thread renderingThread;
//turns this runnable into an object.
public Main() {
frame = new JFrame("Rendering System Example");
frame.setSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(1);
frame.setVisible(true);
panel = new JPanel();
panel.setBackground(Color.DARK_GRAY);
canvas = new Canvas();
canvas.setBackground(Color.BLACK);
canvas.setPreferredSize(new Dimension(800, 600));
canvas.setMinimumSize(new Dimension(800, 600));
canvas.setMaximumSize(new Dimension(2000, 2000));
cache = new Cache();
panel.add(canvas);
frame.getContentPane().add(panel);
}
//used to run things that are not meant to be run in a loop;
private void init() {
cache.initCache(); //can now grab sprites (including names/ids) and other types within cache.
}
//renders everything (this method is used in a while() loop based on a boolean, within the run() method);
private void render(Graphics g) {
g.drawImage(cache.getSprite(0), 400, 300, 25, 25, null);
}
//runs the runnable
public void run() {
init();
while(rendering) {
setFps(16);//simply set fps now - iJustin *codys note on the setFps(fps); method* = not sure if its the same thing lol,
//but since ticking and rendering are separate threads in the main source (and contain separate init() methods) it seems like it would be good.
if(bufferStrategy == null) {
canvas.createBufferStrategy(3);//should only need a max of 3.
bufferStrategy = canvas.getBufferStrategy();
graphics = bufferStrategy.getDrawGraphics();
System.out.println("creating canvas components...");
}
//drawing with methods
render(graphics);
//drawing without methods
graphics.drawImage(cache.getSprite(0), 0, 0, 50, 50, null);
bufferStrategy.show();
graphics.dispose();
}
}
//starts the run method and creates a thread for this
public synchronized void start() {
renderingThread = new Thread(this);
renderingThread.setName("Game Rendering Thread");
renderingThread.start();
rendering = true;
}
//stops the while loop by setting the boolean to false and the thread is now null
public synchronized void stop() {
renderingThread = null;
rendering = false;
}
//@Author iJustin - sets fps of the rendering loop (while() loop within run() method)
@SuppressWarnings("static-access")
public void setFps(long fps) {
try {
renderingThread.sleep(fps);
}
catch(InterruptedException e) {
}
}
//main method obv.
public static void main(String[] args) {
Main gameExample = new Main();
gameExample.start();
}
}
对于fps,你应该考虑最后一帧渲染时间的持续时间
它允许有恒定的帧率
为此,您可以为每一帧:
//a the start of rendering process
long startRendering=System.nanoTime();
... rendering here...
//duration of the frame rendering in ms :
long durationMs=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-startRendering);
// now waits
if (durationMs < fps)
{
renderingThread.sleep(fps - durationMs);
}
System.nanoTime 允许以良好的精度测量时间:如果您计算两次 System.nanoTime() 调用之间的差异,则您可以计算出两次调用之间经过的时间(以纳秒为单位)。
我建议不要使用 System.currentTimeMillis,它不太准确
我正在寻找一种实现 2D 游戏渲染的有效方法。
这是我的游戏目前使用的渲染系统的示例。 (我要么不知道怎么用,要么不够灵活。)
我使用 Canvas 及其 BufferStrategies,但我不确定它们的效率如何。 感谢任何帮助。
//MY RENDERING SYSTEM EXPLAINED IN JAVA AND WITH ONLY ONE CLASS.
//DOES NOT INCLUDE TICKING JUST RENDERING, TICKING IS A DIFFERENT THREAD.
//(so they can run on different frames per second)
//main() method is at the very bottom.
/*
* @Author
* CodyOrr4
*/
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class Main implements Runnable {
public static Cache cache;
public static JFrame frame;
public static JPanel panel;
public static JViewport camera;
public static Canvas canvas;
public static BufferStrategy bufferStrategy;
public static Graphics graphics;
public static boolean rendering = false;
public static Thread renderingThread;
//turns this runnable into an object.
public Main() {
frame = new JFrame("Rendering System Example");
frame.setSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(1);
frame.setVisible(true);
panel = new JPanel();
panel.setBackground(Color.DARK_GRAY);
canvas = new Canvas();
canvas.setBackground(Color.BLACK);
canvas.setPreferredSize(new Dimension(800, 600));
canvas.setMinimumSize(new Dimension(800, 600));
canvas.setMaximumSize(new Dimension(2000, 2000));
cache = new Cache();
panel.add(canvas);
frame.getContentPane().add(panel);
}
//used to run things that are not meant to be run in a loop;
private void init() {
cache.initCache(); //can now grab sprites (including names/ids) and other types within cache.
}
//renders everything (this method is used in a while() loop based on a boolean, within the run() method);
private void render(Graphics g) {
g.drawImage(cache.getSprite(0), 400, 300, 25, 25, null);
}
//runs the runnable
public void run() {
init();
while(rendering) {
setFps(16);//simply set fps now - iJustin *codys note on the setFps(fps); method* = not sure if its the same thing lol,
//but since ticking and rendering are separate threads in the main source (and contain separate init() methods) it seems like it would be good.
if(bufferStrategy == null) {
canvas.createBufferStrategy(3);//should only need a max of 3.
bufferStrategy = canvas.getBufferStrategy();
graphics = bufferStrategy.getDrawGraphics();
System.out.println("creating canvas components...");
}
//drawing with methods
render(graphics);
//drawing without methods
graphics.drawImage(cache.getSprite(0), 0, 0, 50, 50, null);
bufferStrategy.show();
graphics.dispose();
}
}
//starts the run method and creates a thread for this
public synchronized void start() {
renderingThread = new Thread(this);
renderingThread.setName("Game Rendering Thread");
renderingThread.start();
rendering = true;
}
//stops the while loop by setting the boolean to false and the thread is now null
public synchronized void stop() {
renderingThread = null;
rendering = false;
}
//@Author iJustin - sets fps of the rendering loop (while() loop within run() method)
@SuppressWarnings("static-access")
public void setFps(long fps) {
try {
renderingThread.sleep(fps);
}
catch(InterruptedException e) {
}
}
//main method obv.
public static void main(String[] args) {
Main gameExample = new Main();
gameExample.start();
}
}
对于fps,你应该考虑最后一帧渲染时间的持续时间 它允许有恒定的帧率
为此,您可以为每一帧:
//a the start of rendering process
long startRendering=System.nanoTime();
... rendering here...
//duration of the frame rendering in ms :
long durationMs=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-startRendering);
// now waits
if (durationMs < fps)
{
renderingThread.sleep(fps - durationMs);
}
System.nanoTime 允许以良好的精度测量时间:如果您计算两次 System.nanoTime() 调用之间的差异,则您可以计算出两次调用之间经过的时间(以纳秒为单位)。 我建议不要使用 System.currentTimeMillis,它不太准确