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
- make it easier for the player to get across. Bigger faster frog, slower less frequent cars are all good ideas.
- Make a Bike class that is a subclass of Car, which is skinny and easy to avoid.
Challenges
- Make a subclass of
Lane
calledRiver
, and new class callLog
. 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(); } }