1

INTRODUCTION

Hi guys, I was learning how to make a little game where you are a racquet (rectangle) and all the asteroids (enemies) are falling down the screen and you have to avoid them

PROBLEM

When the game starts the enemies (asteroids) are paint on the screen in a very high speed, and they occupy all the size of the screen so there is no way to avoid them (try the game), I just want that between painting an enemy and the next one there is a mininum time to delay (for example 0,5 seconds). But I just don't know how to do that, I tried to use Thread.sleep() and TimeUnit but they just make the game slower. Surfing on stackoverflow I've find out that I may try to use Swing timers, I've read some stuff on the web but I want to know how can I use swing timers in my code (if they can solve my problem).

Here it is the code: The main class:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;

@SuppressWarnings("serial")
public class Game extends JPanel {

    Racquet racquet = new Racquet(this);
    Enemy Enemy = new Enemy(this);

    static ArrayList<Enemy> enemyList = new ArrayList<Enemy>();


    public Game() {
        addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
                racquet.keyReleased(e);
            }

            @Override
            public void keyPressed(KeyEvent e) {
                racquet.keyPressed(e);
            }
        });
        setFocusable(true);
    }

    /** TO SET THE RANDOM POSITION ON WHERE THE ENEMIES HAVE TO APPEAR ON THE SCREEN **/
    public int random(int x, int y, ArrayList<Enemy> pa){ 
        int r = 0;

        for(int i = 0; i<pa.size(); i++){
            Random rand = new Random();
            r = rand.nextInt(x+y)-1;                
            return r;
        }

        return r;
    }

    /** letting the enemies move on the screen **/
    private void move() { 
        for(int i = 0; i < enemyList.size(); i++){
            enemyList.get(i).move();
        }

        racquet.move();         
    }

    /**  Painting on the screen enemies and the racquet **/
@Override
    public void paint(Graphics g) { 
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 

        for(int i = 0; i < enemyList.size(); i++){
            enemyList.get(i).paint(g2d);
        }
        racquet.paint(g2d); 
    }

    public void gameOver() {
        JOptionPane.showMessageDialog(this, "Game Over", "Game Over", JOptionPane.YES_NO_OPTION);
        System.exit(ABORT);
    }

    public void createEnemy(){
        enemyList.add(new Enemy(this));
    }

    public static void main(String[] args) throws InterruptedException {
        JFrame frame = new JFrame("Asteroids");
        Game game = new Game();
        frame.add(game);
        frame.setSize(300, 400);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        game.random(200, 300, enemyList);

        while (true) {
            game.createEnemy();
            game.move();
            game.repaint();
            Thread.sleep(5);
        }
    }
}

The racquet class:

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

public class Racquet {
    private static final int Y = 330;
    private static final int WIDTH = 30;
    private static final int HEIGHT = 6 ;
    int x = 0;
    int xa = 0;
    private Game game;

    public Racquet(Game game) {
        this.game = game;
    }

    /** letting the racquet moves on the screen **/
    public void move() { 
        if (x + xa > 0 && x + xa < game.getWidth() - WIDTH)
            x = x + xa;
    }

    /** Creating the rectangle racquet **/
    public void paint(Graphics2D g) { 
        g.fillRect(x, Y, WIDTH, HEIGHT);
    }

    /** // Setting xa everytime to 0, if we don't do this it just takes a single pression to go to a direction until we press the other key **/
    public void keyReleased(KeyEvent e) {  
        xa = 0;
    }

    /** Choosing the direction **/
    public void keyPressed(KeyEvent e) { 
        if (e.getKeyCode() == KeyEvent.VK_LEFT)
            xa = -1;
        if (e.getKeyCode() == KeyEvent.VK_RIGHT)
            xa = 1;
    }


    public Rectangle getBounds() {
        return new Rectangle(x, Y, WIDTH, HEIGHT);
    }

    public int getTopY() {
        return Y;
    }
}

And the enemy class:

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.*;

public class Enemy {

    private Game game;
    int x = 0;
    int y = 0;
    int xa = 1;
    int ya = 1;

    /** Generating a random position where the enemies have to appear **/
    public Enemy(Game game){
        this.game = game;
        x = game.random(0, 320, game.enemyList); 
    }

    /** Paint the enemies **/
    public void paint(Graphics2D g) {
        g.fillRect(x, y, 20, 20);
    }

    /** move the enemies and detect collisions **/
    public void move(){
        y += ya;
        if(collision()){
            game.gameOver();
        }
    }

    /** returns true if the enemy rectangle touch the racquet **/
    public boolean collision(){
        return game.racquet.getBounds().intersects(getBounds()); 
    }

    public Rectangle getBounds() {
        return new Rectangle(x, y, 20, 20);
    }
}
Hoffman
  • 72
  • 1
  • 9

1 Answers1

2

Suggestions:

  1. Your game "tick" time is 5 mSecs, a not quite reasonable time. I suggest that
    • You get rid of the "magic" number, using a constant instead,
    • and make the tic a little bigger, say 12 to 15 mSec.
    • Also better to use a Swing Timer or to at least make sure that you do your while loop in a defined background thread.
  2. Most importantly, you're creating a new enemy with each tick of your game loop, and that's too fast. Instead:
    • Don't create a new Enemy with each tick,
    • Instead save the time that the last enemy was created in a field,
    • Check the delta-time, the current system time - lastEnemyCreationTime, inside of your game-loop,
    • Only create a new enemy if the delta-time is greater than a reasonable value, either a constant value or a field (not a magic number).
    • On creation of the new enemy, reset the lastEnemyCreationTime to the current system time.

Unrelated recs:

  1. Override the JPanel's paintComponent, not the paint method as this will give you double buffering by default which can lead to smoother perceived graphics.
  2. Favor using Key Bindings over a KeyListener as this will help to easily eliminate focus issues inherent with use of KeyListeners.
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • First of all thanks for your answer but I have some doubts: 1 - how can I use a swing timer in my code, I mean where I have to put/use it? And how I use it? I've searched in google but I didn't understand very well 2 - How can I save the time that the last enemy was created in a field? What I have to write in my code? 3 - What do you mean for "magic" number? – Hoffman May 19 '16 at 13:58
  • @Hoffman: here's a link on [magic numbers](http://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad). As for "where to put in your code" best to check the tutorials and try to figure this out first yourself -- you will learn so much more doing it this way. If still stuck, edit your question, show your latest attempt and tell problems you're having. Note try to fix only one issue at a time though. – Hovercraft Full Of Eels May 19 '16 at 16:16
  • I removed all magic numbers and I solved the problem using a swing timer, it seems to work :D thank you – Hoffman May 19 '16 at 18:01