Frogger

A classic 8-bit graphics game is called Frogger. This will be a very basic implementation, but I hope you will learn enough so you can figure how to add more and more features so that it more closely resembles the original. The original 8-bit characters are here A video showing many versions is here

CheckLIst

  1. make it easier for the player to get across. Bigger faster frog, slower less frequent cars are all good ideas.
  2. Make a Bike class that is a subclass of Car, which is skinny and easy to avoid.

Challenges

  1. Make a subclass of Lane called River, and new class call Log. The idea is if there is a collision with the log, the frog "jumps on" the log and rides it. The frog dies if it drowns in the river or rides too long and goes off screen.

Frog.java

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

public class Frog 
{
	private BufferedImage img, splatImg;
	private int x, y, scale;
	private boolean alive;
	public static Color[] colors = {Color.BLACK, Color.GREEN, Color.YELLOW, Color.MAGENTA};
	public Frog(int x, int y)
	{
		this.x=x;
		this.y=y;
		colors[0] = new Color(0,0,0,1);//transparent alpha
		initImg();
		alive=true;
		scale=3;
	}
	public void initImg(){
		ArrayList<String> a = new ArrayList<String>();
		a.add("010021220010");
		a.add("110312213011");
		a.add("010112211010");
		a.add("011222222110");
		a.add("000212222000");
		a.add("011212222110");
		a.add("010121221010");
		a.add("110012210011");
		a.add("010000000010");
		int rows=a.size();
		int cols=a.get(0).length();
		this.img = new BufferedImage(cols, rows, BufferedImage.TYPE_INT_ARGB); 
		this.splatImg = new BufferedImage(cols, rows, BufferedImage.TYPE_INT_ARGB); 
		for (int r=0; r<rows; r++)
			for(int c=0; c<cols; c++){
				String row=a.get(r);
				String value = row.substring(c, c+1);
				int colorIndex = Integer.parseInt(value);
				int rgb = colors[colorIndex].getRGB();
				img.setRGB(c, r, rgb);
				splatImg.setRGB(c, r, rgb);
				if (colorIndex!= 0)
					splatImg.setRGB(c, r, Color.RED.getRGB());
			}
	}
	public void draw(Graphics g){
		int direction=1*scale;//-1 if pointing down
		if (alive)
			g.drawImage(img,x,y,x+scale*img.getWidth(),y+direction*img.getHeight(), 0,0,img.getWidth(), img.getHeight(), null);
		else
			g.drawImage(splatImg,x,y,x+scale*img.getWidth(),y+direction*img.getHeight(), 0,0, img.getWidth(), img.getHeight(), null);		
	}
	public boolean isAlive() {
		return alive;
	}
	public void setAlive(boolean alive) {
		this.alive = alive;
	}
	public void moveUp(int delta){
		if (alive)
			y-=delta;
	}
	public void moveDown(int delta){
		if (alive)
			y+=delta;
	}
	public void moveLeft(int delta){
		if(alive)
			x-=delta;
	}
	public void moveRight(int delta){
		if (alive)
			x+=delta;
	}
	public Rectangle getLocation(){
		return new Rectangle(x,y,img.getWidth()*scale,img.getHeight()*scale);
	}
}

Car.java

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

public class Car {
	private BufferedImage img;
	private int x,y,scale;

