Java Applet Game 2D Window Scrolling

Java Scrolling Background

Most games redraw the entire background every frame, then redraw all sprites on top of it.

If your background is just a big image, then this is surprisingly efficient (since a memory-> memory image copy is very fast).

To maintain the perception of scrolling in a 2D game:

  • Define a pair of coordinates (scrollX, scrollY) that store the current scroll position of the top left of the screen.
  • You can update scrollX and ScrollY whenever you want to scroll (e.g. following the player)
  • Give each sprite a co-ordinate (spriteX, spriteY) that is relative to the game world (i.e. the game background) and not relative to the screen position. Then you don't need to change these numbers when you scroll....
  • Then you need to draw the background at screen position (-scrollX, -scrollY) and each sprite at (spriteX-scrollX, spriteY-scrollY).
  • Java should take care of the screen clipping for you. But if your game world and background is large, then you may need some additional techniques to avoid the overhead of trying to draw offscreen objects (this would be called "view frustrum culling" in game development lingo)

Infinite background for game

Have a look through this source which behaves in a more predictable manner, and also includes a nice tweak to the chopper image (animated). ;)

Scrolling vehicle with BG

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.*;

public class MainFrame
{
public MainFrame()
{
JFrame f = new JFrame("Helicopter Background Test");
f.add(new AnotherBackground());
//setTitle("Helicopter Background Test"); Redundant..
// Set a preferred size for the content area and pack() the frame instead!
// setSize(FRAME_WIDTH,FRAME_HEIGHT);
// setLocationRelativeTo(null); Better to..
f.setLocationByPlatform(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack(); // Size the GUI - VERY MPORTANT!
f.setVisible(true);
}

public static void main(String[] args)
{
new MainFrame();
}
}

class AnotherBackground extends JPanel
{
private static int PREFERRED_WIDTH = 400;
private static int PREFERRED_HEIGHT = 200;

private BufferedImage heliImage = null;
private BufferedImage heliLeftImage = null;
private BufferedImage heliRightImage = null;
private BufferedImage backImage = null; //getFlippedImage(

private int heliX = 0;
private int heliY = 0;

private int backX = 0;
private int backY = 0;

private int frameWidth = 0;
private int frameHeight = 0;

private int backWidth = 0;
private int backHeight = 0;

public AnotherBackground()
{
frameWidth = PREFERRED_WIDTH;
frameHeight = PREFERRED_HEIGHT;

this.setFocusable(true);
this.addKeyListener(new HeliListener());

try
{
heliLeftImage = ImageIO.read(
new URL("http://imageshack.us/a/img7/2133/helicopter2f.png"));
heliRightImage = getFlippedImage(heliLeftImage);
heliImage = heliLeftImage;
// 2.7 Meg Crap that is an humungous image! Substitute dummy.
backImage = getTileImage(250);
//ImageIO.read(
// new URL("http://i.stack.imgur.com/T5uTa.png"));

backWidth = backImage.getWidth();
backHeight = backImage.getHeight();

//HeliPainter l = new HeliPainter(); // see mention of repaint()
//new Thread(l).start();
} catch(IOException ex) {
// THERE IS NO POINT CONTINUING AFTER THIS POINT!
// unless it is to pop an option pane error message..
System.err.println("Problem during loading heli image");
ex.printStackTrace();
}

}

@Override
public Dimension getPreferredSize() {
return new Dimension(PREFERRED_WIDTH, PREFERRED_HEIGHT);
}

@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);

int normalizeX = (heliRealX-heliX)%backImage.getWidth();
int normalizeY = (heliRealY-heliY)%backImage.getHeight();
int timesRepeatX = (getWidth()/backImage.getWidth())+2;
int timesRepeatY = (getHeight()/backImage.getHeight())+2;

for (int xx=-1; xx<timesRepeatX; xx++) {
for (int yy=-1; yy<timesRepeatY; yy++) {
g.drawImage(
backImage,
(xx*backImage.getWidth())-normalizeX,
(yy*backImage.getHeight())-normalizeY,
this); // A JPanel IS AN ImageObserver!
g.drawImage(heliImage, heliX, heliY, this);
}
}
g.setColor(Color.BLACK);
}

private int heliRealX = 0;
private int heliRealY = 0;

class HeliListener extends KeyAdapter
{
@Override
public void keyPressed(KeyEvent e)
{
int pad = 5;
if (e.getKeyCode() == KeyEvent.VK_LEFT)
{
if(heliX > 0)
{
heliX -= 5;
}
else
{
backX += 5;
}
heliRealX-=5;
heliImage = heliLeftImage;
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
{
// correct for image size + padding
if(heliX+heliImage.getWidth()+pad < getWidth())
{
heliX += 5;
}
else
{
backX -= 5;
}
heliRealX+=5;
heliImage = heliRightImage;
}

else if (e.getKeyCode() == KeyEvent.VK_UP)
{
if(heliY > 0)
{
heliY -= 5;
}
else
{
backY += 5;
}
heliRealY-=5;
}

else if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
// correct for image size + padding
if(heliY+heliImage.getHeight()+pad < getHeight())
{
heliY += 5;
}
else
{
backY -= 5;
}
heliRealY+=5;
}
repaint(); // Replaces need for threads for this simple demo!
}
}

public BufferedImage getFlippedImage(BufferedImage original) {
BufferedImage bi = new BufferedImage(
original.getWidth(),
original.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();

AffineTransform at = AffineTransform.getTranslateInstance(bi.getWidth(),1d);
at.concatenate(AffineTransform.getScaleInstance(-1d,1d));
g.setTransform(at);
g.drawImage(original,0,0,this);

g.dispose();
return bi;
}

public BufferedImage getTileImage(int s) {
BufferedImage bi = new BufferedImage(s,s,BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();

GradientPaint gp1 = new GradientPaint(
(float)0,(float)s/4, Color.YELLOW,
(float)s/4,0f, Color.GREEN,
true);
g.setPaint(gp1);
g.fillRect(0,0,s,s);

int trans = 165;
GradientPaint gp2 = new GradientPaint(
(float)s/2,(float)s/2, new Color(255,0,0,trans),
0f,(float)s/2, new Color(255,255,255,trans),
true);
g.setPaint(gp2);
g.fillRect(0,0,s,s);

g.dispose();
return bi;
}
}

Zoom and Pan JPanel with multiple Images drawn on it

What I would do is start with a BufferedImage which will act as the final output.

Create it so it's size is width x scale and height x scale.

Obtain a Graphics context from it using BufferedImage#createGraphics and set the context's scale accordingly, using Graphics#scale.

Then paint the output to this Graphics context, don't forget to call Graphics#dispose when you're done.

With this BufferedImage, render this to the screen. This gives you the "zoom" functionality.

For the panning, you can take a look at Java Applet Game 2D Window Scrolling for an example.



Related Topics



Leave a reply



Submit