gabzerbinato
gabzerbinato

Reputation: 68

Timer speeding up in Java

I know there are other questions about this, but none of them seem to help me to solve my problem. In my game, there is a Player that shoots a Missile, that fly towards negative y until it disappears out of the screen, going back to its original place and waiting to be shot again. However, every time the Missile is shot, it gets faster, like the delay in the Timer() gets shorter. The code:

Main class:

public class ShooterGame extends JFrame{
    static int playerX=500;
    static int playerY=520;

    InputHandler input = new InputHandler(this);
    public static Player player = new Player(playerX,playerY,50,50);

    public static void main(String[] args){
        ShooterGame game = new ShooterGame();
        game.run();
        System.exit(0);
    }

    static int windowWidth = 1300;
    static int windowHeight = 600;
    static int fps = 30;
    public static BufferedImage backBuffer = new BufferedImage(windowWidth, windowHeight, BufferedImage.TYPE_INT_RGB);
    public static Graphics bbg;

    public void run(){
        boolean running = true;

        initialize();

        while(running){
            long time = System.currentTimeMillis();

            update();
            draw();

            time = (1000 / fps) - (System.currentTimeMillis() - time);

            if (time > 0) { 
                try{ 
                    Thread.sleep(time); 
                } 
                    catch(Exception e){}; 
            };
        }

    }

    public void initialize(){
        setTitle("--- Shooter Game ---");
        setSize(windowWidth, windowHeight);
        setResizable(false);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }

    public void update(){
        player.update(input);
    }

    public void draw(){

        Graphics g = getGraphics(); 

        Graphics bbg = backBuffer.getGraphics(); 

        bbg.setColor(Color.BLACK); 
        bbg.fillRect(0, 0, windowWidth, windowHeight); 
        player.Draw(bbg);
        enemie1.Draw(bbg,10,100);

        if(game.player.Player.missileRunning) game.player.Player.missile.Draw(bbg);

        g.drawImage(backBuffer, 0, 0, this); 
    }

    public static Graphics getMainGraphics(){
        return bbg;
    }
}

Player class:

public class Player{

    private BufferedImage sprite;
    public BufferedImage missileSprite;
    public int x, y, width, height;
    private int fixedX;
    private final double speed = 5.0d;
    public static Missile missile;
    public static boolean missileRunning = false;
    public static boolean missileReady = true;

    public Player(int x, int y, int width, int height){
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        missile = new Missile(this.x);

        try{
            URL url = this.getClass().getResource("ship.png");
            sprite = ImageIO.read(url);
        } catch(IOException e){}

        try{
            URL url2 = this.getClass().getResource("missile.png");
            missileSprite = ImageIO.read(url2);
        } catch(IOException e){}
    }

    public void keyPlayer(double delta, InputHandler i){
        if(i.isKeyDown(KeyEvent.VK_D)){
            if(this.x>=1240) return;
            else this.x+=speed*delta;
        }

        if(i.isKeyDown(KeyEvent.VK_A)){
            if(this.x<=0) return;
            else this.x-=speed*delta;
        }

        if(i.isKeyDown(KeyEvent.VK_SPACE)){
            if(missileReady){ 
                try {
                    AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File("C:/Users/Gabriel/Desktop/Programacao/Other/java/ShootGame/game/player/Fire.wav").getAbsoluteFile());
                    Clip clip = AudioSystem.getClip();
                    clip.open(audioInputStream);
                    clip.start();
                } catch(Exception ex) {
                    System.out.println("Error with playing sound.");
                    ex.printStackTrace();
                }
                missile.x=this.x + 20;
                missileRunning=true; 
            }
        }

    }

    public void update(InputHandler inputP){
        keyPlayer(2.0, inputP);
        updateMissile(game.ShooterGame.backBuffer.getGraphics());
    }

    public void updateMissile(Graphics g){
        if(missileRunning){ 
            missileReady=false;
            missile.update(g);
        }
    }

    public Rectangle missileBounds(){
        return new Rectangle(missile.x, game.player.Missile.y, 6, 18);
    }

    public void Draw(Graphics a){
        a.drawImage(sprite,x,y,width,height,null);
    }

    public Rectangle getBounds(){
        return new Rectangle(x,y,width,height);
    }
}

