Почему при создании createBufferStrategy(3) возникает нелегальное исключение invalidArgumentException?

Я начинаю создавать игру с использованием Java. В моем методе render() у меня есть буферная стратегия с тремя буферами. Однако, когда я запускаю его, я получаю эту ошибку.

Exception in thread "Thread-1" java.lang.IllegalArgumentException: Width (0) and height (0) cannot be <= 0
    at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1016)
    at sun.java2d.opengl.CGLGraphicsConfig.createCompatibleImage(CGLGraphicsConfig.java:200)
    at java.awt.GraphicsConfiguration.createCompatibleImage(GraphicsConfiguration.java:178)
    at sun.awt.image.SunVolatileImage.getBackupImage(SunVolatileImage.java:236)
    at sun.awt.image.VolatileSurfaceManager.getBackupSurface(VolatileSurfaceManager.java:263)
    at sun.awt.image.VolatileSurfaceManager.initialize(VolatileSurfaceManager.java:126)
    at sun.awt.image.SunVolatileImage.<init>(SunVolatileImage.java:88)
    at sun.awt.image.SunVolatileImage.<init>(SunVolatileImage.java:98)
    at sun.awt.image.SunVolatileImage.<init>(SunVolatileImage.java:109)
    at sun.java2d.opengl.CGLGraphicsConfig.createBackBufferImage(CGLGraphicsConfig.java:356)
    at sun.lwawt.LWCanvasPeer.createBuffers(LWCanvasPeer.java:62)
    at java.awt.Component$FlipBufferStrategy.createBuffers(Component.java:4016)
    at java.awt.Component$FlipBufferStrategy.<init>(Component.java:3956)
    at java.awt.Component$FlipSubRegionBufferStrategy.<init>(Component.java:4479)
    at java.awt.Component.createBufferStrategy(Component.java:3833)
    at java.awt.Canvas.createBufferStrategy(Canvas.java:194)
    at java.awt.Component.createBufferStrategy(Component.java:3756)
    at java.awt.Canvas.createBufferStrategy(Canvas.java:169)
    at com.game.framework.Game.render(Game.java:79)
    at com.game.framework.Game.run(Game.java:58)
    at java.lang.Thread.run(Thread.java:744)

Вот полный класс с методом render():

package com.game.framework;

import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;

public class Game extends Canvas implements Runnable {
    private static final long serialVersionUID = 2956560507546666478L;
    private Thread thread;
    private boolean running = false;
    public static final int WIDTH = 640, HEIGHT = WIDTH / 12 * 9;

    public Game() {
        new Window(WIDTH, HEIGHT, "UNKNOWN", this);
    }

    public synchronized void start() {
        thread = new Thread(this);
        thread.start();
        running = true;
    }

    public synchronized void stop() {
        try {
            thread.join();
            running = false;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void run() {
        long lastTime = System.nanoTime();
        long timer = System.currentTimeMillis();

        double amountOfTicks = 60.0;
        double ns = 1000000000 / amountOfTicks;
        double delta = 0;

        int frames = 0;

        while (running) {
            long now = System.nanoTime();

            delta += (now - lastTime) / ns;
            lastTime = now;

            while (delta >= 1) {
                tick();
                delta--;
            }

            if (running)
                render();
            frames++;

            if (System.currentTimeMillis() - timer > 1000) {
                timer += 1000;
                System.out.println("FPS: " + frames);
                frames = 0;
            }
        }
        stop();
    }

    private void tick() { }

    private void render() {
        BufferStrategy bs = this.getBufferStrategy();
        if (bs == null) {
            this.createBufferStrategy(3);
            return;
        }
        Graphics g = bs.getDrawGraphics();
    }

    public static void main(String args[]) {
        new Game();
    }
}

Вот другой класс в моей игре:

package com.game.framework;

import java.awt.Canvas;
import java.awt.Dimension;

import javax.swing.JFrame;

public class Window extends Canvas {
    private static final long serialVersionUID = 70937078350109973L;

    public Window (int width, int height, String title, Game game) {
        JFrame frame = new JFrame(title);
        frame.setPreferredSize(new Dimension(width, height));
        frame.setMaximumSize(new Dimension(width, height));
        frame.setMinimumSize(new Dimension(width, height));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);
        frame.add(game);
        game.start();
    }
}

Я не уверен, в чем проблема, но это довольно неприятно, и я не могу продолжать, пока проблема не будет решена. Спасибо за вашу помощь!

1 ответ

Решение

После некоторых исследований я нашел возможную причину, по которой ваш код работает для меня, но не для вас. Я использую Direct 3D для рендеринга, а вы используете OpenGL.

Классы D3D также используют этот код для создания BufferStrategy:

public VolatileImage createBackBuffer(WComponentPeer peer) {
    Component target = (Component)peer.getTarget();
    // it is possible for the component to have size 0x0, adjust it to
    // be at least 1x1 to avoid IAE
    int w = Math.max(1, target.getWidth());
    int h = Math.max(1, target.getHeight());
    return new SunVolatileImage(target, w, h, Boolean.TRUE);
}

Как видите, он устанавливает ширину и высоту как минимум 1 чтобы избежать IllegalArgumentException,

Версия OpenGL этого класса не:

@Override
public VolatileImage createBackBufferImage(Component target,
                                           long backBuffer) {
    return new SunVolatileImage(target,
                                target.getWidth(), target.getHeight(),
                                Boolean.TRUE);
}

Так что если вы все еще хотите использовать OpenGl, вам нужно обойти короткое время, когда размер холста еще не установлен ** (т.е. ширина и высота 0) самостоятельно:

private void render() {
    if (getWidth() == 0 || getHeight() == 0) {
        return;
    }
    BufferStrategy bs = this.getBufferStrategy();
    if (bs == null) {
        this.createBufferStrategy(3);
        return;
    }
    Graphics g = bs.getDrawGraphics();
}

** Кажется, что очень короткое время, когда Game холст не "имеет" его правильный размер. Это размер 0x0 есть, но позже будет обновлен до 634x449 (после примерно 3 итераций вашего игрового цикла).

Другие вопросы по тегам