Java-全屏将FPS限制为60

Java-全屏将FPS限制为60,java,fullscreen,frame-rate,Java,Fullscreen,Frame Rate,我目前正在编写一个小游戏作为辅助项目,这是我第一次尝试实现多种可能的分辨率(通过图形缩放)和全屏模式 我认为最好在开发之初就实现这一点,到目前为止,它运行良好 但我做了一个奇怪的观察。在窗口模式下,游戏按预期运行,滴答数(每秒更新次数)为60,每秒渲染次数不限 但当我切换到全屏时,它会下降到60的固定滴答声和60的固定FPS 这在我眼里很奇怪。我用来切换到全屏的命令 if (device.isFullScreenSupported()) { device.setFullScreenWin

我目前正在编写一个小游戏作为辅助项目,这是我第一次尝试实现多种可能的分辨率(通过图形缩放)和全屏模式

我认为最好在开发之初就实现这一点,到目前为止,它运行良好

但我做了一个奇怪的观察。在窗口模式下,游戏按预期运行,滴答数(每秒更新次数)为60,每秒渲染次数不限

但当我切换到全屏时,它会下降到60的固定滴答声和60的固定FPS

这在我眼里很奇怪。我用来切换到全屏的命令

if (device.isFullScreenSupported()) {
    device.setFullScreenWindow(frame);
}
项目文件:

窗口类:(启动窗口并在全屏和窗口之间切换)

游戏类:(游戏循环和主要方法)

KeyInputHandler:(仅用于测试,用于在全屏和窗口之间切换)


它是与全屏模式有关,还是与我的代码有所不同?希望看到一些对我有帮助的回复。:)

这难道与
double amountOfTicks=60.0没有关系吗…?这只是确定每秒的滴答声数量。每秒的渲染量(调用渲染方法的频率)不受限制。这正是我眼中的奇怪之处。它不应该限制为60 FPS。但是您有一个基于
ns
delta
循环,它基于
amountOfTicks
,它控制何时调用
render
(和
frames++
)调用…(并不是说您的delta循环基于实际时间值)但是delta循环不限制FPS,只限制滴答声。在这个屏幕截图上,你可以看到FPS在窗口模式下是不受限制的:你可能被全屏锁定在硬件刷新率上——看,这只是个人的事情,我不喜欢这种类型的游戏循环。在没有其他任何东西的情况下,我的CPU高达70%,只是在游戏循环中空转,这是大量的CPU浪费
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;

import javax.swing.JFrame;

public class Window extends Canvas {

    private JFrame frame;
    private int height;
    private int width;
    private double scaleX;
    private double scaleY;
    private String title;
    private Game game;
    private boolean fullscreen = false;
    private GraphicsDevice device;
    private GraphicsEnvironment ge;

    public Window(int width, int height, double scaleX, double scaleY, String title, Game game) {
        this.width = width;
        this.height = height;
        this.scaleX = scaleX;
        this.scaleY = scaleY;
        this.title = title;
        this.game = game;
        init();
    }

    private void init() {
        frame = new JFrame(title);
        ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        device = ge.getDefaultScreenDevice();
        frame.getContentPane().setMinimumSize(new Dimension((int) (width * scaleX), (int) (height * scaleY)));
        frame.getContentPane().setMaximumSize(new Dimension((int) (width * scaleX), (int) (height * scaleY)));
        frame.getContentPane().setPreferredSize(new Dimension((int) (width * scaleX), (int) (height * scaleY)));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(this, BorderLayout.CENTER);
        frame.setUndecorated(false);
        frame.setResizable(false);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public void updateSize(int width, int height, double scaleX, double scaleY) {
        this.width = width;
        this.height = height;
        this.scaleX = scaleX;
        this.scaleY = scaleY;
        frame.getContentPane().setMinimumSize(new Dimension((int) (width * scaleX), (int) (height * scaleY)));
        frame.getContentPane().setMaximumSize(new Dimension((int) (width * scaleX), (int) (height * scaleY)));
        frame.getContentPane().setPreferredSize(new Dimension((int) (width * scaleX), (int) (height * scaleY)));
        frame.pack();
    }

    public void setFullscreen() {
        if (!fullscreen) {
            game.setRender(false);
            frame.dispose();
            frame.setUndecorated(true);
            frame.setVisible(true);
            game.setRender(true);
            if (device.isFullScreenSupported()) {
                device.setFullScreenWindow(frame);
            }
            this.requestFocus();
            fullscreen = true;
        }
    }

    public void setWindowed() {
        if (fullscreen) {
            game.setRender(false);
            frame.dispose();
            frame.setUndecorated(false);
            frame.setVisible(true);
            game.setRender(true);
            device.setFullScreenWindow(null);
            this.requestFocus();
            fullscreen = false;
        }
    }
} 
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;

public class Game implements Runnable {

    // Version number
    private static final String Version = "0.1";
    // The game title
    public static final String TITLE = "Tapper";
    // The default width and height the game is build upon
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;
    // The scaling factor depending on the chosen resolution
    private double scaleX = 1.0d;
    private double scaleY = 1.0d;

    // Game Window
    private Window win;

    // FPS and tick variables to display in debug hud
    private int fps = 0;
    private int ticks = 0;

    // Pixel width of a string in a certain font
    private int stringLen;

