简体   繁体   English

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

[英]How can I optimize my 2d java game rendering?

My problem is, I have a game with a random generated map and it has only 30-40 fps because of the number of blocks.(You can imagine my game like a 2d minecraft).我的问题是,我有一个随机生成地图的游戏,由于块的数量,它只有 30-40 fps。(你可以把我的游戏想象成一个 2d minecraft)。

First I search for the first tile what is in screen.首先,我搜索屏幕中的第一个图块。 Then start a loop render tile next tile... until I reach the last tile what you can see.然后在下一个图块开始循环渲染图块......直到我到达最后一个图块你能看到什么。

(I don't use any of the Java classes like graphics/graphics2d I use my own code what is an int[] with the rows of teh screen in it and when I render a tile I change the int[x+y*width] position of the screen to the correct pixel of the block) (我不使用任何 Java 类,如 graphics/graphics2d 我使用我自己的代码什么是 int[],其中包含屏幕行,当我渲染一个 tile 时,我更改了 int[x+y*width ] 屏幕位置到块的正确像素)

I think logically this is the best way to render my map and i don't understend why is the low fps.我认为从逻辑上讲这是渲染我的地图的最佳方式,我不明白为什么 fps 低。 I am wrong or I need to search for some other problem in my code?我错了还是我需要在我的代码中搜索其他问题? Or there is any better rendering method?或者有什么更好的渲染方法?

If I skip the rendering of the world, there is stabile 120 fps what is capped there.如果我跳过世界的渲染,那里有稳定的 120 fps 上限。 What can be the problem?可能是什么问题?

I know you dont use the Gaphics functions and choose to manipulate a pixel-array instead.我知道您不使用 Gaphics 函数,而是选择操作像素阵列。

Try to change your game to use the Graphics object since there is no (easy and efficient) way around it.尝试将您的游戏更改为使用 Graphics 对象,因为没有(简单有效的)方法可以绕过它。 If you still choose not to do so, try to add如果您仍然选择不这样做,请尝试添加

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

at the very begining of your code just after在您的代码的最开始之后

public static void main(String[] args) {

I tried to do it the same way as you back when i first made simple games but you later come to realize that the built-in Graphics functions are vastly superior in performance and ease of use.当我第一次制作简单的游戏时,我试图以与您相同的方式来做,但您后来意识到内置的图形功能在性能和易用性方面都非常出色。

EDIT:编辑:

A short explanaition on how a basic game might use the Graphics object:关于基本游戏如何使用 Graphics 对象的简短说明:

Suppose you created a JFrame.假设您创建了一个 JFrame。 If you then add a class that extends from Canvas and add that to it.如果然后添加一个从 Canvas 扩展的类并将其添加到其中。 If you want to use a menu, you might even create a JPanel first and add the Canvas into the Jpanel so you can hide it more easily.如果您想使用菜单,您甚至可以先创建一个 JPanel,然后将 Canvas 添加到 Jpanel 中,以便您可以更轻松地隐藏它。

Here you have an example how we create a usable canvas:下面是我们如何创建可用画布的示例:

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

}

This class can be your base.这门课可以成为你的基地。 Its tick method is called 120 times a second and its render method can render stuff onto the screen.它的tick 方法每秒被调用120 次,它的render 方法可以将内容渲染到屏幕上。 If you are not familiar with the functions of the Graphics Object i suggest reading a little about them.如果您不熟悉图形对象的功能,我建议您阅读一些有关它们的信息。

You can't reach the Graphics object from the outside.您无法从外部访问 Graphics 对象。 You need to call the render functions from inside the games render function before the Graphics object gets disposed.在处理 Graphics 对象之前,您需要从游戏渲染函数内部调用渲染函数。 Try to seperate your game logic from the render functions.尝试将您的游戏逻辑与渲染功能分开。

I use a slightly different architecture than what's stated above.我使用的架构与上述架构略有不同。 I actually create a separate Renderer object which is basically a deferred renderer.我实际上创建了一个单独的 Renderer 对象,它基本上是一个延迟渲染器。

It's structured like this它的结构是这样的

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 :渲染器.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 : 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 :抽象引擎.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;
            }
        }
    }
}

Input is handled in the extended Engine class.输入在扩展的 Engine 类中处理。 I have a MouseState object that holds mouse positions and button state.我有一个保存鼠标位置和按钮状态的 MouseState 对象。 I then create a MouseAdapter that updates the date in MouseState and attach it to the renderer.然后我创建一个 MouseAdapter 来更新 MouseState 中的日期并将其附加到渲染器。 It's a bit weird, I know, but works nicely.我知道这有点奇怪,但效果很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM