如何优化我的2D游戏渲染?

时间:2017-08-19 22:36:16

标签: java performance render tile

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

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

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

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

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

2 个答案:

答案 0 :(得分: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次,其渲染方法可以将内容渲染到屏幕上。 如果你不熟悉图形对象的功能,我建议你阅读一下它们。

您无法从外部访问Graphics对象。您需要在处理Graphics对象之前从游戏渲染功能中调用渲染函数。尝试从渲染功能中分离游戏逻辑。

答案 1 :(得分:0)

我使用的架构与上面所述略有不同。实际上,我创建了一个单独的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();                
            }
        });
    }
}

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

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

输入在扩展Engine类中处理。我有一个MouseState对象,该对象保存鼠标位置和按钮状态。然后,我创建一个MouseAdapter来更新MouseState中的日期并将其附加到渲染器。我知道这有点奇怪,但是效果很好。