0

In my "SpcaeShip" game i have a loop game that updates and drawing all of my objects. Now i want to add an array list of asteroids to the game and to draw them, but the problem is that if i drawing the array of asteroids in the loop game, the game im moving very slow. My solution was to draw the asteroids on a timer thread and the game speed is perfect. The only problem i have is that my astreoids are flickering all the time. I need to use a bufferStrategy or something similier?

This is my draw method in the game loop (in this loop i call to the game state draw method):

private void tick()
{
 //update all objects
}

private void render()
    {
        //how many buffers(hold the same data as our screen) the canvas would use
        bs = display.getCanvas().getBufferStrategy();


        if(bs == null)
        {
            display.getCanvas().createBufferStrategy(3);
            return;
        }


        g = bs.getDrawGraphics();


        g.clearRect(0, 0, width, height);

        if(State.getState() != null)
        {
            State.getState().render(g);

        }   


        bs.show();
        g.dispose();

    }

public void run()
{

    init();

    int fps = 60;
    double timePerTick = 1000000000/ fps;
    double delta = 0;
    long now;
    long lastTime =System.nanoTime();
    long timer = 0;


    //game loop
    while(running)
    {
        now = System.nanoTime();
        delta += (now - lastTime) / timePerTick;
        timer += now - lastTime;
        lastTime = now;

        if(delta >= 1)
        {
            tick();
            render();
        }
    }

    stop();
}

This is my game state methods:

import java.awt.Graphics;    
import java.awt.event.ActionEvent;    
import java.awt.event.ActionListener;    
import java.util.ArrayList;         
import javax.swing.Timer;
public class GameState extends State 
{

    private SpaceShip spaceShip;
    private BgHandler bgHandler;
    private ArrayList<Meteor> meteors;

    public GameState(Handler handler)
    {
        super(handler);

        bgHandler = new BgHandler(handler.getWidth(), handler.getHeight());
        spaceShip = new SpaceShip(handler, handler.getWidth() / 2 - Things.DEFAULT_OBJECT_WIDTH / 2,
                handler.getHeight() - Things.DEFAULT_OBJECT_HEIGHT - 100);
        meteors = new ArrayList<Meteor>();

        for(int i = 0; i < 5; i++)
        {
            Meteor m = new Meteor(handler,(int) (Math.random() * (handler.getWidth() - Things.DEFAULT_OBJECT_WIDTH ))
                    , -Things.DEFAULT_OBJECT_WIDTH,
                    Things.DEFAULT_OBJECT_WIDTH,
                    Things.DEFAULT_OBJECT_HEIGHT);

            meteors.add(m);
        }

        timer.start();

    }

    Timer timer = new Timer (50, new ActionListener()
    {
        public void actionPerformed(ActionEvent e)
        {
            tickMeteor();
            renderMeteor(); 

        }
    });

    public void tickMeteor()
    {
        for(int i = 0; i < meteors.size(); i++)
        {
            Meteor m = meteors.get(i);

            if(m.getVisible())
            {
                m.tick();
            }
            else
            {
                meteors.remove(i);
            }   
        }
    }

    public void renderMeteor()
    {

        for(Meteor m: meteors)
        {
            Meteor m1 = (Meteor) m;

            if(m1.getVisible())
            {
                m1.render(handler.getGame().getGraphics());
            }
        }
    }


    @Override
    public void tick()
    {
        spaceShip.tick();
        for(int i = 0; i < meteors.size(); i++)
        {
            Meteor m = meteors.get(i);

            if(m.getVisible())
            {
                m.tick();
            }
            else
            {
                meteors.remove(i);
            }   
        }
    }

    }

    @Override
    public void render(Graphics g)
    {
        bgHandler.render(g);
        spaceShip.render(g);

    }    
}    
  • Check how often `if(State.getState() != null)` generates a blank frame – MadProgrammer Jun 04 '16 at 08:00
  • If you're using a BufferStategy, then you don't need to use a Swing Timer, you can use your own Thread, as you no longer need be concerned about syncing the painting process with the EDT, as they are separate process – MadProgrammer Jun 04 '16 at 08:04
  • Okay, everything should be updated and painted in the same thread and should be done in the same pass together – MadProgrammer Jun 04 '16 at 08:10

1 Answers1

1

This m1.render(handler.getGame().getGraphics()) is massively wrong.

The rendering of the meteors should be done in the render method and use the same Graphics context as the other elements, so they are all rendered in the same pass

getGraphics should never be used, as it provides a reference to the Graphics context outside of the normal painting cycle, which means anything painted to it will be removed on the next paint cycle

To be honest, I have no idea what your "run" method is trying to achieve but basically, it's free-wheeling, which could be consuming CPU cycles unnecessarily, stealing away cycles from other parts of your program.

At no point in the run loop does it sleep to allow time to be given to other threads (or processes).

The following is an example which tries to maintain a constant speed update cycle of 60 ticks a second, based on the amount of time that the actual work took to complete.

public void run() {

    int fps = 60;
    long nanosInSecond = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
    long timePerTick = (long)(nanosInSecond / (double)fps);

    //game loop
    while (true) {
        long now = System.nanoTime();

        try {
            // This is simulating the update and rendering process
            Thread.sleep(5);
        } catch (InterruptedException ex) {
        }
        long delta = System.nanoTime() - now;
        long timer = timePerTick - delta;
        timer = TimeUnit.MILLISECONDS.convert(timer, TimeUnit.NANOSECONDS);
        if (timer > 0) {
            try {
                System.out.println("sleep for " + timer + " milliseconds");
                Thread.sleep(timer);
            } catch (InterruptedException ex) {
            }
        }
    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • ok, i'm using with the same Graphics context now in the render method and the meteors don't flicker any more, but the game is moving very slow because i need to render the array of meteors. what can solve this problem? – Trying_To_Understand Jun 04 '16 at 08:51
  • Normally, you'd use a variable delay between updates to maintain a constant frame rate, so if you wanted 60fps, you need to update and render a frame every 16ms, but if the update/render phase takes 10ms, you only need to "sleep" 6ms. Since you've not supplied the "game loop" code which actually does this, it's hard to say what you should do – MadProgrammer Jun 04 '16 at 08:57
  • A bunch of out of context code snippets aren't really helpful, maybe consider providing a [runnable example](https://stackoverflow.com/help/mcve) which demonstrates your problem. This is not a code dump, but an example of what you are doing which highlights the problem you are having. This will result in less confusion and better responses. – MadProgrammer Jun 04 '16 at 09:27
  • Sorry but i have a little bit difficulty to catch how this work (this is my first game). I saw a tutorial how this constat frame rate work and copy that to my code: https://www.youtube.com/watch?v=w1aB5gc38C8&list=PLah6faXAgguMnTBs3JnEJY0shAc18XYQZ&index=10 Who can i know how much time the the update/render phase takes? – Trying_To_Understand Jun 04 '16 at 09:30
  • You get the time before you start rendering/updating, you subtract the time after it's completed, which represents the amount of time elapsed, you subtract that from the amount of time a "normal" sleep would need and you have your actual sleep value – MadProgrammer Jun 04 '16 at 09:33
  • For [example](http://stackoverflow.com/questions/24131513/java-what-is-heavier-canvas-or-paintcomponent/24131544#24131544) – MadProgrammer Jun 04 '16 at 09:56