    // Boolean tick, running and rendering variables
    private boolean running = false;
    private boolean tick = true;
    private boolean render = true;
    // Main game Thread
    private Thread gameThread;

    // Handles the Keyboard Input
    private KeyInputHandler ki;

    // Jugst for testing
    private double x;

    public Game() {
        win = new Window(WIDTH, HEIGHT, scaleX, scaleY, TITLE, this);
    }

    private void init() {
        // Initiate Handlers and objects
        ki = new KeyInputHandler(this);
        win.addKeyListener(ki);
    }

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

    public synchronized void stop() {
        if (!running) {
            return;
        }
        running = false;
        try {
            gameThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void tick() {
        // Lets objects and handlers tick
        x = x + 0.09;
    }

    private void drawDebugHud(Graphics2D g2d, String fontName) {
        Font font = new Font(fontName, Font.BOLD, 12);
        g2d.setFont(font);
        g2d.drawString("G - Switch to Fullscreen", 5, HEIGHT - g2d.getFontMetrics().getHeight() * 6);
        g2d.drawString("H - Switch to Windowed", 5, HEIGHT - g2d.getFontMetrics().getHeight() * 5);
        g2d.drawString("Ticks:" + ticks, 5, HEIGHT - g2d.getFontMetrics().getHeight());
        g2d.drawString("FPS:" + fps, 5, HEIGHT);
    }

    private void drawVersionNumber(Graphics2D g2d, String fontName) {
        g2d.setFont(new Font(fontName, Font.BOLD, 12));
        stringLen = (int) g2d.getFontMetrics().getStringBounds("v" + Version, g2d).getWidth();
        g2d.drawString("v" + Version, WIDTH - stringLen, HEIGHT);
    }

    private void render() {
        BufferStrategy bs = win.getBufferStrategy();
        if (bs == null) {
            win.createBufferStrategy(3);
            return;
        }

        Graphics2D g2d = (Graphics2D) bs.getDrawGraphics();
        /* DRAW EVERYTHING BELOW HERE */
        g2d.scale(scaleX, scaleY);

        // BACKGROUND
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, WIDTH, HEIGHT);

        // Everything else
        g2d.setColor(Color.BLACK);
        g2d.fillRect((int) x, (int) x, 40, 40);

        // Debug hud
        drawDebugHud(g2d, "Arial");
        drawVersionNumber(g2d, "Arial");

        /* DRAW EVERYTHING ABOVE HERE */

        g2d.dispose();
        bs.show();
    }

    public void run() {
        win.requestFocus();
        init();
        long lastTime = System.nanoTime();
        double amountOfTicks = 60.0;
        double ns = 1000000000 / amountOfTicks;
        double delta = 0;
        int updates = 0;
        int frames = 0;
        long timer = System.currentTimeMillis();
        while (running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while (delta >= 1) {
                if (tick == true) {
                    tick();
                    updates++;
                }
                delta--;
            }
            if (render) {
                render();
                frames++;
            }

            if (System.currentTimeMillis() - timer > 1000) {
                fps = frames;
                ticks = updates;
                timer += 1000;
                frames = 0;
                updates = 0;
            }
        }
        stop();
    }

    public Thread getGameThread() {
        return gameThread;
    }

    public void setTick(boolean tick) {
        this.tick = tick;
    }

    public boolean isTick() {
        return tick;
    }

    public double getScaleX() {
        return scaleX;
    }

    public void setScaleX(double d) {
        this.scaleX = d;
    }

    public double getScaleY() {
        return scaleY;
    }

    public void setScaleY(double d) {
        this.scaleY = d;
    }

    public static int getDefaultWidth() {
        return WIDTH;
    }

    public static int getDefaultHeight() {
        return HEIGHT;
    }

    public Window getWin() {
        return win;
    }

    public boolean isRender() {
        return render;
    }

    public void setRender(boolean render) {
        this.render = render;
    }

    public static void main(String[] args) {
        new Game().start();
    }
}   
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

// Handles the Keyboard Input

public class KeyInputHandler implements KeyListener{

    private Game game;
    private Dimension screenSize;

    public KeyInputHandler(Game game) {
        this.game = game;
    }

    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();   

        if (key == KeyEvent.VK_G) {
            screenSize = Toolkit.getDefaultToolkit().getScreenSize();
            // calculating and setting the scaling factor
            game.setScaleX(screenSize.getWidth() / Game.getDefaultWidth());
            game.setScaleY(screenSize.getHeight() / Game.getDefaultHeight());
            // Switch to Windowed Mode
            game.getWin().setFullscreen();
            // Refresh window size
            game.getWin().updateSize(Game.getDefaultWidth(), Game.getDefaultHeight(), game.getScaleX(), game.getScaleY());
        }

        if (key == KeyEvent.VK_H) {
            // Calculating and setting and setting the scaling factor
            game.setScaleX(800d / Game.getDefaultWidth());
            game.setScaleY(600d / Game.getDefaultHeight());
            // Switch to Windowed Mode
            game.getWin().setWindowed();
            // Refresh window size
            game.getWin().updateSize(Game.getDefaultWidth(), Game.getDefaultHeight(), game.getScaleX(), game.getScaleY());
        }
    }

    public void keyReleased(KeyEvent e) {}

    public void keyTyped(KeyEvent e) {}
}