Java 2D графика игры

В следующем семестре у нас есть модуль для создания Java-приложений в команде. Требование модуля - сделать игру. Во время рождественских каникул я немного потренировался, но не могу найти лучший способ рисовать графику.

Я использую объект Java Graphics2D для рисования фигур на экране и вызываю repaint () 30 раз в секунду, но это ужасно мерцает. Есть ли лучший способ рисовать высокопроизводительную 2D-графику на Java?

15
задан wchargin 23 March 2013 в 03:10
поделиться

4 ответа

Что вы хотите сделать, так это создать компонент холста с BufferStrategy и выполнить его рендеринг. Код ниже должен показать вам, как это работает. Я извлек код из своего собственного движка. выше здесь .

Производительность зависит исключительно от того, что вы хотите рисовать, в моих играх в основном используются изображения. Примерно с 1500 из них я все еще выше 200 кадров в секунду при 480x480. И всего со 100 изображениями я получаю 6k FPS при отключении ограничения кадров.

Я создал небольшую игру (в этой игре одновременно отображается около 120 изображений), и ее можно найти здесь (да, приведенный ниже подход также отлично работает как апплет.)

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.WindowConstants;

public class Test extends Thread {
    private boolean isRunning = true;
    private Canvas canvas;
    private BufferStrategy strategy;
    private BufferedImage background;
    private Graphics2D backgroundGraphics;
    private Graphics2D graphics;
    private JFrame frame;
    private int width = 320;
    private int height = 240;
    private int scale = 1;
    private GraphicsConfiguration config =
            GraphicsEnvironment.getLocalGraphicsEnvironment()
                .getDefaultScreenDevice()
                .getDefaultConfiguration();

    // create a hardware accelerated image
    public final BufferedImage create(final int width, final int height,
            final boolean alpha) {
        return config.createCompatibleImage(width, height, alpha
                ? Transparency.TRANSLUCENT : Transparency.OPAQUE);
    }

    // Setup
    public Test() {
        // JFrame
        frame = new JFrame();
        frame.addWindowListener(new FrameClose());
        frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        frame.setSize(width * scale, height * scale);
        frame.setVisible(true);

        // Canvas
        canvas = new Canvas(config);
        canvas.setSize(width * scale, height * scale);
        frame.add(canvas, 0);

        // Background & Buffer
        background = create(width, height, false);
        canvas.createBufferStrategy(2);
        do {
            strategy = canvas.getBufferStrategy();
        } while (strategy == null);
        start();
    }

    private class FrameClose extends WindowAdapter {
        @Override
        public void windowClosing(final WindowEvent e) {
            isRunning = false;
        }
    }

    // Screen and buffer stuff
    private Graphics2D getBuffer() {
        if (graphics == null) {
            try {
                graphics = (Graphics2D) strategy.getDrawGraphics();
            } catch (IllegalStateException e) {
                return null;
            }
        }
        return graphics;
    }

    private boolean updateScreen() {
        graphics.dispose();
        graphics = null;
        try {
            strategy.show();
            Toolkit.getDefaultToolkit().sync();
            return (!strategy.contentsLost());

        } catch (NullPointerException e) {
            return true;

        } catch (IllegalStateException e) {
            return true;
        }
    }

    public void run() {
        backgroundGraphics = (Graphics2D) background.getGraphics();
        long fpsWait = (long) (1.0 / 30 * 1000);
        main: while (isRunning) {
            long renderStart = System.nanoTime();
            updateGame();

            // Update Graphics
            do {
                Graphics2D bg = getBuffer();
                if (!isRunning) {
                    break main;
                }
                renderGame(backgroundGraphics); // this calls your draw method
                // thingy
                if (scale != 1) {
                    bg.drawImage(background, 0, 0, width * scale, height
                            * scale, 0, 0, width, height, null);
                } else {
                    bg.drawImage(background, 0, 0, null);
                }
                bg.dispose();
            } while (!updateScreen());

            // Better do some FPS limiting here
            long renderTime = (System.nanoTime() - renderStart) / 1000000;
            try {
                Thread.sleep(Math.max(0, fpsWait - renderTime));
            } catch (InterruptedException e) {
                Thread.interrupted();
                break;
            }
            renderTime = (System.nanoTime() - renderStart) / 1000000;

        }
        frame.dispose();
    }

    public void updateGame() {
        // update game logic here
    }

    public void renderGame(Graphics2D g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, width, height);
    }

    public static void main(final String args[]) {
        new Test();
    }
}
17
ответ дан 1 December 2019 в 02:10
поделиться

Мерцание происходит из-за того, что вы пишете прямо на экран. Используйте буфер для рисования, а затем записывайте весь экран за один раз. Это Двойная буферизация , о которой вы, возможно, слышали раньше. Здесь - самая простая возможная форма.

public void paint(Graphics g)
{

    Image image = createImage(size + 1, size + 1);
    Graphics offG = image.getGraphics();
    offG.setColor(Color.BLACK);
    offG.fillRect(0, 0, getWidth(), getHeight());
    // etc

См. Использование внеэкранной графики offG . Создание внеэкранного изображения дорого, поэтому я бы посоветовал создавать его только при первом вызове.

Есть и другие области, в которых вы можете улучшить это дальше, например, создание совместимого изображения , используя отсечение ] и т. д. Для более точной настройки управления анимацией вам следует заглянуть в активный рендеринг .

Там есть приличная страница, которую я добавил в закладки, обсуждая обучающие материалы по играм здесь .

Удачи!

8
ответ дан 1 December 2019 в 02:10
поделиться

Java OpenGL ( JOGL ) является односторонним.

3
ответ дан 1 December 2019 в 02:10
поделиться

Я думаю, вы сделали переопределение из paint (Graphics g) ? Это не лучший способ. Используйте тот же код, но в paintComponent (Graphics g) вместо paint (Graphics g) .

Метка, которую вы можете найти, - это doublebuffer . Это то, что будет сделано автоматически при переопределении paintComponent .

2
ответ дан 1 December 2019 в 02:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: