【问题标题】:How can I optimize my 2d java game rendering?如何优化我的 2d java 游戏渲染?
【发布时间】:2018-01-28 08:00:19
【问题描述】:

我的问题是,我有一个随机生成地图的游戏,由于块数的关系,它只有 30-40 fps。(你可以把我的游戏想象成一个 2d minecraft)。

首先我搜索屏幕中的第一个图块。然后开始循环渲染瓦片下一个瓦片......直到我到达你可以看到的最后一个瓦片。

(我不使用任何 Java 类,例如 graphics/graphics2d 我使用自己的代码什么是 int[],其中包含屏幕行,当我渲染图块时,我更改了 int[x+ y*width] 屏幕位置到块的正确像素)

我认为从逻辑上讲这是渲染我的地图的最佳方式,我不明白为什么 fps 低。我错了还是我需要在我的代码中搜索其他问题?或者有什么更好的渲染方式?

如果我跳过世界的渲染,那里的上限是稳定的 120 fps。可能是什么问题?

【问题讨论】:

  • 您确实需要添加更多细节和一些代码。我们现在一无所知。屏幕上的图块数量,它们的大小,以及它们变化的速度……我不明白你是怎么画的。这一切都很难用语言来解释。在此处发布相关部分,最好是一个工作示例。
  • 使用诸如VisualVM之类的分析器,找出时间花在了哪里。然后想想缓慢的操作——它们可以用更快的东西代替吗?它们都是必需的吗(例如,我曾经通过保留更改单元格的列表并仅绘制这些单元格来极大地加快生命游戏程序的速度)。

标签: java performance render tile


【解决方案1】:

我知道您不使用 Gaphics 函数而是选择操作像素阵列。

尝试更改您的游戏以使用 Graphics 对象,因为没有(简单而有效的)解决方法。如果你仍然选择不这样做,请尝试添加

System.setProperty("sun.java2d.opengl", "true");

在代码的最开头,紧接在

之后
public static void main(String[] args) {

当我第一次制作简单的游戏时,我尝试过和你一样的方式,但后来你意识到内置的图形功能在性能和易用性方面非常优越。

编辑:

关于基本游戏如何使用 Graphics 对象的简短说明:

假设您创建了一个 JFrame。如果您随后添加一个从 Canvas 扩展的类并将其添加到其中。如果您想使用菜单,您甚至可以先创建一个 JPanel,然后将 Canvas 添加到 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 方法可以将内容渲染到屏幕上。 如果您不熟悉图形对象的功能,我建议您阅读一下它们。

您无法从外部接触到 Graphics 对象。在处理 Graphics 对象之前,您需要从游戏渲染函数内部调用渲染函数。尝试将您的游戏逻辑与渲染功能分开。

【讨论】:

  • 我明白你在说什么,但有些事情我不明白或者我做不到。就像我如何使用内置的 Graphics 制作一个有效的渲染方法(我的意思是有一个类正在使用 Graphics,我如何从外部访问它)?或者我怎样才能得到一个像素的颜色?
  • 您应该使用渲染循环内的函数来渲染您的对象。假设您在循环周期开始时创建图形对象。然后,您需要通过函数将其传递给对象,然后才能在循环结束时处理它。如果你想让我更详细地解释它,我可以编辑我的答案给你看。
  • 如果您向我解释一下,我会很高兴的。
【解决方案2】:

我使用的架构与上述架构略有不同。我实际上创建了一个单独的 Renderer 对象,它基本上是一个延迟渲染器。

结构是这样的

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();                
            }
        });
    }
}

渲染器.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);
    }
}

Drawable.java:

import java.awt.*;
abstract class Drawable {

    protected Stroke stroke;
    protected Color color, stroke_color;
    public Dimension size;
   
    public float sub_pixel_x;
    public float sub_pixel_y;

    public Drawable(Color color) {

        setColor(color);
        setStrokeColor(new Color(0));

        sub_pixel_x = 0.0f;
        sub_pixel_y = 0.0f;
        size = new Dimension(10, 10);

    }

    public void setStroke(float width) {
        stroke = new BasicStroke(width);
    }
   
    public void noStroke() {
        stroke = null;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public void setStrokeColor(Color color) {
        this.stroke_color = color;
    }

    public void setLocation(float x, float y) {
        sub_pixel_x = x;
        sub_pixel_y = y;
    }

    
    protected abstract void paint(Graphics2D g2d);
    
}

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 中的日期并将其附加到渲染器。我知道这有点奇怪,但效果很好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多