GameGrid: Game programming with Java

Research project PHBern  
HomePrintJava-Online

Breakout Game (Stonebreaker)


The goal of this game is to guide the ball with the help of the paddle and to hit and remove all stones within 120 seconds. At the middle of the board, the ball is reflected perpendicular. If the ball hits the paddle on the left or on the right it is reflected left or right. Each stone hit adds points to the total score and the stone is removed. If the paddle misses the ball, the game is over.

The game is started by clicking one of the level buttons or the paddle. The paddle can be controlled by moving the mouse left and right. Clicking on the level buttons at the start or during the game, the speed of the ball can be set or changed (level 3 being the fastest). The total score is shown in the title bar and the time left in the bottom right corner. After the game is over, it can be restarted by clicking into the game window.

Run this example

Edit this example in the Online-Editor

 

Download the program code (BreakoutGame.zip)

The class design consists of five classes: the application class BreakoutGame.java and the actor classes Breaker.java, BreakerBar.java, Brick.java and LevelButton.java. The layout, the mouse and game controls are implemented in the application class. The class Breaker controls the reflections of the ball on the window's borders. The class LevelButton defines the different speeds.

Program code

// BreakoutGame.java

import ch.aplu.jgamegrid.*;
import java.awt.*;

public class BreakoutGame extends GameGrid implements GGMouseListenerGGActorCollisionListener
{
  private int points;
  private static final int TIMELIMIT = 120; //in seconds
  private static final int STARTSPEED = 35;
  private long startTime;
  private BreakerBar bar;
  private Breaker breaker;
  private boolean gameOver, startingStage;
  private static final Location startLocation = new Location(400, 463);

  public BreakoutGame()
  {
    super(800, 550, 1, false);
    getBg().setBgColor(Color.black);
    bar = new BreakerBar();
    bar.setCollisionRectangle(new Point(0, 0), 60, 15);
    breaker = new Breaker(this);
    breaker.setCollisionCircle(new Point(0, 0), 10);
    addActor(breaker, startLocation);
    breaker.addCollisionActor(bar);
    breaker.addActorCollisionListener(this);
    reset();
    setSimulationPeriod(STARTSPEED);
    for (int = 1; i <= 3; i++)
    {
      LevelButton lvlBtn = new LevelButton(i);
      addActor(lvlBtn, new Location(* 110 - 50, nbVertCells - 25));
      if (i == 1) //start with level 1
        lvlBtn.show(1);
    }
    addActor(bar, new Location(nbHorzCells / 2, nbVertCells - 70));
    addMouseListener(thisGGMouse.move | GGMouse.lClick);
    getBg().setPaintColor(Color.red);
    show();
    doRun();
  }

  public void act()
  {
    if (!(startingStage || gameOver))
    {
      getBg().clear();
      long timeLeft = (TIMELIMIT * 1000 + startTime - System.currentTimeMillis()) / 1000;
      if (timeLeft > 0)
        getBg().drawText(timeLeft + " seconds left"new Point(600, 540));
      else
        gameOver();
    }
  }

  public void reset()
  {
    setTitle("Breaker-Game, start by clicking. Break as many bricks as possible in " + TIMELIMIT + " seconds");
    breaker.setActEnabled(false);
    getBg().clear();
    points = 0;
    startTime = 0;
    gameOver = false;
    startingStage = true;
    removeActors(Brick.class);
    breaker.setLocation(startLocation);
    bar.setLocation(new Location(nbHorzCells / 2, nbVertCells - 70));
    breaker.setDirection((Math.random() * 120 - 60) + 270); //upwards, but random
    for (int = 0; j < 10; j++)
    {
      for (int = 0; i < 13; i++)
      {
        if (Math.random() < 0.85)
        { //erstelle nur 85 % der Steine
          Brick brick = new Brick(/ 3);
          brick.setCollisionRectangle(new Point(0, 0), 50, 20);
          breaker.addCollisionActor(brick);
          addActor(brick, new Location(* 51 + 90, 21 * + 60));
        }
      }
    }
  }

  public boolean mouseEvent(GGMouse mouse)
  {
    switch (mouse.getEvent())
    {
      case GGMouse.lClick:
        if (gameOver)
        {
          reset();
        }
        else if (startTime == 0)
        { //start game
          startTime = System.currentTimeMillis();
          breaker.setActEnabled(true);
          startingStage = false;
        }
        break;

      case GGMouse.move:
        bar.setX(mouse.getX());
        if (startingStage)
          breaker.setX(mouse.getX());
        break;
    }
    return true;
  }

