我目前正在编写一个小型游戏作为侧面项目,并且我第一次尝试实现多种可能的分辨率(通过图形比例)和全屏模式。
我认为最好在开发开始时实现这一点,到目前为止它工作正常。
但我做了一个奇怪的观察。当处于窗口模式时,游戏按预期运行,其中滴答(每秒更新量)为60,无限制FPS(每秒渲染)速率。
但是当我切换到全屏时,它会下降到固定的60和固定FPS为60 。
在我眼里这很奇怪。我用来切换到全屏的命令。
if (device.isFullScreenSupported()) {
device.setFullScreenWindow(frame);
}
项目文件:
窗口类:(启动窗口并在全屏和窗口之间切换)
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();
}
}
KeyInputHandler :(仅用于测试目的,用于在全屏和窗口之间切换)
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) {}
}
是否与全屏模式有关或者我的代码中有什么不同之处?很想看到一些可能帮助我的回复。 :)