2017-10-14 6 views
0

Je dois ajouter un compteur FPS fonctionnel dans mon jeu (Pong). Pour faire un compteur FPS j'ai apparemment aussi besoin d'une boucle de jeu, j'ai essayé de faire une boucle de jeu et un compteur FPS séparément et cela semble fonctionner. Cependant, j'ai quelques problèmes en ajoutant le compteur de FPS à mon jeu de Pong.FPS Counter dans mon jeu (Java)

C'est le code pour la boucle de jeu/Compteur de 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(); 
} 
} 

J'ai essayé d'ajouter le compteur FPS au jeu, mais il ne fonctionne pas pour une raison quelconque. Si vous savez comment créer un meilleur compteur FPS et/ou comment l'ajouter au jeu, merci de nous aider. En passant, je ne veux pas que le FPS soit verrouillé à 60.

Répondre

0

Dans cette réponse, je ne trouve pas la cause du problème de votre compteur. C'est assez compliqué pour moi. Mais je vais vous donner un code pour aider à démarrer:

C'est un code pour un compteur 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(); 
    } 
} 

Les principaux tests de méthode que les travaux de compteur (toutes les 2 secondes, il affiche le nombre de boucles vides par seconde).

Vous pouvez l'utiliser comme ceci:

  1. Ajouter un private FPSCounter fpscnt; dans votre classe de jeu. Construisez l'objet dans le constructeur de votre jeu comme ceci: fpscnt = new FPSCounter();. Commencez le compteur avant le début de la partie en appelant le fpscnt.start();. Vous pouvez l'appeler dans le constructeur du jeu par exemple.
  2. Maintenant, chaque fois qu'un cadre est peint, vous devez appeler la méthode frame() du compteur. Je montre maintenant ...
  3. Maintenant, chaque fois que vous voulez obtenir le nombre d'images par seconde, vous devez appeler la méthode get() du compteur. Je démontre à côté ...

Alors maintenant, à l'intérieur de votre méthode de jeu paintComponent(), les lignes d'impression le nombre d'images/sec doivent ressembler à ceci:

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. 

Remarque:
Chaque valeur renvoyée par la méthode get() est retardée d'une seconde. Je veux dire si vous voyez 64 fps, alors 64 où les images dans la dernière (dernière) seconde.

Alors Main classe de votre jeu devrait alors ressembler à ceci:

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

} 
} 
+0

Merci beaucoup pour votre réponse, il m'a beaucoup aidé. Le truc, c'est que j'avais une minuterie dans le jeu qui ne permettait pas au jeu de courir plus vite que 5ms et maintenant le jeu ignore cette minuterie et tourne TRES vite. Comment puis-je ralentir le jeu sans affecter le fps? –

+0

@ J.Doe peut-être utiliser [Thread.sleep] (https://docs.oracle.com/javase/9/docs/api/java/lang/Thread.html#sleep-long-) dans le code de votre jeu? (pas le code de peinture) – thanopi57

+0

Bon, tout fonctionne maintenant sauf afficher le FPS sur l'écran. J'ai essayé de taper ce que vous avez suggéré avec g.drawString etc mais cela n'a pas fonctionné. Les images par seconde doivent apparaître sur l'écran une fois par seconde. Où exactement dois-je placer le code que vous avez suggéré dans vos 5 étapes? –