Using JScrollPane as the camera for a top-down shooter

Hey all,

New here, but the community seems great. As the title says, I’m trying to use a JScrollPane as a camera in my game. My main problem is that I can’t center the camera on the player. I can make the camera scroll when the player hits the edge with scrollRectToVisible(), but even that breaks after a bit, allowing the character to walk off screen. A good example of what I’m trying to do is Notch’s Left 4k Dead. I looked at the source for that, but everything seems to be somewhat hacked together. I have looked at the Java API for methods that would help me follow the player but none of them seem to work. I’ve toyed around with changing the underlying JViewPort but that didn’t work either.

Any help would be appreciated with this problem.

I don’t see any point of using JScrollPane as a scrolling map. In fact, you shouldn’t be using Swing to make games at all. It is best to just stick to pure AWT and Graphics2D.

To scroll, it is best to make a Camera class that only stores an X and Y offset. Then in your paint method before you draw anything, translate the transform:


public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D)g;
    
    g2.translate(myCamera.getXOffset(),myCamera.getYOffset());
    
    //draw the world

    //to un-translate:
    g2.translate(-myCamera.getXOffset(),-myCamera.getYOffset());

I would do something to what ra4king is saying, but I would make a Followable interface with .getX() and .getY(). Have any class that you want to be able to follow implement the interface. You then could do something like this:



//Your variable
Followable following;

//Somewhere else:
following = player1;

public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    
    g2d.translate(following.getX(), following.getY());
    
    //draw stuff

You could then have fancy stuff, like a Camera class that can be followed. It could move over to where something is going on, and then move back to Player1. etc…

It’s java, so it’s all up to your imagination.

To center a player (or any “Followable”) in the middle of the screen:


g2.translate(-following.getX() + screenWidth/2, -following.getY() + screenHeight/2);

Thanks for the replies. I’ll start restructuring everything to run on a canvas. I’ve just been drawing to a class that extended JPanel.

Yea, I was just being cheap.

Just curious, why did you decide to use JPanel? I’m trying to track down this widespread recommendation of JPanel usage. The most serious issue of why JPanel is bad is that it doesn’t accept input events by default!

Canvas is the best choice to draw on and, combined with BufferStrategy, will give you optimal performance.

It’s actually stemming from a couple colleagues at school. I asked about canvas and they said that I shouldn’t bother and it wouldn’t help performance very much. So I just went with JPanel and it seemed simple enough so I stuck with it.

Well, IMHO, I think they’re wrong.

LOL! Bunch of clueless colleagues ;D

Canvas is giving me some problems drawing. Anything explicitly different than JPanel that would be causing me problems? I have the BufferStrategy and such. When it did draw all it did was flicker.

My Paint method looks, like this:

public void paint(Graphics g) {
		
		Graphics2D b = (Graphics2D) bufferStrategy.getDrawGraphics();
	    //b.clearRect(0, 0, WIDTH, HEIGHT);
	    b.drawImage(imgDbl, 0, 0, screenWidth, screenHeight, null);
	    //b.drawImage(imgAll, 0, 0, null);
	    b.dispose();
	    bufferStrategy.show();
		
	}

Note: My main class extends Frame, and a canvas is added to it.

This would be basically what I have without all the extra methods and stuff for my actual game. No matter what I try this will just not draw.


public class Game extends Canvas
{
	BufferStrategy strategy;

   Game()
	{
	  /*
		*	Frame crap!
		*
		*/
		 
		//Make the frame and a panel to put stuff in
		JFrame frame = new JFrame("GAME");
		frame.setPreferredSize(new Dimension(1024,768));
		
		JPanel panel = (JPanel) frame.getContentPane();
		panel.setPreferredSize(new Dimension(frame.getWidth(), frame.getHeight()));
		panel.setLayout(null);
		
		setBounds(0,0,frame.getWidth(),frame.getHeight());
		frame.add(this);
		
		setIgnoreRepaint(true);
		
		frame.pack();
		
		//Some stuff to make the frame do what we want
		frame.setResizable(false);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLocationRelativeTo(null);
		
		//Make the buffering strategy
		createBufferStrategy(2);
		strategy = getBufferStrategy();
    }
   public void paint(Graphics g)
	{
		Graphics2D d = (Graphics2D) strategy.getDrawGraphics();
		
		d.setColor(Color.BLUE);
		d.fillRect(50,50,200,200);
		
		d.dispose();
		strategy.show();
	}
	public static void main(String[] args)
	{
		Game g = new Game();
	}

I can’t make that rectangle draw. Let alone my actual paint method with enemies and such. Any tips?

Nooooooooooo do not use the repaint mechanism at all when using BufferStrategy:


public class MyGame implement Runnable {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setSize(WIDTH,HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        frame.setIgnoreRepaint(true); //important!

        Canvas canvas = (Canvas)frame.add(new Canvas());
        canvas.setIgnoreRepaint(true); //important!
        
        frame.setVisible();
        
        canvas.createBufferStrategy(2);
        strategy = canvas.getBufferStrategy();

        new Thread(this).start();
    }

    private BufferStrategy strategy;
    
    public void run() {
        //game loop
        
        while(true) {
            //update

            //render: recommended to setup like this according to BufferStrategy JavaDocs.
            do {
                do {
                    Graphics2D g = (Graphics2D)strategy.getDrawGraphics();
                    
                    //draw everything
                    
                    g.dispose();
                } while(strategy.contentsRestored());
                
                strategy.show();
        } while(strategy.contentsLost());
    }
}

Thank you ra4king. That got the display working.

I had setIgnoreRepaint(true); then called repaint(); in the main loop each frame. Kinda’ the same thing, I guess.

Calling repaint() when you have setIgnoreRepaint(true) will do absolutely nothing :smiley:

but it’s working fine… maybe it’s an OpenJVM thing. I have to try it on a windows machine.

Are you calling those two methods on the same object? :wink: