如何优化2d java游戏渲染?

如何优化2d java游戏渲染?,java,performance,render,tile,Java,Performance,Render,Tile,我的问题是,我有一个随机生成地图的游戏,由于方块的数量,它只有30-40 fps。你可以想象我的游戏就像一个2d雷击机 首先,我搜索屏幕中的第一个磁贴。然后开始循环渲染平铺下一个平铺。。。直到我到达你能看到的最后一块瓷砖 我不使用任何Java类,比如graphics/graphics2d,我使用我自己的代码什么是int[],其中包含屏幕的行,当我渲染一个平铺时,我将屏幕的int[x+y*width]位置更改为块的正确像素 我认为从逻辑上讲,这是渲染贴图的最佳方式,我不理解为什么fps较低。我错了

我的问题是,我有一个随机生成地图的游戏,由于方块的数量,它只有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;
            }
        }
    }
}