如何优化2d java游戏渲染?
我的问题是,我有一个随机生成地图的游戏,由于方块的数量,它只有30-40 fps。你可以想象我的游戏就像一个2d雷击机 首先,我搜索屏幕中的第一个磁贴。然后开始循环渲染平铺下一个平铺。。。直到我到达你能看到的最后一块瓷砖 我不使用任何Java类,比如graphics/graphics2d,我使用我自己的代码什么是int[],其中包含屏幕的行,当我渲染一个平铺时,我将屏幕的int[x+y*width]位置更改为块的正确像素 我认为从逻辑上讲,这是渲染贴图的最佳方式,我不理解为什么fps较低。我错了,或者我需要在代码中搜索其他问题?还是有更好的渲染方法如何优化2d java游戏渲染?,java,performance,render,tile,Java,Performance,Render,Tile,我的问题是,我有一个随机生成地图的游戏,由于方块的数量,它只有30-40 fps。你可以想象我的游戏就像一个2d雷击机 首先,我搜索屏幕中的第一个磁贴。然后开始循环渲染平铺下一个平铺。。。直到我到达你能看到的最后一块瓷砖 我不使用任何Java类,比如graphics/graphics2d,我使用我自己的代码什么是int[],其中包含屏幕的行,当我渲染一个平铺时,我将屏幕的int[x+y*width]位置更改为块的正确像素 我认为从逻辑上讲,这是渲染贴图的最佳方式,我不理解为什么fps较低。我错了
如果我跳过世界的渲染,这里有120 fps的稳定速度。有什么问题吗?我知道您不使用Gaphics函数,而是选择操作像素阵列 尝试更改游戏以使用图形对象,因为没有简单有效的方法来解决它。如果您仍然选择不这样做,请尝试添加 System.setPropertysun.java2d.opengl,true 在刚开始编写代码时
public static void main(String[] args) {
当我第一次制作简单的游戏时,我试着用和你一样的方法来做,但是你后来意识到内置的图形功能在性能和易用性上都非常出色
编辑:
关于基本游戏如何使用图形对象的简短说明:
假设您创建了一个JFrame。如果然后添加一个从画布扩展的类并将其添加到画布中。如果您想使用菜单,您甚至可以先创建一个JPanel,然后将画布添加到JPanel中,以便更容易地隐藏它
下面是我们如何创建可用画布的示例:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 1920;
public static final int HEIGHT = WIDTH * 9 / 16;
public static final String TITLE = "YOUR GAMES NAME";
public static final int TICKSPERS = 120;
public static final boolean ISFRAMECAPPED = false;
public static JFrame frame;
private Thread thread;
private boolean running = false;
public int frames;
public int lastFrames;
public int ticks;
public Game(){
Dimension size = new Dimension(WIDTH, HEIGHT);
setPreferredSize(size);
setMaximumSize(size);
setMinimumSize(size);
}
public void render(){
frames++;
BufferStrategy bs = getBufferStrategy();
if (bs == null){
createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(new Color(79,194,232));
g.fillRect(0, 0, getWidth(), getHeight());
//Call your render funtions from here
g.setColor(Color.BLACK);
g.fillRect(120,70,35,90);
g.dispose();
bs.show();
}
public void tick(){
}
public synchronized void start(){
if(running) return;
running = true;
thread = new Thread(this, "Thread");
thread.start();
}
public synchronized void stop(){
if(!running) return;
running = false;
try {
System.exit(1);
frame.dispose();
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void init(){
}
public void run() {
init();
//Tick counter variable
long lastTime = System.nanoTime();
//Nanoseconds per Tick
double nsPerTick = 1000000000D/TICKSPERS;
frames = 0;
ticks = 0;
long fpsTimer = System.currentTimeMillis();
double delta = 0;
boolean shouldRender;
while(running){
shouldRender = !ISFRAMECAPPED;
long now = System.nanoTime();
delta += (now - lastTime) / nsPerTick;
lastTime = now;
//if it should tick it does this
while(delta >= 1 ){
ticks++;
tick();
delta -= 1;
shouldRender = true;
}
if (shouldRender){
render();
}
if (fpsTimer < System.currentTimeMillis() - 1000){
System.out.println(ticks +" ticks, "+ frames+ " frames");
ticks = 0;
lastFrames = frames;
frames = 0;
fpsTimer = System.currentTimeMillis();
}
}
}
public static void main(String[] args){
Game game = new Game();
frame = new JFrame(TITLE);
frame.add(game);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
game.start();
}
}
这个班可以是你的基础。它的tick方法每秒调用120次,其render方法可以在屏幕上渲染内容。
如果您不熟悉图形对象的功能,我建议您阅读一些关于它们的内容
您无法从外部接触图形对象。在处理图形对象之前,需要从游戏渲染函数内部调用渲染函数。尝试将游戏逻辑与渲染函数分离 我使用的架构与上面提到的略有不同。我实际上创建了一个单独的渲染器对象,它基本上是一个延迟渲染器 它的结构是这样的
public class App {
JFrame window;
Renderer renderer;
Engine engine; //implementation is a nested class within App
Dimension window_dimension; //stored for later use
public App() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
window = new JFrame("MyGame");
boolean full_screen = true;
window_dimension = initializeWindow(window, full_screen);
renderer = new Renderer(window_dimension);
window.add(renderer);
engine = new Engine(renderer);
engine.start();
}
});
}
}
Renderer.java:
import java.awt.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class Renderer extends JPanel {
Dimension dim;
private CopyOnWriteArrayList<Drawable> drawables = new CopyOnWriteArrayList<Drawable>();
Renderer(Dimension dim) {
this.dim = dim;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.clearRect(0, 0, getWidth(), getHeight());
for (Drawable drawable : drawables) {
drawable.paint(g2d);
}
g2d.dispose();
drawables.clear();
}
public synchronized void render() {
repaint();
}
public synchronized void submit(Drawable drawable) {
drawables.add(drawable);
}
public synchronized void submitBackground(Drawable drawable) {
drawables.add(0,drawable);
}
}
AbstractEngine.java:
import java.awt.*;
abstract class AbstractEngine implements Runnable {
Renderer renderer;
Dimension dimension;
boolean running;
Thread thread;
public AbstractEngine(Renderer renderer) {
this.renderer = renderer;
dimension = renderer.dim;
}
public void start() {
if (running) return;
running = true;
thread = new Thread(this, "Tread");
thread.start();
}
public void stop() {
if(!running) return;
running = false;
try {
System.exit(1);
thread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
public abstract void handleInput();
public abstract void update();
public abstract void render();
@Override
public void run() {
final int UPS = 120;
final int FPS = 60;
long initialTime = System.nanoTime();
final double timeU = 1000000000 / UPS;
final double timeF = 1000000000 / FPS;
double deltaU = 0, deltaF = 0;
int frames = 0, ticks = 0;
long timer = System.currentTimeMillis();
while (running) {
long currentTime = System.nanoTime();
deltaU += (currentTime - initialTime) / timeU;
deltaF += (currentTime - initialTime) / timeF;
initialTime = currentTime;
if (deltaU >= 1) {
handleInput();
update();
ticks++;
deltaU--;
}
if (deltaF >= 1) {
render();
renderer.render();
frames++;
deltaF--;
}
if (System.currentTimeMillis() - timer > 1000) {
frames = 0;
ticks = 0;
timer += 1000;
}
}
}
}
在扩展引擎类中处理输入。我有一个MouseState对象,它保存鼠标位置和按钮状态。然后,我创建一个MouseAdapter来更新MouseState中的日期,并将其附加到渲染器。我知道这有点奇怪,但效果很好。您确实需要添加更多细节和一些代码。我们现在什么都不知道。无论是屏幕上的磁贴数量、大小,还是变化速度。。。。我不明白你是怎么画的。这一切很难用语言来解释。在这里发布相关部分,理想情况下是一个工作示例。使用VisualVM等探查器,找出花费时间的地方。然后想想那些缓慢的操作——它们可以被更快的操作所取代吗?它们都是必需的吗?例如,我曾经通过保存一个改变过的细胞列表,并只绘制这些细胞,极大地加快了生命游戏程序的速度。我理解你说的话,但有些事情我不理解或我不能做。例如,我如何使用内置的图形创建一个有效的渲染方法,我的意思是,有一个类在使用图形,我如何从外部访问它?或者如何获得像素的颜色?您应该使用渲染循环中的函数来渲染对象。假设您在循环周期开始时创建图形对象。然后需要通过函数将其传递给对象,然后才能在循环结束时释放它。如果你想让我解释得更详细,我可以编辑我的答案给你看。如果你能解释给我听,我会很高兴的。
import java.awt.*;
abstract class AbstractEngine implements Runnable {
Renderer renderer;
Dimension dimension;
boolean running;
Thread thread;
public AbstractEngine(Renderer renderer) {
this.renderer = renderer;
dimension = renderer.dim;
}
public void start() {
if (running) return;
running = true;
thread = new Thread(this, "Tread");
thread.start();
}
public void stop() {
if(!running) return;
running = false;
try {
System.exit(1);
thread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
public abstract void handleInput();
public abstract void update();
public abstract void render();
@Override
public void run() {
final int UPS = 120;
final int FPS = 60;
long initialTime = System.nanoTime();
final double timeU = 1000000000 / UPS;
final double timeF = 1000000000 / FPS;
double deltaU = 0, deltaF = 0;
int frames = 0, ticks = 0;
long timer = System.currentTimeMillis();
while (running) {
long currentTime = System.nanoTime();
deltaU += (currentTime - initialTime) / timeU;
deltaF += (currentTime - initialTime) / timeF;
initialTime = currentTime;
if (deltaU >= 1) {
handleInput();
update();
ticks++;
deltaU--;
}
if (deltaF >= 1) {
render();
renderer.render();
frames++;
deltaF--;
}
if (System.currentTimeMillis() - timer > 1000) {
frames = 0;
ticks = 0;
timer += 1000;
}
}
}
}