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

时间:2017-10-14 11:55:35

标签: java multithreading performance frames

我需要在游戏(Pong)中添加一个可用的FPS计数器。为了制作一个FPS计数器我显然也需要一个游戏循环,我尝试分别制作一个游戏循环和FPS计数器,它似乎正在工作。但是,我在将PPS计数器添加到我的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。

1 个答案:

答案 0 :(得分:0)

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

这是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();
    }
}

主方法测试计数器是否有效(每2秒,它会打印每秒空循环的数量)。

您可以像这样使用它:

  1. 在游戏类中添加private FPSCounter fpscnt;
  2. 在游戏的构造函数中构造对象,如下所示:fpscnt = new FPSCounter();
  3. 在游戏开始前,通过致电:fpscnt.start();启动计数器。例如,你可以在游戏的构造函数中调用它。
  4. 现在,每当绘制一个帧时,您应该调用计数器的frame()方法。我接下来要示威......
  5. 现在,只要您想获得每秒的帧数,就应该调用计数器的get()方法。我接下来要示威......
  6. 现在,在游戏的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) {
    
    }
    }