我游戏中的 FPS 计数器 (Java)

FPS Counter in my game (Java)

我需要在我的游戏 (Pong) 中添加一个有效的 FPS 计数器。要制作 FPS 计数器,我显然还需要一个游戏循环,我尝试分别制作一个游戏循环和 FPS 计数器,它似乎可以正常工作。但是,我在将 FPS 计数器添加到我的 Pong 游戏时遇到了一些问题。

这是游戏的代码 loop/FPS 计数器:

import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.Timer;

public class Main extends Canvas implements Runnable{

JFrame frame;
int windowWidth, windowHeight;
Point bollXY;
int screenWidth, screenHeight;
Timer timer;
Image bildPaddel;
Image bildBollen;
int paddelY;
int paddel2Y;
boolean paddelUp, paddelDown;
Random rand;
int score, score2;
boolean bollUp, bollRight, changeDirection;
boolean paused;
int fruktDistansRand;
long time;
int fps, newfps;

private boolean running = false;
private Thread thread;



private synchronized void start() {
    if(running)
        return;

    running = true;
    thread = new Thread(this);
    thread.start();
}

private synchronized void stop(){
    if(!running)
        return;

    running = false;
    try {
        thread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.exit(1);
}

public static void main(String args[]) {

    Main main = new Main();

    JFrame frame = new JFrame();
    frame.add(main);
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);

    main.start();
}


@Override
public void run() {
    long lastTime = System.nanoTime();
    final 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 += (lastTime - now) / ns;
        lastTime = now;
        if(delta >= 1) {
            updates++;
            delta--;
        }
        frames++;

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

我尝试将 FPS 计数器添加到游戏中,但由于某种原因它不起作用。如果您知道如何制作更好的 FPS 计数器 and/or 如何将其添加到游戏中,请提供帮助。顺便说一下,我不希望 FPS 锁定在 60。

在这个回答中,我没有找到您的计数器问题的原因。这对我来说相当复杂。但我会给你一些代码来帮助你开始:

这是 FPS 计数器的代码:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;

public class FPSCounter implements ActionListener {
    private final Timer resetTimer;
    private int current, last;

    public FPSCounter() {
        resetTimer = new Timer(1000, this);
    }

    public synchronized void start() {
        resetTimer.start();
        current = 0;
        last = -1;
    }

    public synchronized void stop() {
        resetTimer.stop();
        current = -1;
    }

    public synchronized void frame() {
        ++current;
    }

    @Override
    public synchronized void actionPerformed(final ActionEvent e) {
        last = current;
        current = 0;
    }

    public synchronized int get() {
        return last;
    }

    public static void main(final String[] args) {
        final FPSCounter cnt = new FPSCounter();
        cnt.start();
        new Timer(2000, e -> { System.out.println(cnt.get()); }).start();
        while (true)
            cnt.frame();
//        cnt.stop();
    }
}

main 方法测试计数器是否工作(每 2 秒打印一次每秒的空循环数)。

你可以这样使用它:

  1. 在您的游戏中添加 private FPSCounter fpscnt; class。
  2. 像这样在游戏的构造函数中构建对象:fpscnt = new FPSCounter();
  3. 在游戏开始前启动计数器,调用:fpscnt.start();。例如,您可以在游戏的构造函数中调用它。
  4. 现在,每画一帧,都要调用计数器的frame()方法。接下来我要示范...
  5. 现在,无论什么时候想要获取每秒的帧数,都需要调用计数器的get()方法。接下来我要示范...

现在,在游戏的 paintComponent() 方法中,打印 frames/sec 数字的行应如下所示:

fpscnt.frame(); //Because this is a new frame paint operation.
g.setFont(new Font("Arial", Font.BOLD, 25));
g.drawString("" + fpscnt.get(), 5, 22); //Paint the FPS.

:
get() 方法返回的每个值都会延迟 1 秒。我的意思是,如果你看到 64 fps,那么 64 就是最后(过去)秒的帧。

所以您的游戏 Main class 应该如下所示:

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class Main extends JPanel implements KeyListener, ActionListener{
    public static class FPSCounter implements ActionListener {
        private final Timer resetTimer;
        private int current, last;

        public FPSCounter() {
            resetTimer = new Timer(1000, this);
        }

        public synchronized void start() {
            resetTimer.start();
            current = 0;
            last = -1;
        }

        public synchronized void stop() {
            resetTimer.stop();
            current = -1;
        }

        public synchronized void frame() {
            ++current;
        }

        @Override
        public synchronized void actionPerformed(final ActionEvent e) {
            last = current;
            current = 0;
        }

        public synchronized int get() {
            return last;
        }
    }


// Variabler
JFrame frame;
int windowWidth, windowHeight;
Point ballXY;
int screenWidth, screenHeight;
Timer timer;
Image imagePaddle;
Image imageBall;
int paddleY;
int paddle2Y;
boolean paddelUp, paddelDown;
Random rand;
int score, score2;
boolean ballUp, ballRight, changeDirection;
boolean paused;
int DistanceRand;
long time;
int fps, newfps;

private final FPSCounter fpscnt; //added line (step 1).

// Konstruktor
public Main(){
    // Definera variabler
    frame = new JFrame();
    imagePaddle = new ImageIcon("src/images/Player.png").getImage();
    imageBall = new ImageIcon("src/images/Pong.png").getImage();
    ballXY = new Point(673, 352);
    paddleY = 312;
    paddle2Y = 312;
    paddelUp = false;
    rand = new Random();
    score = 0;
    score2 = 0;
    ballUp = false;
    ballRight = false;
    changeDirection = false;
    paused = false;
    DistanceRand = 0;
    time = System.currentTimeMillis();
    fps = 0;
    newfps = 0;

    // Lyssnare
    frame.addKeyListener(this);

    // Bygga fönstret

    frame.add(this);

    // Obligatoriska egenskaper
    frame.setTitle("Pong");
    frame.setSize(1366, 724);
    frame.setVisible(true);
    frame.setLocationRelativeTo(null);
    frame.setResizable(false);
    this.setBackground(Color.BLACK);
    frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
    ballUp = true;
    ballRight = true;
    changeDirection = false;
    ballXY = new Point();

    frame.add(this);

    timer = new Timer(5, this);
    timer.start();

    fpscnt = new FPSCounter(); //added line (step 2).
    fpscnt.start(); //added line (step 3).
}
// Metoder
public static void main(String[] args) {
    new Main();
}

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);

    g.drawImage(imageBall, ballXY.x, ballXY.y, null);
    g.drawImage(imagePaddle, 50, paddleY, null);
    g.drawImage(imagePaddle, 1300, paddle2Y, null);
    g.setFont(new Font("Arial", Font.BOLD, 100));
    g.setColor(Color.WHITE);
    g.drawString(score2 + "", 1002, 100);
    g.drawString(score + "", 314, 100);

    if(paused){ 
        g.setFont(new Font("Arial", Font.BOLD, 80));
        g.drawString("PAUSED", 510, 370);
        timer.stop();
    }
    if(System.currentTimeMillis() - time >= 1000){
        time = System.currentTimeMillis();
        fps = newfps;
        newfps = 0;
    }
    else{
        newfps++;
    }
    fpscnt.frame(); //added line (step 4).
    g.setFont(new Font("Arial", Font.BOLD, 25));
    g.drawString("" + fpscnt.get(), 5, 22); //added line (step 5).
}

public void AI(){
    DistanceRand = rand.nextInt(200) + 960;
    if(ballXY.x > DistanceRand && ballXY.x < 1380 && ballRight && paddle2Y > 0 && paddle2Y < 596){
        if(paddle2Y + 50 < ballXY.y){
            paddle2Y = paddle2Y + 3;
        }
        else{
            paddle2Y = paddle2Y - 3;
        }
        if(paddle2Y <= 0){
            paddle2Y = paddle2Y + 3;
        }
        if(paddle2Y >= 596){
            paddle2Y = paddle2Y - 3;
        }
    }
}

public void ifUp(){
    if(ballUp){
        if(changeDirection){
            if(ballXY.y < 0){
                ballUp = false;
            }
            else{
                ballXY.y = ballXY.y - 3;
            }
        }
        else{
            if(ballXY.y < 0){
                ballUp = false;
            }
            else{
                ballXY.y = ballXY.y - 3;
            }
        }
    }

    else{
        if(changeDirection){
            if(ballXY.y > 675){
                ballUp = true;
            }
            else{
                ballXY.y = ballXY.y + 3;
            }
        }
        else{
            if(ballXY.y > 675){
                ballUp = true;
            }
            else{
                ballXY.y = ballXY.y + 3;
            }
        }
    }
}

public void update(){
    if(paddelUp){
        if(paddleY > 0){
            paddleY = paddleY - 3;
        }
    }
    if(paddelDown){
        if(paddleY < 596){
            paddleY = paddleY + 3;
        }
    }

    if(ballRight){

        if(ballXY.x > 1290 && ballXY.x < 1300 && ballXY.y < paddle2Y + 100 && ballXY.y > paddle2Y-20){

            if(!ballUp && ballXY.y < paddle2Y){
                changeDirection = true;
                ballUp = true;
            }
            else if(ballUp && ballXY.y > paddle2Y + 80){
                changeDirection = true;
                ballUp = false;
            }

            ballRight = false;

        }
        else if(ballXY.x > 1600){
            score++;
            ballXY.y = rand.nextInt(690);
            ballXY.x = 678;
        }
        else
            ballXY.x = ballXY.x + 3;


        ifUp();
    }
    else{
        if(ballXY.x > 50 && ballXY.x < 60 &&ballXY.y < paddleY + 100 && ballXY.y > paddleY-20){
            if(!ballUp && ballXY.y < paddleY){
                changeDirection = true;
                ballUp = true;
            }
            else if(ballUp && ballXY.y > paddleY + 80){
                changeDirection = true;
                ballUp = false;
            }
            ballRight = true;
        }
        else if(ballXY.x < -244){
            score2++;
            ballXY.x = 678;
            ballXY.y = rand.nextInt(596);
        }
        else
            ballXY.x = ballXY.x - 3;

        ifUp();
    }
    AI();
    repaint();
}

@Override
public void actionPerformed(ActionEvent e) {
    update();
}

@Override
public void keyPressed(KeyEvent e) {
    if(e.getKeyCode() == KeyEvent.VK_W || e.getKeyCode() == KeyEvent.VK_UP){
        paddelUp = true;
    }
    if(e.getKeyCode() == KeyEvent.VK_S || e.getKeyCode() == KeyEvent.VK_DOWN){
        paddelDown = true;
    }
    if(e.getKeyCode() == KeyEvent.VK_SPACE){
        if(paused){
            paused = false;
            timer.start();
        }
        else{
            paused = true;
        }
    }
}

@Override
public void keyReleased(KeyEvent e) {
    if(e.getKeyCode() == KeyEvent.VK_W || e.getKeyCode() == KeyEvent.VK_UP){
        paddelUp = false;
    }
    if(e.getKeyCode() == KeyEvent.VK_S || e.getKeyCode() == KeyEvent.VK_DOWN){
        paddelDown = false;
    }
}

@Override
public void keyTyped(KeyEvent e) {

}
}