  public int collide(Actor actor1, Actor actor2)
  {
    double dir = actor1.getDirection();
    //how did they hit each other?
    double hitDirection = actor2.getLocation().getDirectionTo(actor1.getLocation());
    System.out.println("Collision! " + actor1 + " vs " + actor2 + " hitDir: " + hitDirection);
    System.out.println("direction before: " + actor1.getDirection());
    if (actor2.getClass().equals(Brick.class))
    { //hit brick
      actor1.setDirection(reflectPhysicallyCorrect(actor1.getLocation(), actor2.getLocation(), dir));
      points += 10;
      if (getActors(Brick.class).size() == 0)
        gameOver();
      else
        setTitle("Breaker-Game   Points: " + points);
      removeActor(actor2);
      System.out.println("direction after: " + actor1.getDirection());
      return 0; //be immediately ready for next collision
    }
    else
    //hit pad
      if (actor1.getY() <= actor2.getY()) // breaker has to be higher than bar
        actor1.setDirection(reflectWithHitZones(actor1.getLocation(), actor2.getLocation()));
      return 10; //don't hit for another 10 cycles
    }
  }

  private double reflectWithHitZones(Location loc1, Location loc2)
  {
    int distance = loc1.x - loc2.x;
    int dir = (int)(distance * 2.5);
    System.out.println(distance + " -> winkel: " + dir);
    return dir - 90;
    //TODO: what if exactly 90°
  }

  private double reflectPhysicallyCorrect(Location loc1, Location loc2, double dir)
  {
    int xMovement = loc2.x - loc1.x;
    int yMovement = loc2.y - loc1.y;
    if (Math.abs(xMovement) <= 25) //von oben und unten
      dir = 360 - dir;
    else if (Math.abs(yMovement) <= 10) //von links und rechts
      dir = 180 - dir;
    else
      System.out.println("omg, none of the cases!");
    return dir;
  }

  public void gameOver()
  {
    gameOver = true;
    breaker.setActEnabled(false);
    long elapsedTime = (System.currentTimeMillis() - startTime/ 1000;
    setTitle("Game Over! " + points + " points " + " in " + elapsedTime + " seconds. Reset by clicking.");
  }

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

// -------class Breaker -------------
class Breaker extends Actor
{
  private double speed;
  private BreakoutGame gg;

  public Breaker(BreakoutGame gg)
  {
    super("sprites/breaker.png");
    this.gg = gg;
  }

  public void act()
  {
    double dir = getDirection();
    if (getY() > this.gameGrid.nbVertCells - 60)
    { //breaker has dropped
      gg.gameOver();
    }
    //collision with wall:
    if (getX() < 10)
    {
      dir = 180 - dir;
    }
    if (getX() > 790)
    {
      dir = 180 - dir;
    }
    if (getY() < 10)
    {
      dir = 360 - dir;
    }
    setDirection(dir);
    move();
  }
}

// -------class Breakerbar --------------
class BreakerBar extends Actor
{
  public BreakerBar()
  {
    super("sprites/bar2.png");
  }
}

// ---------class Brick --------------------

class Brick extends Actor
{
  public Brick(int sprite)
  {
    super("sprites/brickS.png"4);
    show(sprite);
  }
}

// ---------class LevelButton ----------------

class LevelButton extends Actor implements GGMouseTouchListener
{
  private int level;

  public LevelButton(int level)
  {
    super("sprites/Level" + level + ".png"2);
    this.level = level;
    setMouseTouchImage();
    this.addMouseTouchListener(this, GGMouse.lClick);
  }

  public void mouseTouched(Actor actor1, GGMouse arg1, Point arg2)
  {
    for (Actor act : gameGrid.getActors(LevelButton.class)//set all buttons to gray
      act.show(0);
    this.show(1);
    switch (level)  //change speed
    {
      case 1:
        gameGrid.setSimulationPeriod(35);
        break;
      case 2:
        gameGrid.setSimulationPeriod(25);
        break;
      case 3:
        gameGrid.setSimulationPeriod(15);
        break;
    }
  }
}