	public static Color[] colors = {Color.BLACK, Color.GREEN, Color.CYAN, Color.MAGENTA, Color.RED, Color.WHITE};
	public Car (int x, int y, int scale){
		this.x=x;
		this.y=y;
		this.scale = scale;
		colors[0] = new Color(0,0,0,1);//transparent alpha
		initImg();

	}
	public void initImg(){
		ArrayList<String> a = new ArrayList<String>();
		a.add("000110000011100");
		a.add("023333300333330");
		a.add("233332232333332");
		a.add("213322333322230");
		a.add("233322333322230");
		a.add(a.get(4));
		a.add(a.get(3));
		a.add(a.get(2));
		a.add(a.get(1));
		a.add(a.get(0));

		int rows=a.size();
		int cols=a.get(0).length();
		this.setImg(new BufferedImage(cols, rows, BufferedImage.TYPE_INT_ARGB)); 

		for (int r=0; r<rows; r++)
			for(int c=0; c<cols; c++){
				String row=a.get(r);
				String value = row.substring(c, c+1);
				int colorIndex = Integer.parseInt(value);
				int rgb = colors[colorIndex].getRGB();
				getImg().setRGB(c, r, rgb);

			}
	}
	public void moveX(int delta){
		this.x+=delta;
	}
	public void draw(Graphics g){

		g.drawImage(getImg(),x,y,x+scale*getImg().getWidth(),y+scale*getImg().getHeight(), 0,0,getImg().getWidth(), getImg().getHeight(), null);

	}
	public Rectangle getLocation(){
		return new Rectangle(x,y,getImg().getWidth()*scale,getImg().getHeight()*scale);
	}
	public BufferedImage getImg() {
		return img;
	}
	public void setImg(BufferedImage img) {
		this.img = img;
	}
}

RaceCar.java

The power of Subclasses-- just a tweak and your only change the methods you want to modify, and inherit the ones you want to keep.

import java.awt.image.BufferedImage;
import java.util.ArrayList;

public class RaceCar extends Car
{

	public RaceCar(int x, int y, int scale) {
		super(x, y, scale);
		initImg();
	}
	public void initImg(){
		ArrayList<String> a = new ArrayList<String>();
		a.add("0444440000000000");
		a.add("0444440004444400");
		a.add("0444440004444400");
		a.add("0001000000010000");
		a.add("0555555555555500");
		a.add("1111115511155550");
		a.add("0045404551115555");
		a.add("0545454551115555");
		a.add(a.get(5));
		a.add(a.get(4));
		a.add(a.get(3));
		a.add(a.get(2));
		a.add(a.get(1));
		a.add(a.get(0));

		int rows=a.size();
		int cols=a.get(0).length();
		this.setImg(new BufferedImage(cols, rows, BufferedImage.TYPE_INT_ARGB)); 

		for (int r=0; r<rows; r++)
			for(int c=0; c<cols; c++){
				String row=a.get(r);
				String value = row.substring(c, c+1);
				int colorIndex = Integer.parseInt(value);
				int rgb = colors[colorIndex].getRGB();
				getImg().setRGB(c, r, rgb);

			}
	}
}

Lane.java

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.Timer;

public class Lane
{
	private Rectangle loc;
	private ArrayList<Car> cars;
	private Timer timer;
	private int restCount;
	public Lane(int speed, int width, int y, int height, ActionListener al) {
		loc = new Rectangle(0, y, width, height);
		setCars(new ArrayList<Car>());

		timer=new Timer(speed, al);
		timer.start();
	}
	public void drawRoad(Graphics g){
		g.setColor(Color.BLACK);
		g.fillRect(loc.x, loc.y, loc.width,loc.height);

	}
	public void drawCars(Graphics g){

		g.setColor(Color.YELLOW);
		for(Car c:getCars())
			c.draw(g);

	}
	public void move() {
		setRestCount(getRestCount() + 1);			
		if (Math.random()<0.04 && getRestCount() >50){
			getCars().add(new Car (loc.width, loc.y+loc.height/8, 6));
			setRestCount(0);
		}
		for(Car c:getCars()){
			c.moveX(-3);
		}
	    int i=0;
	    while(i<getCars().size()){
	    	Rectangle carLocation = getCars().get(i).getLocation();
	    	if(carLocation.x + carLocation.getWidth()<0)
	    		getCars().remove(i);
	    	else
	    		i++;
	    }

	}
	public boolean collidesWith(Rectangle location) {
		int i=0;
		while(i<getCars().size()){
			if (location.intersects(getCars().get(i).getLocation()))
				return true;
			i++;
		}
		return false;
	}
	public Timer getTimer(){
		return timer;
	}
	public int getRestCount() {
		return restCount;
	}
	public void setRestCount(int restCount) {
		this.restCount = restCount;
	}
	public ArrayList<Car> getCars() {
		return cars;
	}
	public void setCars(ArrayList<Car> cars) {
		this.cars = cars;
	}
	public Rectangle getLoc(){
		return loc;
	}
}