Missile class:

public class Missile{

    public BufferedImage sprite;
    public static int x;
    public static int y=504;
    private int interval = 2000;
    private Timer timer2;
    private boolean isAlive = game.player.Player.missileRunning;
    static final AtomicInteger count = new AtomicInteger();
    public static boolean timerReady;

    public Missile(int x){

        this.x=x;
        this.y=504;
        this.sprite=sprite;

        try{
            URL url = this.getClass().getResource("missile.png");
            sprite = ImageIO.read(url);
        } catch(IOException e){System.out.println("Error loading image");}  
    }

    public void Draw(Graphics g){
        g.drawImage(sprite,x,this.y,6,18,null);
    }

    public void update(Graphics g){ //The problem
        if(game.player.Player.missileRunning==true){
            timerReady=true;
            if(checkTimer()){
                timer2 = new Timer();
                timer2.schedule(new Move(), 0, interval);
            }
            this.y = y;
            if(y <= 0){
                game.player.Player.missileRunning=false;
                timerReady=false;
                y=504;
                if(!checkTimer()){
                    timer2.cancel(); 
                    timer2.purge();
                }
                timer2=null;
                reload();
            }
        }
    }

    public boolean checkTimer(){
        if(timerReady){
            return true;
        } else {
            return false;
        }
    }

    class Move extends TimerTask{
        public void run(){
            int keeper = 3;
            if(keeper>0) y-=interval/1000;
        }
    }

    public synchronized void reload(){
        Timer missileBetween = new Timer();

        missileBetween.cancel();
        missileBetween = new Timer();

        TimerTask readyMissile = new TimerTask(){
            public void run(){
                game.player.Player.missileReady=true;
            }
        };

        missileBetween.schedule(readyMissile, 20);
    }

    public static int getNumber(){
        return count.get();
    }

    public static AtomicInteger getAtomic(){
        return count;
    }

}

(I am not posting all my program, just the parts that matter for the question) (If anything is missing, please say it)

Thanks

Upvotes: 1

Views: 2602

Answers (2)

hhafez
hhafez

Reputation: 39750

The time you are sleeping in the ShooterGame.run() method is wrong. Instead of time = (1000 / fps) - (System.currentTimeMillis() - time); why not simply time = 1000/fps.

First time you enter the while loop in run time will be approximately 1000/fps because there is little time between the two method calls. Future cycles through run will give you a negative number (System.currentTimeMillis() is a big number compared to 1000/fps) which means you will not call Thread.sleep(time). Set a break point there and tell us if this is correct.

Upvotes: -1

MadProgrammer
MadProgrammer

Reputation: 347204

Without running the code...

timerReady=true;
if(checkTimer()){
    timer2 = new Timer();
    timer2.schedule(new Move(), 0, interval);
}

Basically means that checkTimer will ALWAYS return true, meaning that each time that update is called, you are creating ANOTHER Timer, which is probably creating a few dozen timers, all updating the game state independently...which will cause the object to speed up...

This kind of logic and functionality should be controlled by the main-loop. On each cycle, you should check the state of the object and make a decision to move or remove it, there should be no other "timers" or "loops" involved here...

Oh, and learn to do without static, it will cause you more issues then it will solve in this case.

And...

Timer missileBetween = new Timer();
missileBetween.cancel();
missileBetween = new Timer();

...is pointless, you create a local Timer, cancel it, even though it's not actually, running, create a new, local instance and schedule some task...

You lose the reference to the local version and can no longer be modified...

Upvotes: 3

Related Questions