Skip to content

Instantly share code, notes, and snippets.

@frodosda
Created May 18, 2013 12:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frodosda/5604272 to your computer and use it in GitHub Desktop.
Save frodosda/5604272 to your computer and use it in GitHub Desktop.
BreakOut Game Code - Stanford CS106A Assignment 3 - My Solutiion (Comments in Portuguese)
/*
* File: Arkanoid.java
* -------------------
*
* This recreates the BreakOut Game - also known as Arkanoid. After clicking to start the user can control the
* paddle with the mouse and keyboard and has to hit the ball in order to destroy bricks on top.
*
* Some bricks drop stars of diferrent colors. Each color when picked up by the paddle has a different effect.
* The bricks and the items add to the overall score (some stars actually decrease the score when picked up).
*
* The game ends when all the bricks are destroyed (victory) or when all the lives are spent (defeat).
*/
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Arkanoid extends GraphicsProgram {
/** Width and height of application window in pixels */
public static final int APPLICATION_WIDTH = 400;
public static final int APPLICATION_HEIGHT = 600;
/** Dimensions of game board (usually the same) */
private static final int WIDTH = APPLICATION_WIDTH;
private static final int HEIGHT = APPLICATION_HEIGHT;
/** Dimensions of the paddle */
private static final int PADDLE_WIDTH = 60;
private static final int PADDLE_HEIGHT = 10;
/** Offset of the paddle up from the bottom */
private static final int PADDLE_Y_OFFSET = 30;
/** Number of bricks per row */
private static final int NBRICKS_PER_ROW = 10;
/** Number of rows of bricks */
private static final int NBRICK_ROWS = 10;
/** Separation between bricks */
private static final int BRICK_SEP = 4;
/** Width of a brick */
private static final int BRICK_WIDTH =
(WIDTH - (NBRICKS_PER_ROW -1) * BRICK_SEP) / NBRICKS_PER_ROW;
/** Height of a brick */
private static final int BRICK_HEIGHT = 8;
/** Radius of the ball in pixels */
private static final int BALL_RADIUS = 8;
/** Offset of the top brick row from the top */
private static final int BRICK_Y_OFFSET = 70;
/** Number of turns */
private static final int NTURNS = 3;
/** Comprimento da GLabel que mostra as Vidas */
private static final int VIDAS_LBL_WIDTH = 60;
/** Altura da GLabel as Vidas */
private static final int VIDAS_LBL_HEIGHT = 30;
/** Runs the Breakout program. */
public void run() {
addMouseListeners();
addKeyListeners();
criarNivel();
correrJogo();
}
private void criarNivel () {
criarTijolos();
criarRaquete();
criarBola();
criarInfo();
}
/** Cria o conjunto de Tijolos (10x10) do BreakOut centrados na horizontal e na posição vertical designada.
* Cada 2 filas possui uma cor diferente - do topo para baixo: VERMELHO, LARANJA, AMARELO, VERDE E AZUL CLARO. */
private void criarTijolos () {
item = new GEstrela (30); // Define os itens (estrelas) que saem dos tijolos
itemNoEcra = false;
nTijolos = 0; // define como 0 o número inicial de tijolos /tela em branco)
int offsetX = BRICK_SEP / 2; // Espaço em branco a acrescentar na horizontal para que a figura fique centrada no ecrã
for (int i = 0; i < NBRICK_ROWS; i++) { // Cria cada fila de Tijolos
for (int j = 0; j < NBRICKS_PER_ROW; j++) { // Cria cada Coluna de Tijolos
// Define o espaçamento entre cada tijolo na horizontal e vertical
double dx = (j * BRICK_WIDTH) + (j * BRICK_SEP);
double dy = BRICK_Y_OFFSET + (BRICK_HEIGHT * i) + (i * BRICK_SEP);
// Cria os tijolos coloridos e adiciona-os na tela.
tijolo = new GRect (offsetX + dx, dy, BRICK_WIDTH, BRICK_HEIGHT);
tijolo.setFilled(true);
tijolo.setColor (corDaFila(i));
add (tijolo);
nTijolos++; // conta o número de tijolos à medida que são criados
}
}
}
/** Vai atribuir as cores certas a cada fila de tijolos.
* @param numFila O número da fila em que os tijolos se encontram (sendo o número 0 a fila mais a topo)
* @return A cor adequada à fila */
private Color corDaFila (int numFila) {
switch (numFila) {
case 0: case 1: return Color.RED;
case 2: case 3: return Color.ORANGE;
case 4: case 5: return Color.YELLOW;
case 6: case 7: return Color.GREEN;
case 8: case 9: return Color.CYAN;
default: return Color.BLACK;
}
}
/** Cria a Raquete centrada horizontalmente no ecrã e na zona inferior da tela */
private void criarRaquete () {
nHitsRaquete = 0; // inicia a contagem de hits na raquete
raquete = new GRect (PADDLE_WIDTH, PADDLE_HEIGHT);
raquete.setFilled(true);
add (raquete, (WIDTH - PADDLE_WIDTH) / 2, HEIGHT - PADDLE_HEIGHT - PADDLE_Y_OFFSET);
}
/** Cria a bola e centra-a no ecrã */
private void criarBola () {
double x = ((WIDTH - 2 * BALL_RADIUS) / 2);
double y = ((HEIGHT - 2 * BALL_RADIUS) / 2);
bola = new GOval (x, y, 2 * BALL_RADIUS, 2 * BALL_RADIUS);
bola.setFilled (true);
add (bola);
}
/** Cria uma GLabel no canto inferior esquerdo com o número de vidas que o jogador tem e outra no canto
* inferior direito com a pontuação até ao momento */
private void criarInfo () {
// Define a pontuação inicial do jogador
score = 0;
vidas = new GLabel ("VIDAS: " + (NTURNS - vidasGastas), VIDAS_LBL_WIDTH, VIDAS_LBL_HEIGHT);
vidas.setFont("arial-bold-14");
vidas.setColor(Color.RED);
add (vidas, 10 , HEIGHT - vidas.getAscent() /2);
pontuacao = new GLabel ("PONTUAÇÃO: " + score);
pontuacao.setFont("arial-bold-14");
pontuacao.setColor(Color.BLUE);
add (pontuacao, WIDTH - pontuacao.getWidth() - 10 , HEIGHT - vidas.getAscent() /2);
}
/** Método que vai correr o jogo em si - contém o ciclo while principal, que será responsável pela animação
* dos movimentos no jogo - quer da bola, quer dos itens */
private void correrJogo () {
// define o número inicial de vidas gastas
vidasGastas = 0;
// Define a direcão inicial da bola e a velocidade da mesma
vx = rGen.nextDouble(1.0, 3.0); // direcção horizontal aleatória
if (rGen.nextBoolean(0.5)) vx = -vx;
vy = 2.0; // direcção vertical
mensagemInicial();
// O jogo corre até se gastarem todas as vidas (derrota) ou até se destruirem todos os tijolos (vitória)
while (vidasGastas < NTURNS && nTijolos > 0) {
moverBola();
verificarColisaoBola();
moverItens();
verificarColisaoItens();
}
fimDoJogo(); // corre as rotinas do final do jogo
}
/** Cria uma mensagem inicial ao jogador esperando que este carregue com o rato para iniciar o jogo */
private void mensagemInicial() {
GLabel mensInicial = new GLabel ("CLICA PARA COMEÇAR", 300, 300);
mensInicial.setFont("arial-bold-30");
mensInicial.setColor (Color.BLUE);
add (mensInicial, (WIDTH - mensInicial.getWidth()) / 2, (HEIGHT - mensInicial.getAscent()) / 2);
waitForClick();
remove (mensInicial);
}
/** Método que cria o movimento da bola e muda-a de direcção sempre que faz ricochete nas paredes. */
private void moverBola () {
// Faz com que a bola mude de direcção quando bate numa parede
if (bola.getX() >= WIDTH - bola.getWidth()) { // Parede Este
// Previne bug em que a bola por vezes ultrapassava parede (devido aos valores vx elevados)
bola.setLocation(WIDTH - bola.getWidth(), bola.getY());
vx = -vx;
} else if (bola.getX() <= 0) { // Parede Oeste
bola.setLocation(0, bola.getY());
vx = -vx;
} else if (bola.getY() >= (HEIGHT - bola.getHeight()) || bola.getY() <= 0) { // Parede Sul e Norte
vy = -vy;
if (bola.getY() >= (HEIGHT - bola.getHeight())){
perderVida(); // Se a bola ultrapassa a Parede Sul o jogador gasta uma vida
}
}
bola.move(vx, vy); // faz a bola mover-se na direcção X e Y
pause(10); // pausa o programa durante x tempo para dar o efeito da animação
}
/** Verifica se a bola colidiu ou não com um objecto (neste caso a raquete ou um tijolo), assumindo o
* comportamento adequado em função do objecto em causa */
private void verificarColisaoBola () {
GObject objColisao = getObjColisao(); // O objecto com que a bola colidiu
AudioClip clipRicochete = MediaTools.loadAudioClip("bounce.au"); // Som de Colisões
if (objColisao == raquete) { // Colisão com a raquete
vy = -vy; // Muda a tranjectória vertical da bola
// sobe ligeiramente a bola na altura da colisão para evitar que o a bola se "cole" à raquete quando
// se move esta última rapidamente - evita sobreponição de "colisões" com alterações de vy constantes
bola.setLocation(bola.getX(),bola.getY() - PADDLE_HEIGHT / 2);
// Altera a direcção da bola quando esta bate no bordo da raquete mais distante ao da direcção da bola
if ((bola.getX() < raquete.getX() + 0.20 * raquete.getWidth() && vx > 0)
|| (bola.getX() > raquete.getX() + 0.80 * raquete.getWidth() && vx < 0)) {
vx = -vx;
}
clipRicochete.play();
nHitsRaquete++; // Contabiliza o número de vezes que a bola bate na raquete
// Aumenta a velocidade da bola depois de 7, 21 e 45 hits seguidos
if (nHitsRaquete == 7 || nHitsRaquete == 21 || nHitsRaquete == 45) vx *= 2;
} else if (objColisao == vidas || objColisao == pontuacao || objColisao == item){
// por agora nada acontece
} else if (objColisao != null) { // Colisão com um tijolo
remove (objColisao); // remove o tijolo
nTijolos--; // desconta 1 ao número de tijolos
vy = -vy;
clipRicochete.play();
addPontuacao(objColisao.getColor()); // Adiciona os pontos correspondentes ao tijolo
// Cria a possibilidade de dropar um Item do tijolo, desde que não haja já um item no ecrã
if (itemNoEcra == false) itemAleatorio();
}
}
/** Descobre se os limites da bola (cantos do GRect que envolve o GOval) colidiram com um objecto
* e identifica-o
* @return O objecto que colidiu com a bola (ou null caso não tenha ocorrido colisão) */
private GObject getObjColisao () {
if (getElementAt (bola.getX(), bola.getY()) != null) { // Canto superior esquerdo
return getElementAt (bola.getX(), bola.getY());
} else if (getElementAt (bola.getX() + bola.getWidth(), bola.getY()) != null) { // Superior dto
return getElementAt (bola.getX() + bola.getWidth(), bola.getY());
} else if (getElementAt (bola.getX(), bola.getY() + bola.getWidth()) != null) { // Inf esq
return getElementAt (bola.getX(), bola.getY() + bola.getWidth());
} else if (getElementAt (bola.getX() + bola.getWidth(), bola.getY() + bola.getWidth()) != null) { // Inf dto
return getElementAt (bola.getX() + bola.getWidth(), bola.getY() + bola.getWidth());
} else {
return null;
}
}
/** Define uma dada probabilidade de um item sair e cria um item aleatóriamente quando isso acontece:
* Itens possíveis: Estrelas: Vermelha, Laranja, Verde, Magenta, Azul */
private void itemAleatorio () {
boolean saiItem = rGen.nextBoolean(0.35);
if (saiItem == true) {
int numItem = rGen.nextInt(1, 5);
switch (numItem) {
case 1: item.setColor(Color.RED); break;
case 2: item.setColor(Color.ORANGE); break;
case 3: item.setColor(Color.GREEN); break;
case 4: item.setColor(Color.MAGENTA); break;
default: item.setColor(Color.BLUE); break;
}
add (item, bola.getX(), bola.getY());
itemNoEcra = true;
}
}
/** Faz descer os itens até ao fundo do ecrã e remove-os quando lá chegam */
private void moverItens () {
if (item.getY() - item.getHeight() / 2 < HEIGHT) {
item.move(0, 2);
}
if (item.getY() - item.getHeight() / 2 >= HEIGHT) {
remove (item);
itemNoEcra = false;
}
}
/** Verifica se os itens que caem dos tijolos colidiram com a Raquete de Jogo e em caso afirmativo remove os
* itens do ecrã e atribui ao jogador o bónus respectivo */
private void verificarColisaoItens () {
GObject itemColisao = getRaqueteColisao();
if (itemColisao == item) {
addBonusItem(itemColisao.getColor());
remove (itemColisao);
itemNoEcra = false;
}
}
/** Divide a raquete em quatro pontos e detecta se qualquer um destes pontos colidiu com um objecto.
* @return O objecto que colidiu com a raquete (ou null caso não tenha ocorrido colisão) */
private GObject getRaqueteColisao () {
// Divide a Raquete horizontalmente em 4 pontos
double raqEsq = raquete.getX();
double raqCentrEsq = raquete.getX() + raquete.getWidth() / 3;
double raqCentrDto = raquete.getX() + 2 * raquete.getWidth() / 3;
double raqDto = raquete.getX() + raquete.getWidth();
// Superficie vertical imediatamente superior à raquete (para evitar que detecte a própria raquete)
double raqSup = raquete.getY() - 1;
if (getElementAt (raqEsq, raqSup) != null) {
return getElementAt (raqEsq, raqSup);
}else if (getElementAt (raqCentrEsq, raqSup) != null) {
return getElementAt (raqCentrEsq, raqSup);
}else if (getElementAt (raqCentrDto, raqSup) != null) {
return getElementAt (raqCentrDto, raqSup);
}else if (getElementAt (raqDto, raqSup) != null) {
return getElementAt (raqDto, raqSup);
} else {
return null;
}
}
/** Adiciona um determinado efeito cada vez que um determinado item é apanhado pela raquete
* ESTRELA VERDE: Aumenta o tamanho da bola e diminui ligeiramente a velocidade (Vale 3000 pontos)
* ESTRELA VERMELHA: Diminui o tamanho da bola e aumenta a velocidade (Subtrai 3000 pontos)
* ESTRELA LARANJA: 75% de Diminuir a velocidade (4000 pontos) e 25% de Aumentar a Velocidade (-5000 pontos)
* ESTRELA AZUL: Aumenta o tamanho da raquete - até 3x mais (Vale 3000 pontos)
* ESTRELA MAGENTA: Diminui o tamanho da raquete - até 3x menos (Subtrai 3000 pontos)
* @param corEstrela A cor da estrela que cai */
private void addBonusItem (Color corEstrela) {
if (corEstrela.equals(Color.GREEN)) {
bola.setSize(bola.getWidth() * 1.5, bola.getHeight() * 1.5);
bola.setColor(Color.GREEN);
vx /= 1.5;
score += 3000;
} else if (corEstrela.equals(Color.RED)) {
bola.setSize(bola.getWidth() / 1.5, bola.getHeight() / 1.5);
bola.setColor(Color.RED);
vx *= 2;
score -= 3000;
} else if (corEstrela.equals(Color.ORANGE)) {
boolean aleat = rGen.nextBoolean(0.75);
if (aleat == true) vx /= 2; score += 4000;
if (aleat == false) vx *= 2; score -= 5000;
} else if (corEstrela.equals(Color.BLUE)) {
if (raquete.getWidth() <= PADDLE_WIDTH * 3) {
raquete.setSize(raquete.getWidth() * 1.5, PADDLE_HEIGHT);
raquete.setColor(Color.BLUE);
score += 3000;
}
} else if (corEstrela.equals(Color.MAGENTA)) {
if (raquete.getWidth() >= PADDLE_WIDTH / 3) {
raquete.setSize(raquete.getWidth() / 1.5, PADDLE_HEIGHT);
raquete.setColor(Color.MAGENTA);
score -= 3000;
}
actualizarPontuacao(); // Actualiza a pontuação
}
}
/** Atribui uma determinada pontuação a cada tijolo dependendo da sua cor
* @param corTijolo A cor do tijolo atingido */
private void addPontuacao (Color corTijolo) {
if (corTijolo.equals(Color.CYAN)) score += 100;
if (corTijolo.equals(Color.GREEN)) score += 200;
if (corTijolo.equals(Color.YELLOW)) score += 500;
if (corTijolo.equals(Color.ORANGE)) score += 1000;
if (corTijolo.equals(Color.RED)) score += 2500;
actualizarPontuacao(); // actualiza a pontuação no ecrã
}
/** Método coadjuvante que actualiza a pontuação no canto inferior direito do ecrã de jogo */
private void actualizarPontuacao () {
pontuacao.setLabel(("PONTUAÇÃO: " + score));
pontuacao.setLocation(WIDTH - pontuacao.getWidth() - 10 , HEIGHT - vidas.getAscent() /2);
}
/** Determina o que acontece quando o jogador perde uma vida. Prepara o jogo para uma nova tentativa,
* reinicializando a bola e a raquete e descontando uma das vidas. */
private void perderVida () {
vidasGastas++;
vidas.setLabel("VIDAS: " + (NTURNS - vidasGastas)); // Actualiza informação das vidas
if (vidasGastas < NTURNS) { // Se o jogador ainda tiver vidas faz um mini-reset ao campo de jogo
remove (bola);
remove (raquete);
criarBola();
criarRaquete();
nHitsRaquete = 0;
vx = rGen.nextDouble(1.0, 3.0);
if (rGen.nextBoolean(0.5)) vx = -vx;
vy = -vy; // para que a bola caia na direcão certa
mensagemInicial();
}
}
/** Cria as mensagens do Final do Jogo quer o resultado seja uma vitória ou uma derrota do jogador.
* Mostra essa mensagem juntamente com a pontuação adquirida */
private void fimDoJogo () {
GLabel mensFinal = new GLabel ("");
mensFinal.setFont("arial-bold-30");
pontuacao.setFont("arial-bold-30");
score *= (NTURNS - vidasGastas);
if (vidasGastas == NTURNS) { // Mensagem de Derrota - Perdeu todas as vidas
mensFinal.setLabel("GAME OVER!");
mensFinal.setColor (Color.RED);
} else if (nTijolos == 0) { // Mensagem de vitória - destruiu todos os tijolos
mensFinal.setLabel("GANHASTE!");
mensFinal.setColor (Color.GREEN);
}
removeAll(); // remove todos os elementos do jogo
add (mensFinal, (WIDTH - mensFinal.getWidth()) / 2, (HEIGHT - mensFinal.getAscent()) / 2);
add (pontuacao, (WIDTH - pontuacao.getWidth()) / 2, (HEIGHT - pontuacao.getAscent()) / 1.5);
}
/**Alinha o centro horizontal da raquete sobre o ponteiro do rato, fazendo com que a raquete acompanhe
* o movimento horizontal do rato até que esta chegue aos limites do ecrã, nunca os ultrapassando */
public void mouseMoved (MouseEvent e) {
double centroRaq = PADDLE_WIDTH / 2; // define o centro da raquete
// alinha o centro horizontal da raquete com o ponteiro do rato e fá-la acompanhar o seu movimento
raquete.setLocation(e.getX() - centroRaq, HEIGHT - PADDLE_HEIGHT - PADDLE_Y_OFFSET);
limitarRaquete(); // evita que a raquete ultrapasse os limites do ecrã
}
/** Permite o controlo da raquete com o teclado - utilizando a seta para a esquerda ou para a direita */
public void keyPressed (KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT: raquete.move(-WIDTH / 15, 0); break;
case KeyEvent.VK_RIGHT: raquete.move(WIDTH / 15, 0); break;
}
limitarRaquete(); // evita que a raqueta saia do ecrã
}
/** Evita que a Raquete saia do ecrã quando está a ser manipulada pelo rato ou pelo teclado */
private void limitarRaquete () {
if (raquete.getX() < 0) {
raquete.setLocation(0, HEIGHT - PADDLE_HEIGHT - PADDLE_Y_OFFSET);
} else if (raquete.getX() + raquete.getWidth() > WIDTH) {
raquete.setLocation(WIDTH - raquete.getWidth(), HEIGHT - PADDLE_HEIGHT - PADDLE_Y_OFFSET);
}
}
private double vx, vy; // Variáveis que definem a deslocação / velocidade horizontal e vertical da bola
private boolean itemNoEcra; // Determina se o item que cai se encontra presente no ecrã de jogo
private int score; // A pontuação do jogo
private int nHitsRaquete; // O número de vezes que a bola bateu na raquete
private int nTijolos; // O número de tijolos em jogo
private int vidasGastas; // O número de vidas perdidas
private GEstrela item; // Variável de instância que define cada estrela
private GLabel pontuacao; // Variável de instância que define o texto com a pontuação
private GLabel vidas; // Variável de instância que define o texto com o número de vidas
private GRect tijolo; // Variável de instância que define cada tijolo
private GRect raquete; // Variável de instância que define a raquete
private GOval bola; // Variável de instância que define a bola
// Gerador de Números Aleatórios
private RandomGenerator rGen = RandomGenerator.getInstance();
}
/** Ficheiro: GEstrela
*
* Define uma nova classe GObject que aparece como uma estrela de 5 pontas
*/
import acm.graphics.*;
public class GEstrela extends GPolygon {
/**
* Cria uma nova GEstrela centrada no ponto de origem com uma largura horizontal especifica.
* @param largura A largura da estrela
*/
public GEstrela (double largura) {
double dx = largura / 2;
double dy = dx * GMath.tanDegrees(18);
double limite = largura / 2 - dy * GMath.tanDegrees(36);
addVertex (-dx, -dy);
int angulo = 0;
for (int i = 0; i < 5; i++) {
addPolarEdge (limite, angulo);
addPolarEdge (limite, angulo + 72);
angulo -= 72;
setFilled(true);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment