【问题标题】:FPS Counter in my game (Java)我的游戏中的 FPS 计数器 (Java)
【发布时间】:2017-10-14 11:55:34
【问题描述】:

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

这是游戏循环/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 计数器和/或如何将其添加到游戏中,请提供帮助。顺便说一句,我不希望 FPS 锁定在 60。

【问题讨论】:

    标签: java multithreading performance frames


    【解决方案1】:

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

    这是 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;
    2. 在游戏的构造函数中构造对象,如下所示:fpscnt = new FPSCounter();
    3. 在比赛开始前启动计数器,调用:fpscnt.start();。例如,您可以在游戏的构造函数中调用它。
    4. 现在,每当绘制帧时,您应该调用计数器的frame() 方法。我接下来要展示...
    5. 现在,当你想获得每秒的帧数时,你应该调用计数器的get() 方法。我接下来要展示...

    现在,在游戏的 paintComponent() 方法中,打印每秒帧数的行应如下所示:

    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 类应该如下所示:

    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) {
    
    }
    }
    

    【讨论】:

    • 非常感谢您的回复,帮助很大。问题是,我在游戏中有一个计时器,它没有让游戏运行速度超过 5 毫秒,现在游戏忽略了那个计时器并且运行得非常快。如何在不影响 fps 的情况下让游戏运行变慢?
    • @J.Doe 可能在您的游戏代码中使用Thread.sleep? (不是绘画代码)
    • 好的,现在一切正常,除了在屏幕上显示 FPS。我尝试使用 g.drawString 等输入您建议的内容,但没有成功。每秒帧数应该每秒出现在屏幕上一次。我应该将您在 5 个步骤中建议的代码放在哪里?
    • @J.Doe 这些步骤解释了将 fps 计数器的代码放在哪里。您对步骤有任何疑问吗? (当我写“游戏代码”时,我指的是你的第一堂课)
    • 很抱歉,我在理解最后一部分时遇到了一些问题。我添加了“Private Main fpscnt;” (我的课叫做Main)一开始。我添加了“fpscnt = new Main();”在构造函数的开头。我添加了“fpscnt.start();”在构造函数的底部。我在paintComponent 中添加了“fpscnt.frame();”、“g.setFont...”和“g.drawString...”。有什么我想念的吗?运行此程序时出现错误。
    猜你喜欢
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多