I need to add a working FPS counter in my game (Pong). To make an FPS counter I apparently also need a game loop, I tried making a game loop and FPS counter separately and it seems to be working. However, I am having some problems adding the FPS counter to my Pong game.
This is the code for the game loop/FPS counter:
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();
}
}
I tried adding the FPS counter to the game but it did not work for some reason. If you know how to make a better FPS counter and/or how to add it to the game please help. By the way, I do NOT want the FPS locked at 60.
In this answer, I'm not finding the cause of your counter's problem. It's fairly complicated for me. But I will give you some code to help get started:
This is a code for an FPS counter:
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();
}
}
The main method tests that the counter works (every 2 seconds, it prints the number of empty loops per second).
You can use it like so:
private FPSCounter fpscnt;
in your game class.fpscnt = new FPSCounter();
.fpscnt.start();
. You can call this inside the game's constructor for example.frame()
method of the counter. I'm demonstrating up next...get()
method of the counter. I'm demonstrating up next...So now, inside your game's paintComponent()
method, the lines printing the number of frames/sec shall look like this:
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.
Note:
Each value returned by get()
method is delayed for 1 second. I mean if you see 64 fps, then 64 where the frames in the last (past) second.
So your game's Main
class should then look like this:
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) {
}
}