I know you're not supposed to use Lightweight Components like JFrame with non-Lightweight Components like JFrame, so my question is, what do you use with a Canvas?

Is there also some example code for it?

Thanks!

Recommended Answers

All 10 Replies

Why? Canvas is an old AWT component that you would use in an old AWT Frame. Forget it, and use a JPanel.

I find it's more useful when Rendering in Games.

Useful in what ways?

Drawing to a BufferStrategy, and having multiple buffers. Also, it's not a hassle to call repaint and revalidate :P

Swing components are double buffered by default, unlike the old AWT components that make you do it. If you place a canvas with some more complex buffering strategy inside a double buffered Swing parent it's far from clear what your custom buffering will achieve anyway.

If you are updating the painting of a JPanel you do not need to call revalidate, a simple call to repaint will do, so that's no different from canvas.
Have a look at Oracle's write up on AWT vs Swing. http://www.oracle.com/technetwork/java/painting-140037.html

So unless you are doing something very special in a difficult hardware environment it's safe to say that using obsolete AWT components instead of their superior Swing replacements is always a mistake, and mixing them is a bigger mistake.

Alright.
How would I go about painting if I want to use a separate method for rendering, especially when used in a game loop?

Here's my game loop for reference:

@Override
    public void run()
    {
        init();

        long lastTime = System.nanoTime();
        double delta = 0.0;
        double ns = 1000000000.0 / 60.0;
        long timer = System.currentTimeMillis();
        int updates = 0;
        int frames = 0;

        while(isRunning)
        {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;

            if (delta >= 1.0)
            {
                delta--;
                update(delta);
                updates++;
            }

            render();
            frames++;

            if (System.currentTimeMillis() - timer > 1000)
            {
                timer += 1000;
                System.out.println("Updates: " + updates + ", Frames: " + frames);
                updates = 0;
                frames = 0;
            }
        }

        dispose();
    }

And this is how I'm currently rendering: (gsm is a GameStateManager which just acts as a passthrough for each require GameState method)

public void render()
    {
        BufferStrategy bufferStrategy = getBufferStrategy();
        if(bufferStrategy == null)
        {
            createBufferStrategy(2);
            return;
        }

        Graphics g = bufferStrategy.getDrawGraphics();

        gsm.render(g);

        g.dispose();
        bufferStrategy.show();
    }

I have successfully converted the code to use a JPanel instead of a Canvas, however, now I am having issues with it.

I am now getting an offset when drawing, it seems to be -4 pixels to the left and +4 pixels down. I've googled this and I guess it has something to do with the getPreferredSize() but I'm not sure what I would override this with to actually render in the correct location.
I have printed the getX and getY of the JPanel and they are at 0, 0.

I know I could just correct this by rendering at (x + 4, y - 4) however, this is not a good solution.

PS: I am still using a BufferStrategy because it helps with the flickering.

New Render Method:

public void render()
    {
        bufferStrategy = window.getBufferStrategy();
        if(bufferStrategy == null)
        {
            window.createBufferStrategy(2);
            return;
        }

        graphics = bufferStrategy.getDrawGraphics();

        gsm.render(graphics);

        if(!bufferStrategy.contentsLost())
            bufferStrategy.show();
    }

This is where I'm testing some rendering stuff, g.drawImage(outside.get(6).getTileImage(), 0, 0, null);

Here's what's produced from rendering (note I want the image to be completely visible): puush Image

It looks like you are fighting the Java graphics system rather than working with it.
Before getting into buffer strategies, just try the recommended (flicker free) mechanism...
Override paintComponent for a JPanel and do all your drawing there or called from there
Update the game state in a non-Swing thread
Call repaint() at the required intervals
Let Swing's double buffering handle the flicker

The link I gave you earlier has some other hints to optimise performance.

Woaahh, okay. I think I definitely set this up correctly. I'm now getting an insane amount of frames (way more efficient than the other way)!

About 8 million frames compared to to 2400 ish frames from before.

Excellent!
A while ago I did a test/demo program using the simple architecture above. It has hundreds of balls bouncing around and bouncing off each other, with full spherical bounce calculations and gravity, plus animated gifs moving across the screen at the same time and assorted bricks and balloons crashing/floating around. Runs smoothly at 30 fps on a five year old ordinary PC.
Back in the days of Java 1.4 that was the first version where animated games became possible, but only if you spent a lot of time doing your own buffering etc.. Unfortunately there are still many tutorials on the web that date back to those days. Java 1.5 gave us double buffering and a whole load of internal optimisations, and life has been a lot simpler ever since.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.