FastLane.java

Just lie Lane but has RaceCars.... again the power of inheritance!

import java.awt.Rectangle;
import java.awt.event.ActionListener;

public class FastLane extends Lane
{

	public FastLane(int speed, int width, int y, int height, ActionListener al) {
		super(speed, width, y, height, al);

	}
	public void move() {
		setRestCount(getRestCount() + 1);			
		if (Math.random()<0.04 && getRestCount() >50){
			getCars().add(new RaceCar (0-100, getLoc().y+getLoc().height/8, 6));
			setRestCount(0);
		}
		for(Car c:getCars()){
			c.moveX(5);
		}
	    int i=0;
	    while(i<getCars().size()){
	    	Rectangle carLocation = getCars().get(i).getLocation();
	    	if(carLocation.x > getLoc().width)
	    		getCars().remove(i);
	    	else
	    		i++;
	    }

	}	
}

FroggerGame.java

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class FroggerGame extends JPanel implements KeyListener, ActionListener
{
	public static int WIDTH=800;
	public static int HEIGHT=600;
	private Color bgColor;
    private Font titleFont, regularFont;
    private Frog frog;
    private int score, speed;
    private ArrayList<Lane> traffic;

    public FroggerGame()
    {

    	//initialize variables here...
    	bgColor = new Color(200, 70, 50);
    	titleFont = new Font("Roman", Font.BOLD, 18);
    	regularFont = new Font("Helvetica", Font.PLAIN, 12);
    	frog=new Frog(WIDTH/2,HEIGHT-60);
    	speed=6;//how much the frog moves each key press
    	traffic = new ArrayList<Lane>();
    	traffic.add(new Lane(40, WIDTH,450, 75, this));
    	traffic.add(new FastLane(20, WIDTH,320, 100, this));
    	traffic.add(new Lane(30, WIDTH,220, 75, this));
    	/// Add more lanes or Rivers here

    }
	public static void main(String[] args) {
		FroggerGame app= new FroggerGame();
		JFrame window = new JFrame("Frogger ");
		window.setSize(WIDTH, HEIGHT);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.getContentPane().add(app);
		window.addKeyListener(app);

		//window.pack();
		window.setVisible(true);

	}
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		g.setColor(bgColor);
		g.fillRect(0, 0, getWidth(),getHeight());
		g.setColor(Color.BLUE);
		g.setFont(titleFont);
		g.drawString("Frogger", 20, 20);
		g.setColor(Color.BLACK);
		g.setFont(regularFont);
		g.drawString("Score ="+score, 20, 40);
		for (Lane x:traffic){
			x.drawRoad(g);
		}
		frog.draw(g);
		for (Lane x:traffic){
			x.drawCars(g);
		}

	}
	// update is a workaround to cure Windows screen flicker problem
	public void update(Graphics g){
		paint(g);
	}

	// These 3 methods need to be declares to implement the KeyListener Interface
	@Override
	public void keyTyped(KeyEvent e) {}

	@Override
	public void keyPressed(KeyEvent e) {
		int keyCode=e.getKeyCode();
		if (keyCode==38){//up arrow
			frog.moveUp(speed/2);
		}else if (keyCode==40){//down
			frog.moveDown(speed);
		}else if (keyCode == 37){//left
			frog.moveLeft(speed);
		}else if (keyCode == 39){//right
			frog.moveRight(speed);
		}
		if (frog.isAlive())
			score = Math.min(6000-frog.getLocation().y*10, 6000);
		repaint();
	}

	@Override
	public void keyReleased(KeyEvent e) {}
	@Override
	public void actionPerformed(ActionEvent e) {
		for (Lane lane:traffic){
			if (e.getSource()==lane.getTimer()){
				lane.move();
				if (lane.collidesWith(frog.getLocation())){
					frog.setAlive(false);
				}	
			}
		}
		if (frog.isAlive())
			score--;
		repaint();
	}

}