Spielprogrammierung mit Java
HomeAufgabenDruckenJava-Online

Breakout Game (Stonebreaker)


Ziel des Spiels ist es, mit einem Schläger den Ball so zu lenken, dass er innerhalb 120 Sekunden möglichst viele "Mauersteine" trifft. Die getroffenen Steine werden entfernt. In der Mitte des Schlägers wird der Ball senkrecht reflektiert. Wenn der Ball am linken bzw. rechten Schlägerteil ankommt, wird er gegen links bzw. gegen rechts reflektiert. Bei jedem getroffenen Stein werden Punkte zum Totalscore hinzugezählt. Für Wenn der Ball den Schläger vefällt, ist das Spiel fertig.

Das Spiel wird mit einem Mausklick auf den Schläger oder auf einen Levelbutton gestartet. Die Steuerung erfolgt mit Links- bzw. Rechtsbewegung der Maus. Mit den Levelbuttons kann vor dem Spielbeginn oder während des Spiels die Geschwindigkeit geändert werden (Level 3 ist am schnellsten). Die Anzahl der erreichten Punkte wird in der Titelleiste angezeigt, die Zeit rechts unten. Nach dem "Game over" kann das Spiel mit einem Mausklick erneut gestartet werden.

 

Programmcode für lokale Bearbeitung downloaden: BreakoutGame.zip

Der Klassendesign besteht aus fünf Klassen: Applikationsklasse BreakoutGame.java, Breaker.java, BreakerBar.java, Brick.java und LevelButton.java. Das Layout, Maus- und Spielablaufsteuerung sind in der Applikationsklasse implementiert. In der Klasse Breaker wird geregelt, wie der Ball an den Wänden reflektiert werden soll. Die Klasse LevelButton enthält die Geschwindigkeites-Regelung.

Programmcode

// 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;
    }
  }
}