Con esta estructura y clases es que finalizaremos el proyecto. Es importante destacar que el código de las clases se ha abordado en su mayor parte en las actividades anteriores, por lo que no es necesario volver a explicarlo. Sin embargo, se pueden realizar algunas modificaciones para mejorar la legibilidad y el rendimiento del código. A continuación, se presentan las modificaciones que se pueden realizar en cada una de las clases.
1. Clase FontCache
package org.brick_breaker.cache;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
public class FontCache {
public static final Map<String, Font> cache = new HashMap<>();
private static final FontCache INSTANCE = new FontCache();
private FontCache() {
}
public static FontCache getInstance() {
return INSTANCE;
}
public static Font addFont(String fontName, String fontPath) {
Font font;
if (!cache.containsKey(fontName)) {
font = FontLoader.loadFont(fontPath);
cache.put(fontName, font);
} else
font = cache.get(fontName);
return font;
}
public static Font getFont(String fontName, int style, int size) {
return cache.getOrDefault(fontName, Font.getFont("Arial")).deriveFont(style, size);
}
public static Font getFont(String fontName) {
return getFont(fontName, Font.PLAIN, 12);
}
public static Font getFont(String fontName, int size) {
return getFont(fontName, Font.PLAIN, size);
}
}
2. Clase FontLoader
package org.brick_breaker.cache;
import javax.swing.*;
import java.awt.*;
import java.io.File;
public class FontLoader {
public static Font loadFont(String path) {
try {
return Font.createFont(Font.TRUETYPE_FONT, new File(path)).deriveFont(12f);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "Error al cargar la fuente: " + path,
"Error", JOptionPane.ERROR_MESSAGE);
}
return Font.getFont("Arial").deriveFont(12f);
}
}
3. Clase SpriteCache
package org.brick_breaker.cache;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.util.HashMap;
public class SpriteCache {
private final HashMap<String, BufferedImage> cache = new HashMap<>();
private final HashMap<String, ImageIcon> gifCache = new HashMap<>();
private static final SpriteCache INSTANCE = new SpriteCache();
private SpriteCache() {
}
public static SpriteCache getInstance() {
return INSTANCE;
}
public void addImage(String name, BufferedImage image) {
cache.putIfAbsent(name, image);
}
public void addImage(String name, ImageIcon image) {
gifCache.putIfAbsent(name, image);
}
public BufferedImage getImage(String name) {
return cache.getOrDefault(name, null);
}
public ImageIcon getImageIcon(String name) {
return gifCache.getOrDefault(name, null);
}
}
4. Clase SpriteLoader
package org.brick_breaker.cache;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class SpriteLoader {
private static final String SPRITES_PATH = "image/";
public static BufferedImage loadImage(String fileName) {
try {
return ImageIO.read(new File(SPRITES_PATH + fileName));
} catch (IOException e) {
JOptionPane.showConfirmDialog(null,
"Error al cargar la imagen: " + fileName,
"Error", JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE);
}
return null;
}
public static ImageIcon loadGif(String fileName) {
try {
return new ImageIcon(SPRITES_PATH + fileName);
} catch (Exception e) {
JOptionPane.showConfirmDialog(null,
"Error al cargar la imagen: " + fileName,
"Error", JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE);
return null;
}
}
}
5. Clase Level
package org.brick_breaker.game;
import org.brick_breaker.sprites.bricks.Brick;
import org.brick_breaker.sprites.bricks.BrickType;
import org.brick_breaker.utils.Randomized;
import java.awt.*;
import java.io.Serializable;
public class Level implements Serializable {
public static final int ROW_MARGIN = 18;
public static final int COLUMN_MARGIN = 19;
public static final int LEVEL_WIDTH = 10;
public static final int LEVEL_HEIGHT = 10;
public static int levelNumber = 1;
private Brick[][] bricks;
private final String backgroundName;
private final String musicName;
private final int currentLevel;
public Level() {
this.backgroundName = "bg_level_" + levelNumber;
this.musicName = "m_level_" + levelNumber;
bricks = new Brick[LEVEL_WIDTH][LEVEL_HEIGHT];
currentLevel = levelNumber++;
populateLevel();
}
private void populateLevel() {
BrickType brickType;
Point position;
for (int row = 0; row < LEVEL_WIDTH; row++) {
for (int column = 0; column < LEVEL_HEIGHT; column++) {
// Se genera un número aleatorio para seleccionar un tipo de ladrillo.
brickType = BrickType.values()[Randomized.randomInt(0, BrickType.values().length - 1)];
// Se generá la posición del ladrillo.
position = new Point(column * Brick.BRICK_WIDTH + ROW_MARGIN,
row * Brick.BRICK_HEIGHT + COLUMN_MARGIN);
// Se crea el ladrillo según el tipo.
switch (brickType) {
case RED -> bricks[row][column] = new Brick(position, "red_brick", BrickType.RED);
case BLUE -> bricks[row][column] = new Brick(position, "blue_brick", BrickType.BLUE);
case GREEN -> bricks[row][column] = new Brick(position, "green_brick", BrickType.GREEN);
case YELLOW -> bricks[row][column] = new Brick(position, "yellow_brick", BrickType.YELLOW);
}
}
}
}
public Brick[][] getBricks() {
return bricks;
}
public void setBricks(Brick[][] bricks) {
this.bricks = bricks;
}
public String getBackgroundName() {
return backgroundName;
}
public int getCurrentLevel() {
return currentLevel;
}
}
6. Clase Brick
package org.brick_breaker.sprites.bricks;
import org.brick_breaker.sprites.Sprite;
import org.brick_breaker.utils.collisions.CollisionManager;
import java.awt.*;
import java.io.Serializable;
public class Brick extends Sprite implements Serializable {
public final static int BRICK_WIDTH = 42;
public final static int BRICK_HEIGHT = 20;
public final static Dimension BRICK_SIZE = new Dimension(BRICK_WIDTH, BRICK_HEIGHT);
private final BrickType type;
private int life;
private boolean destroyed;
public Brick(Point position, String imageName, BrickType type) {
super(position, imageName, BRICK_SIZE);
this.type = type;
this.life = type.getLife();
this.destroyed = false;
}
@Override
public void addImageToCache() {
if (type != null)
type.loadSprite(imageName);
}
public void hit() {
life--;
if (life == 0) {
destroyed = true;
CollisionManager.getInstance().unregisterCollidable(this);
}
}
@Override
public String toString() {
return type.toString();
}
public boolean isDestroyed() {
return destroyed;
}
public int getScore() {
return type.getScore();
}
public BrickType getType() {
return type;
}
public void setDestroyed(boolean destroyed) {
this.destroyed = destroyed;
}
}
7. Clase BrickType
package org.brick_breaker.sprites.bricks;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import java.io.Serializable;
public enum BrickType implements Serializable {
YELLOW(1, 10),
RED(2, 20),
BLUE(3, 30),
GREEN(4, 40);
private final int life;
private final int score;
BrickType(int life, int score) {
this.life = life;
this.score = score;
}
public void loadSprite(String imageName) {
SpriteCache spriteCache = SpriteCache.getInstance();
switch (this) {
case YELLOW -> spriteCache.addImage(imageName, SpriteLoader.loadImage("bricks/brick-yellow.png"));
case RED -> spriteCache.addImage(imageName, SpriteLoader.loadImage("bricks/brick-red.png"));
case BLUE -> spriteCache.addImage(imageName, SpriteLoader.loadImage("bricks/brick-blue.png"));
case GREEN -> spriteCache.addImage(imageName, SpriteLoader.loadImage("bricks/brick-green.png"));
}
}
public int getLife() {
return life;
}
public int getScore() {
return score;
}
}
8. Clase Paddle
package org.brick_breaker.sprites.paddles;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import org.brick_breaker.sprites.Borders;
import org.brick_breaker.sprites.MovingSprite;
import org.brick_breaker.sprites.Resettable;
import org.brick_breaker.ui.panels.GamePanel;
import java.awt.*;
public class Paddle extends MovingSprite implements Resettable {
private static final int SPEED = 3;
public static final int INITIAL_PADDLE_X = 202;
public static final int INITIAL_PADDLE_Y = 588;
public static final Point INITIAL_PADDLE_POSITION = new Point(INITIAL_PADDLE_X, INITIAL_PADDLE_Y);
private PaddleType type;
public Paddle(PaddleType type) {
super(INITIAL_PADDLE_POSITION, type.getImageName(), type.getSize(), 0, 0);
this.type = type;
}
@Override
public void move() {
if (dx != 0) {
position.x += dx * SPEED;
if (position.x < Borders.LEFT_BAR.getImage().getWidth()) {
position.x = Borders.LEFT_BAR.getImage().getWidth();
} else if (position.x > GamePanel.GAME_WIDTH - getImage().getWidth()) {
position.x = GamePanel.GAME_WIDTH - getImage().getWidth();
}
}
}
@Override
protected void addImageToCache() {
SpriteCache spriteCache = SpriteCache.getInstance();
spriteCache.addImage(PaddleType.SMALL.getImageName(),
SpriteLoader.loadImage("paddle.png"));
spriteCache.addImage(PaddleType.MEDIUM.getImageName(),
SpriteLoader.loadImage("paddle.png"));
spriteCache.addImage(PaddleType.LARGE.getImageName(),
SpriteLoader.loadImage("paddle-large.png"));
spriteCache.addImage(PaddleType.SHOOTER.getImageName(),
SpriteLoader.loadImage("paddle-laser.png"));
}
public void changeType(PaddleType type) {
this.type = type;
this.imageName = type.getImageName();
setSize(type.getSize());
}
public PaddleType getType() {
return type;
}
@Override
public void resetPosition() {
position.x = INITIAL_PADDLE_X;
position.y = INITIAL_PADDLE_Y;
dy = 0;
dx = 0;
changeType(PaddleType.MEDIUM);
}
}
package org.brick_breaker.sprites;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import org.brick_breaker.sprites.bricks.Brick;
import org.brick_breaker.sprites.paddles.Paddle;
import org.brick_breaker.ui.panels.GamePanel;
import org.brick_breaker.utils.collisions.EdgeType;
import org.brick_breaker.utils.collisions.CollisionListener;
import org.brick_breaker.utils.collisions.CollisionManager;
import javax.swing.*;
import java.awt.*;
public class Ball extends MovingSprite implements Resettable, CollisionListener {
public static final int BALL_WIDTH = 20;
public static final int INITIAL_BALL_X = 224;
public static final int INITIAL_BALL_Y = 570;
public static final Point INITIAL_BALL_POSITION = new Point(INITIAL_BALL_X, INITIAL_BALL_Y);
public static final Dimension BALL_SIZE = new Dimension(BALL_WIDTH, BALL_WIDTH);
private int speed;
private boolean stop;
public Ball() {
super(INITIAL_BALL_POSITION, "ball", BALL_SIZE, 1, -1);
speed = 4;
stop = true;
CollisionManager.getInstance().addListener(this);
}
public Ball(Point startPosition) {
super(startPosition, "ball", BALL_SIZE, 1, -1);
speed = 4;
stop = true;
CollisionManager.getInstance().addListener(this);
}
@Override
public void onCollisionDetected(Sprite collider, Sprite collidedWith, EdgeType edgeType) {
// Se verifica si la pelota colisiona con otro objeto.
if (collider == this) {
if (collidedWith instanceof Paddle || collidedWith instanceof Brick || collidedWith instanceof Borders) {
// Se ajusta la posición de la pelota para que no se quede pegada al borde del objeto con el que colisionó.
switch (edgeType) {
case LEFT_EDGE -> getPosition().x = collidedWith.getPosition().x - getImageIcon().getIconWidth();
case RIGHT_EDGE -> getPosition().x = collidedWith.getPosition().x + collidedWith.getSize().width;
case TOP_EDGE -> getPosition().y = collidedWith.getPosition().y - getImageIcon().getIconHeight();
case BOTTOM_EDGE -> getPosition().y = collidedWith.getPosition().y + collidedWith.getSize().height;
}
// Se invierte la dirección de la pelota al colisionar con un borde o un ladrillo.
switch (edgeType) {
case LEFT_EDGE, RIGHT_EDGE -> setDx(-getDx());
case TOP_EDGE, BOTTOM_EDGE -> setDy(-getDy());
}
}
// Se determina el tipo de objeto con el que colisiona la pelota.
// Si es un ladrillo, se indicará al panel que lo elimine.
if (collidedWith instanceof Brick brick) {
brick.hit();
if (brick.isDestroyed()) {
GamePanel.removeBrick(brick);
}
}
// Si es un borde y además es el borde inferior, se eliminará la pelota.
if (collidedWith instanceof Borders) {
if (collidedWith == Borders.BOTTOM_BAR) {
GamePanel panel = GamePanel.getInstance();
panel.removeBall(this);
panel.getPaddle().resetPosition();
}
}
}
}
@Override
public void draw(Graphics2D g2d) {
Graphics2D g2 = (Graphics2D) g2d.create();
g2.drawImage(getImageIcon().getImage(), position.x, position.y, null);
g2.dispose();
}
@Override
public void move() {
if (!stop) {
position.x += dx * speed;
position.y += dy * speed;
} else {
// Si la pelota está detenida, se ajusta su posición a la posición de la paleta.
Paddle paddle = GamePanel.getInstance().getPaddle();
position.x = paddle.getPosition().x + (paddle.getSize().width / 2) - (getSize().width / 2);
position.y = paddle.getPosition().y - getSize().height;
}
}
@Override
public void resetPosition() {
// Se reinicia la posición de la pelota a la posición inicial.
position.x = INITIAL_BALL_X;
position.y = INITIAL_BALL_Y;
// Se reinicia la velocidad de la pelota.
speed = 3;
// Se reinicia la dirección de la pelota.
dx = 1;
dy = -1;
// Se reinicia el estado de la pelota.
stop = true;
}
@Override
public void addImageToCache() {
SpriteCache.getInstance().addImage(imageName, SpriteLoader.loadGif("ball.gif"));
}
public ImageIcon getImageIcon() {
return SpriteCache.getInstance().getImageIcon(getImageName());
}
// Getters y setters
public void setStop(boolean stop) {
this.stop = stop;
}
public boolean isStop() {
return stop;
}
}
11. Clase Borders
package org.brick_breaker.sprites;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import java.awt.*;
import java.awt.image.BufferedImage;
public class Borders extends Sprite {
public static final Borders LEFT_BAR = new Borders("leftBar", new Point(0, 0));
public static final Borders RIGHT_BAR = new Borders("rightBar", new Point(438, 0));
public static final Borders TOP_BAR = new Borders("topBar", new Point(18, 0));
public static final Borders BOTTOM_BAR = new Borders("topBar", new Point(18, 622));
private Borders(String imageName, Point location) {
super(location, imageName, new Dimension(0, 0));
addImageToCache();
}
@Override
protected void addImageToCache() {
SpriteCache spriteCache = SpriteCache.getInstance();
BufferedImage image = SpriteLoader.loadImage(imageName + ".png");
spriteCache.addImage(imageName, image);
if (image != null) {
setSize(new Dimension(image.getWidth(), image.getHeight()));
}
}
}
12. Clase Missile
package org.brick_breaker.sprites;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import org.brick_breaker.sprites.bricks.Brick;
import org.brick_breaker.ui.panels.GamePanel;
import org.brick_breaker.utils.collisions.CollisionListener;
import org.brick_breaker.utils.collisions.CollisionManager;
import org.brick_breaker.utils.collisions.EdgeType;
import java.awt.*;
public class Missile extends MovingSprite implements CollisionListener {
public static final int MISSILE_WIDTH = 16;
public static final int MISSILE_HEIGHT = 37;
public static final int MISSILE_SPEED = 5;
public static final Dimension MISSILE_SIZE = new Dimension(MISSILE_WIDTH, MISSILE_HEIGHT);
public Missile(Point position) {
super(position, "missile", MISSILE_SIZE, 0, MISSILE_SPEED);
// Se registra el misil en el gestor de colisiones.
CollisionManager.getInstance().addListener(this);
}
@Override
public void onCollisionDetected(Sprite collider, Sprite collidedWith, EdgeType edgeType) {
// Se verifica si el misil colisiona con otro objeto.
if (collider == this) {
if (collidedWith instanceof Brick brick) {
brick.hit();
if (brick.isDestroyed()) {
GamePanel.removeBrick(brick);
}
// Se destruye el misil al colisionar con un ladrillo.
GamePanel.removeMissile(this);
}
if (collidedWith instanceof Borders) {
// Se destruye el misil al colisionar con un borde.
GamePanel.removeMissile(this);
}
}
}
@Override
public void move() {
position.y -= dy;
}
@Override
public void addImageToCache() {
SpriteCache spriteCache = SpriteCache.getInstance();
spriteCache.addImage("missile", SpriteLoader.loadImage("missile.png"));
}
}
13. Clase MovingSprite
package org.brick_breaker.sprites;
import java.awt.*;
public abstract class MovingSprite extends Sprite {
protected int dx;
protected int dy;
public MovingSprite(Point position, String imageName, Dimension size, int dx, int dy) {
super(position, imageName, size);
this.dx = dx;
this.dy = dy;
}
public abstract void move();
public int getDx() {
return dx;
}
public void setDx(int dx) {
this.dx = dx;
}
public int getDy() {
return dy;
}
public void setDy(int dy) {
this.dy = dy;
}
}
14. Interfaz Resettable
package org.brick_breaker.sprites;
public interface Resettable {
void resetPosition();
}
15. Clase Sprite
package org.brick_breaker.sprites;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.utils.collisions.CollisionManager;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.Serializable;
public abstract class Sprite implements Serializable {
protected Point position;
protected String imageName;
protected Dimension size;
public Sprite(Point position, String imageName, Dimension size) {
this.position = position;
this.imageName = imageName;
this.size = size;
addImageToCache();
CollisionManager.getInstance().registerCollidable(this);
}
public void draw(Graphics2D g2d) {
Graphics2D g2 = (Graphics2D) g2d.create();
g2.drawImage(getImage(), position.x, position.y, null);
g2.dispose();
}
protected abstract void addImageToCache();
public Rectangle getBounds() {
return new Rectangle(position, size);
}
public BufferedImage getImage() {
return SpriteCache.getInstance().getImage(imageName);
}
public Point getPosition() {
return position;
}
public String getImageName() {
return imageName;
}
public Dimension getSize() {
return size;
}
public void setSize(Dimension size) {
this.size = size;
}
}
16. Clase Bonus
package org.brick_breaker.sprites.bonus;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import org.brick_breaker.sprites.MovingSprite;
import org.brick_breaker.sprites.Sprite;
import org.brick_breaker.sprites.bricks.Brick;
import org.brick_breaker.sprites.paddles.Paddle;
import org.brick_breaker.ui.panels.GamePanel;
import org.brick_breaker.utils.collisions.CollisionListener;
import org.brick_breaker.utils.collisions.CollisionManager;
import org.brick_breaker.utils.collisions.EdgeType;
import javax.swing.*;
import java.awt.*;
import java.io.Serializable;
public class Bonus extends MovingSprite implements Serializable, CollisionListener {
private boolean active;
private BonusType type;
public Bonus(Point startPosition, BonusType type) {
super(startPosition, type.getImageName(), Brick.BRICK_SIZE, 0, 1);
this.type = type;
this.active = false;
CollisionManager.getInstance().addListener(this);
}
@Override
public void move() {
position.y += dy;
}
@Override
public void draw(Graphics2D g2d) {
Graphics2D g2 = (Graphics2D) g2d.create();
g2.drawImage(getImageIcon().getImage(), position.x, position.y, null);
g2.dispose();
}
public ImageIcon getImageIcon() {
return SpriteCache.getInstance().getImageIcon(getImageName());
}
@Override
public void onCollisionDetected(Sprite collider, Sprite collidedWith, EdgeType edgeType) {
if (collider == this) {
if (collidedWith == null) {
GamePanel.removeBonus(this);
}
if (collidedWith instanceof Paddle) {
switch (edgeType) {
case TOP_EDGE -> getPosition().y = collidedWith.getPosition().y - getImageIcon().getIconHeight();
case BOTTOM_EDGE -> getPosition().y = collidedWith.getPosition().y + collidedWith.getSize().height;
}
GamePanel.removeBonus(this);
getType().activateBonus();
}
}
}
@Override
public void addImageToCache() {
SpriteCache cache = SpriteCache.getInstance();
if (type != null) {
switch (type) {
case L -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/L.gif"));
case S -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/S.gif"));
case M -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/M.gif"));
case R -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/R.gif"));
case E -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/E.gif"));
case B -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/B.gif"));
case C -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/C.gif"));
case D -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/D.gif"));
case P -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/P.gif"));
case T -> cache.addImage(imageName, SpriteLoader.loadGif("bonus/T.gif"));
}
}
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public BonusType getType() {
return type;
}
public void setType(BonusType type) {
this.type = type;
}
}
17. Clase BonusType
package org.brick_breaker.sprites.bonus;
import org.brick_breaker.sprites.paddles.PaddleType;
import org.brick_breaker.ui.panels.GamePanel;
import java.io.Serializable;
/**
* Esta clase permitirá definir los tipos de bonus posibles dentro del juego, la lista de opciones es la siguientes:
*/
public enum BonusType implements Serializable {
L("l_bonus") {
@Override
public void activateBonus() {
GamePanel.getInstance().addLife();
}
},
S("s_bonus") {
@Override
public void activateBonus() {
}
},
M("m_bonus") {
@Override
public void activateBonus() {
}
},
R("r_bonus") {
@Override
public void activateBonus() {
}
},
E("e_bonus") {
@Override
public void activateBonus() {
}
},
B("b_bonus") {
@Override
public void activateBonus() {
}
},
C("c_bonus") {
@Override
public void activateBonus() {
}
},
D("d_bonus") {
@Override
public void activateBonus() {
}
},
P("p_bonus") {
@Override
public void activateBonus() {
}
},
T("t_bonus") {
@Override
public void activateBonus() {
}
};
private final String imageName;
BonusType(String imageName) {
this.imageName = imageName;
}
public String getImageName() {
return imageName;
}
public abstract void activateBonus();
}
18. Clase ButtonState
package org.brick_breaker.ui.buttons;
import javax.swing.*;
public enum ButtonState {
NORMAL,
ROLLOVER,
PRESSED,
DISABLED;
public static ButtonState getButtonState(JButton button) {
if (button == null) {
throw new IllegalArgumentException("Button cannot be null");
}
if (button.isEnabled()) {
if (button.getModel().isPressed()) {
return ButtonState.PRESSED;
} else if (button.getModel().isRollover()) {
return ButtonState.ROLLOVER;
} else {
return ButtonState.NORMAL;
}
} else {
return ButtonState.DISABLED;
}
}
}
19. Clase ThreePartButtonUI
package org.brick_breaker.ui.buttons;
import org.brick_breaker.cache.FontCache;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import org.brick_breaker.ui.labels.ScoreLabelUI;
import javax.swing.*;
import javax.swing.plaf.basic.BasicButtonUI;
import java.awt.*;
import java.awt.image.BufferedImage;
public class ThreePartButtonUI extends BasicButtonUI {
private final BufferedImage[] images = new BufferedImage[3];
private final int[] imageWidths = new int[3];
private final int imageHeight;
// Carga de imágenes estáticas
static {
SpriteCache cache = SpriteCache.getInstance();
cache.addImage("leftSideNormal", SpriteLoader.loadImage("buttons/normal/leftSide.png"));
cache.addImage("leftSideHover", SpriteLoader.loadImage("buttons/hover/leftSide.png"));
cache.addImage("leftSidePressed", SpriteLoader.loadImage("buttons/pressed/leftSide.png"));
cache.addImage("leftSideDisabled", SpriteLoader.loadImage("buttons/disabled/leftSide.png"));
cache.addImage("middleNormal", SpriteLoader.loadImage("buttons/normal/middleSide.png"));
cache.addImage("middleHover", SpriteLoader.loadImage("buttons/hover/middleSide.png"));
cache.addImage("middlePressed", SpriteLoader.loadImage("buttons/pressed/middleSide.png"));
cache.addImage("middleDisabled", SpriteLoader.loadImage("buttons/disabled/middleSide.png"));
cache.addImage("rightSideNormal", SpriteLoader.loadImage("buttons/normal/rightSide.png"));
cache.addImage("rightSideHover", SpriteLoader.loadImage("buttons/hover/rightSide.png"));
cache.addImage("rightSidePressed", SpriteLoader.loadImage("buttons/pressed/rightSide.png"));
cache.addImage("rightSideDisabled", SpriteLoader.loadImage("buttons/disabled/rightSide.png"));
FontCache.addFont("diffusion", "fonts/diffusion.ttf");
}
public ThreePartButtonUI() {
images[0] = SpriteCache.getInstance().getImage("leftSideNormal");
images[1] = SpriteCache.getInstance().getImage("middleNormal");
images[2] = SpriteCache.getInstance().getImage("rightSideNormal");
imageWidths[0] = images[0].getWidth(null);
imageWidths[1] = images[1].getWidth(null);
imageWidths[2] = images[2].getWidth(null);
imageHeight = images[0].getHeight(null);
}
@Override
public void paint(Graphics g, JComponent c) {
JButton button = (JButton) c;
Graphics2D g2 = (Graphics2D) g.create();
ScoreLabelUI.configureRenderingHints(g2);
// Determinar qué imágenes usar según el estado del botón
setButtonImages(button);
// Calcular dimensiones
FontMetrics fm = g2.getFontMetrics();
int textWidth = fm.stringWidth(button.getText());
int centerWidth = textWidth + 40; // 20px de padding a cada lado
// Dibujar las tres partes del botón
// Parte izquierda
g2.drawImage(images[0], 0, 0, imageWidths[0], imageHeight, null);
// Parte central (se estira según el texto)
g2.drawImage(images[1], imageWidths[0], 0, centerWidth, imageHeight, null);
// Parte derecha
g2.drawImage(images[2], imageWidths[0] + centerWidth, 0, imageWidths[2], imageHeight, null);
// Dibujar el texto
int textX = imageWidths[0] + (centerWidth - textWidth) / 2;
int textY = (imageHeight - fm.getHeight()) / 2 + fm.getAscent();
if (button.getModel().isPressed()) {
textX += 1;
textY += 1;
}
g2.setColor(button.getModel().isEnabled() ? button.getForeground() : Color.GRAY);
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON));
g2.drawString(button.getText(), textX, textY);
g2.dispose();
}
public static void configureButton(JButton button) {
button.setUI(new ThreePartButtonUI());
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.setFocusPainted(false);
button.setOpaque(false);
button.setHorizontalTextPosition(SwingConstants.CENTER);
button.setVerticalTextPosition(SwingConstants.CENTER);
button.setVerticalAlignment(SwingConstants.CENTER);
button.setHorizontalAlignment(SwingConstants.CENTER);
button.setFont(FontCache.getFont("diffusion").deriveFont(Font.BOLD, 20f));
}
@Override
public Dimension getPreferredSize(JComponent c) {
JButton button = (JButton) c;
FontMetrics fm = button.getFontMetrics(button.getFont());
int textWidth = fm.stringWidth(button.getText());
int centerWidth = textWidth + 18; // 9px de padding a cada lado
int width = imageWidths[0] + centerWidth + images[1].getWidth();
return new Dimension(width, imageHeight);
}
private void setButtonImages(JButton button) {
String stateName = switch (ButtonState.getButtonState(button)) {
case NORMAL -> "Normal";
case ROLLOVER -> "Hover";
case PRESSED -> "Pressed";
case DISABLED -> "Disabled";
};
images[0] = SpriteCache.getInstance().getImage("leftSide" + stateName);
images[1] = SpriteCache.getInstance().getImage("middle" + stateName);
images[2] = SpriteCache.getInstance().getImage("rightSide" + stateName);
}
}
20. Clase KeyboardAction
package org.brick_breaker.ui.events;
import org.brick_breaker.sprites.paddles.PaddleType;
import org.brick_breaker.ui.panels.GamePanel;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class KeyboardAction extends KeyAdapter {
GamePanel gamePanel;
public KeyboardAction(GamePanel gamePanel) {
this.gamePanel = gamePanel;
}
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
gamePanel.getPaddle().setDx(-1);
break;
case KeyEvent.VK_RIGHT:
gamePanel.getPaddle().setDx(1);
break;
case KeyEvent.VK_SPACE:
if (gamePanel.isGameRunning()) {
if (!gamePanel.getBalls().isEmpty() && gamePanel.getBalls().getFirst().isStop())
gamePanel.getBalls().getFirst().setStop(false);
if (gamePanel.getPaddle().getType() == PaddleType.SHOOTER)
gamePanel.addMissile();
}
break;
case KeyEvent.VK_F:
gamePanel.destroyAllBricks();
default:
break;
}
}
@Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
gamePanel.getPaddle().setDx(0);
break;
default:
break;
}
}
}
21. Clase StartButtonAction
package org.brick_breaker.ui.events;
import org.brick_breaker.ui.panels.GamePanel;
import org.brick_breaker.ui.windows.MainWindow;
import java.awt.event.ActionListener;
public class StartButtonAction implements ActionListener {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
GamePanel.getInstance().playGame();
MainWindow.getInstance().getStartButton().setEnabled(false);
MainWindow.getInstance().getStopButton().setEnabled(true);
}
}
22. Clase StopButtonAction
package org.brick_breaker.ui.events;
import org.brick_breaker.ui.panels.GamePanel;
import org.brick_breaker.ui.windows.MainWindow;
import java.awt.event.ActionListener;
public class StopButtonAction implements ActionListener {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
GamePanel.getInstance().stopGame();
MainWindow.getInstance().getStartButton().setEnabled(true);
MainWindow.getInstance().getStopButton().setEnabled(false);
}
}
23. Clase LifeLabelUI
package org.brick_breaker.ui.labels;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class LifeLabelUI extends ScoreLabelUI {
static {
SpriteCache cache = SpriteCache.getInstance();
cache.addImage("lifeBackground", SpriteLoader.loadImage("labels/life-label.png"));
cache.addImage("lifeIcon", SpriteLoader.loadImage("labels/life-count.png"));
}
/**
* Constructor de la clase ScoreLabelUI.
* Carga la imagen de fondo para la etiqueta de puntuación.
*/
public LifeLabelUI() {
background = SpriteCache.getInstance().getImage("lifeBackground");
}
/**
* FUnción que se encarga de pintar la etiqueta de puntuación.
*
* @param g El objeto Graphics utilizado para dibujar.
* @param c El componente JComponent que representa la etiqueta.
*/
@Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
// Configura los hints de renderizado
configureRenderingHints(g2);
// Dibuja la imagen de fondo
g2.drawImage(background, 0, 0, null);
// Dibuja el texto de la etiqueta
JLabel label = (JLabel) c;
FontMetrics fm = g2.getFontMetrics(label.getFont());
BufferedImage lifeIcon = SpriteCache.getInstance().getImage("lifeIcon");
int textWidth = fm.stringWidth(label.getText());
int textHeight = fm.getHeight();
int iconWidth = lifeIcon.getWidth();
int iconHeight = lifeIcon.getHeight();
int x = (background.getWidth() - textWidth) / 2;
int y = (background.getHeight() + textHeight) / 2;
int iconX = 2 + iconWidth / 2;
int iconY = ((background.getHeight() - iconHeight) / 2) + 3;
g2.drawImage(lifeIcon, iconX, iconY, null);
g2.setColor(label.getForeground());
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON));
g2.drawString(label.getText(), x, y);
g2.dispose();
}
public static void configureLabel(JLabel label) {
label.setUI(new LifeLabelUI());
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.CENTER);
label.setForeground(Color.WHITE);
label.setFont(new Font("Arial", Font.BOLD, 20));
}
}
24. Clase ScoreLabelUI
package org.brick_breaker.ui.labels;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import javax.swing.*;
import javax.swing.plaf.basic.BasicLabelUI;
import java.awt.*;
import java.awt.image.BufferedImage;
public class ScoreLabelUI extends BasicLabelUI {
protected BufferedImage background;
static {
SpriteCache cache = SpriteCache.getInstance();
cache.addImage("scoreBackground", SpriteLoader.loadImage("labels/score-label.png"));
}
public ScoreLabelUI() {
background = SpriteCache.getInstance().getImage("scoreBackground");
}
@Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
// Configura los hints de renderizado
configureRenderingHints(g2);
// Dibuja la imagen de fondo
g2.drawImage(background, 0, 0, null);
// Dibuja el texto de la etiqueta
JLabel label = (JLabel) c;
FontMetrics fm = g2.getFontMetrics(label.getFont());
int textWidth = fm.stringWidth(label.getText());
int textHeight = fm.getHeight();
int x = (background.getWidth() - textWidth) / 2;
int y = (background.getHeight() + textHeight) / 2;
g2.setColor(label.getForeground());
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON));
g2.drawString(label.getText(), x, y);
g2.dispose();
}
public static void configureRenderingHints(Graphics2D g2) {
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON));
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON));
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC));
}
public static void configureLabel(JLabel label) {
label.setUI(new ScoreLabelUI());
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.CENTER);
label.setForeground(Color.WHITE);
label.setFont(new Font("Arial", Font.BOLD, 20));
}
@Override
public Dimension getPreferredSize(JComponent c) {
return new Dimension(background.getWidth(), background.getHeight());
}
}
25. Clase TitleLabel
package org.brick_breaker.ui.labels;
import org.brick_breaker.cache.FontCache;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
public class TitleLabel extends JLabel {
static {
FontCache.addFont("diffusion", "fonts/diffusion.ttf");
}
public TitleLabel() {
Font font = FontCache.getFont("diffusion").deriveFont(Font.BOLD, 42);
setText("Brick Breaker");
setFont(font);
setHorizontalAlignment(SwingConstants.CENTER);
setVerticalAlignment(SwingConstants.CENTER);
setHorizontalTextPosition(SwingConstants.CENTER);
setVerticalTextPosition(SwingConstants.CENTER);
}
public void setTitle(String title) {
setText(title);
}
}
26. Clase GamePanel
package org.brick_breaker.ui.panels;
import org.brick_breaker.cache.SpriteCache;
import org.brick_breaker.cache.SpriteLoader;
import org.brick_breaker.game.Level;
import org.brick_breaker.sprites.*;
import org.brick_breaker.sprites.bonus.Bonus;
import org.brick_breaker.sprites.bonus.BonusType;
import org.brick_breaker.sprites.bricks.Brick;
import org.brick_breaker.sprites.paddles.Paddle;
import org.brick_breaker.sprites.paddles.PaddleType;
import org.brick_breaker.ui.events.KeyboardAction;
import org.brick_breaker.ui.windows.GameOverWindow;
import org.brick_breaker.ui.windows.MainWindow;
import org.brick_breaker.utils.FileManager;
import org.brick_breaker.utils.GameCycle;
import org.brick_breaker.utils.Randomized;
import org.brick_breaker.utils.collisions.CollisionManager;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Collections;
import java.util.concurrent.CopyOnWriteArrayList;
public class GamePanel extends JPanel {
private static final Borders LEFT_BORDER = Borders.LEFT_BAR;
private static final Borders RIGHT_BORDER = Borders.RIGHT_BAR;
private static final Borders TOP_BORDER = Borders.TOP_BAR;
private static final Borders BOTTOM_BORDER = Borders.BOTTOM_BAR;
public static GamePanel INSTANCE;
public static final int INITIAL_LIVES = 3;
public static final int INITIAL_SCORE = 0;
public static final int INITIAL_LEVEL = 1;
public static final int MAX_LEVEL = 5;
public static final int WIDTH = (int) (2 * LEFT_BORDER.getSize().getWidth() + TOP_BORDER.getSize().getWidth());
public static final int HEIGHT = (int) (LEFT_BORDER.getSize().getHeight());
public static final int GAME_WIDTH = WIDTH - RIGHT_BORDER.getSize().width;
private static Level level;
private static final CopyOnWriteArrayList<Ball> balls = new CopyOnWriteArrayList<>();
private Paddle paddle;
public static Timer timer;
private static boolean gameRunning = false;
private boolean bricksDestroyed = false;
private static int lives = INITIAL_LIVES;
private static int score = INITIAL_SCORE;
private int levelNumber = INITIAL_LEVEL;
private static final CopyOnWriteArrayList<Sprite> gameObjects = new CopyOnWriteArrayList<>();
private static final CopyOnWriteArrayList<Missile> missiles = new CopyOnWriteArrayList<>();
private GamePanel() {
initPanelSize();
level = FileManager.readLevel(Level.levelNumber);
}
public void restartGame() {
levelNumber = INITIAL_LEVEL;
lives = INITIAL_LIVES;
score = INITIAL_SCORE;
gameRunning = false;
bricksDestroyed = false;
gameObjects.clear();
CollisionManager.getInstance().clearCollidableObjects();
CollisionManager.getInstance().clearListeners();
CollisionManager.getInstance().addListener(balls.getFirst());
missiles.clear();
level = FileManager.readLevel(levelNumber);
paddle.resetPosition();
balls.getFirst().resetPosition();
registerObjects();
playGame();
}
public void addMissile() {
if (missiles.size() <= 5) {
// Si no existe un misil en la posición de la barra, se crea uno nuevo.
if (missiles.isEmpty()) {
createMissile();
} else {
// Verificamos la posición del último misil
Missile lastMissile = missiles.getLast();
// Si el último misil esta al menos a 50px de la barra, se crea uno nuevo
if (lastMissile.getPosition().y < paddle.getPosition().y - 150) {
createMissile();
}
}
}
}
private void createMissile() {
Missile m = new Missile(new Point(paddle.getPosition().x + paddle.getSize().width / 2 - Missile.MISSILE_WIDTH / 2,
paddle.getPosition().y - Missile.MISSILE_HEIGHT));
missiles.add(m);
gameObjects.add(m);
CollisionManager.getInstance().registerCollidable(m);
}
public void addLife() {
lives++;
}
public void addScore(int score) {
GamePanel.score += score;
}
public void duplicateScore() {
GamePanel.score *= 2;
}
public void startGame() {
paddle = new Paddle(PaddleType.MEDIUM);
balls.add(new Ball());
timer = new Timer(10, new GameCycle(this));
playGame();
registerObjects();
addKeyListener(new KeyboardAction(this));
setFocusable(true);
requestFocus();
}
public static GamePanel getInstance() {
if (INSTANCE == null) {
INSTANCE = new GamePanel();
}
return INSTANCE;
}
private void registerBricks() {
if (level != null) {
for (Brick[] row : level.getBricks()) {
Collections.addAll(gameObjects, row);
}
}
if (isGameRunning()) {
for (Sprite sprite : gameObjects) {
if (sprite instanceof Brick brick) {
CollisionManager.getInstance().registerCollidable(brick);
}
}
}
}
private void registerObjects() {
registerBricks();
gameObjects.add(LEFT_BORDER);
gameObjects.add(RIGHT_BORDER);
gameObjects.add(TOP_BORDER);
gameObjects.add(BOTTOM_BORDER);
gameObjects.add(paddle);
gameObjects.addAll(balls);
for (Sprite sprite : gameObjects) {
if (sprite instanceof Brick) {
((Brick) sprite).addImageToCache();
}
CollisionManager.getInstance().registerCollidable(sprite);
}
}
private void initPanelSize() {
setSize(WIDTH, HEIGHT);
setPreferredSize(getSize());
setMinimumSize(getSize());
setMaximumSize(getSize());
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// Se activa el antializado de la imagen para mejorar la calidad de la imagen.
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Se activa el renderizado de calidad para mejorar la calidad de la imagen.
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
// Se activa la interpolación bilineal para mejorar la calidad de la imagen.
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// Se activa el antializado de texto para mejorar la calidad del texto.
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
BufferedImage background = SpriteLoader.loadImage(level.getBackgroundName() + ".png");
g2d.drawImage(background, 0, 0, this.getWidth(), this.getHeight(), null);
for (Sprite sprite : gameObjects)
sprite.draw(g2d);
Toolkit.getDefaultToolkit().sync();
}
private static void createBonus(Brick brick) {
if (brick != null && Randomized.getRandomBoolean(99)) {
BonusType bonusType = Randomized.getRandomBonusType();
Bonus bonus = new Bonus(brick.getPosition(), BonusType.M);
bonus.addImageToCache();
gameObjects.add(bonus);
CollisionManager.getInstance().registerCollidable(bonus);
}
}
public static void removeBonus(Bonus bonus) {
if (bonus != null) {
gameObjects.remove(bonus);
CollisionManager.getInstance().unregisterCollidable(bonus);
}
}
public static void removeMissile(Missile missile) {
if (missile != null) {
gameObjects.remove(missile);
CollisionManager.getInstance().unregisterCollidable(missile);
missiles.remove(missile);
}
}
public static void removeBrick(Brick brick) {
if (brick != null) {
score += brick.getScore();
gameObjects.remove(brick);
CollisionManager.getInstance().unregisterCollidable(brick);
createBonus(brick);
}
}
public void removeBall(Ball ball) {
if (ball != null) {
if (balls.size() > 1) {
balls.remove(ball);
gameObjects.remove(ball);
CollisionManager.getInstance().unregisterCollidable(ball);
} else {
ball.resetPosition();
lives--;
if (lives == 0) {
gameRunning = false;
MainWindow mainWindow = MainWindow.getInstance();
mainWindow.setVisible(false);
mainWindow.dispose();
new GameOverWindow();
}
}
}
}
public void stopGame() {
gameRunning = false;
timer.stop();
}
public void playGame() {
gameRunning = true;
timer.start();
this.requestFocus();
}
private void updateLabels() {
MainWindow mainWindow = MainWindow.getInstance();
mainWindow.getScoreLabel().setText(String.valueOf(score));
mainWindow.getLifeLabel().setText(String.valueOf(lives));
}
public void destroyAllBricks() {
for (Sprite sprite : gameObjects) {
if (sprite instanceof Brick brick) {
brick.setDestroyed(true);
CollisionManager.getInstance().unregisterCollidable(brick);
gameObjects.remove(brick);
}
}
}
public void update() {
if (gameRunning) {
updateLabels();
checkBricksDestroy();
// Se verifica si se ha llegado al final del nivel.
if (bricksDestroyed) {
loadLevel();
}
// Se verifica si se ha colisionado con algún objeto.
CollisionManager.getInstance().checkCollisions();
// Se actualiza la posición de los objetos del juego.
for (Sprite sprite : gameObjects) {
if (sprite instanceof MovingSprite) {
((MovingSprite) sprite).move();
}
}
}
}
private void loadLevel() {
levelNumber++;
if (levelNumber <= MAX_LEVEL) {
level = FileManager.readLevel(levelNumber);
if (level != null) {
paddle.resetPosition();
int size = balls.size();
for (int i = 1; i < size; i++) {
balls.removeLast();
}
balls.getFirst().resetPosition();
registerBricks();
}
} else {
stopGame();
}
}
private void checkBricksDestroy() {
// Se verifica si se han destruido todos los ladrillos.
if (level != null && level.getBricks() != null) {
bricksDestroyed = true;
for (Brick[] row : level.getBricks()) {
for (Brick brick : row) {
if (!brick.isDestroyed()) {
bricksDestroyed = false;
break;
}
}
}
}
}
public Paddle getPaddle() {
return paddle;
}
public boolean isGameRunning() {
return gameRunning;
}
public CopyOnWriteArrayList<Ball> getBalls() {
return balls;
}
public boolean isBricksDestroyed() {
return bricksDestroyed;
}
public void setBricksDestroyed(boolean bricksDestroyed) {
this.bricksDestroyed = bricksDestroyed;
}
public int getLevelNumber() {
return levelNumber;
}
public void setLevelNumber(int levelNumber) {
this.levelNumber = levelNumber;
}
}
package org.brick_breaker.utils;
import org.brick_breaker.ui.panels.GamePanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class GameCycle implements ActionListener {
private final GamePanel panel;
public GameCycle(GamePanel panel) {
this.panel = panel;
}
@Override
public void actionPerformed(ActionEvent e) {
doGameCycle();
}
private void doGameCycle() {
panel.update();
panel.repaint();
}
}
32. Clase FileManager
package org.brick_breaker.utils;
import org.brick_breaker.game.Level;
import java.io.*;
import javax.swing.*;
public class FileManager {
public static Level readLevel(int currentLevel) {
Level level;
InputStream file;
InputStream buffer;
ObjectInput input;
try {
file = new FileInputStream("levels/level_" + currentLevel + ".lvl");
buffer = new BufferedInputStream(file);
input = new ObjectInputStream(buffer);
level = (Level) input.readObject();
input.close();
buffer.close();
file.close();
} catch (Exception e) {
JOptionPane.showMessageDialog(null,
"Error al cargar el nivel: " + currentLevel,
"Error",
JOptionPane.ERROR_MESSAGE);
return null;
}
return level;
}
public static void writeLevel(Level level) {
OutputStream file;
OutputStream buffer;
ObjectOutputStream output;
try {
file = new FileOutputStream(
"levels/level_" + level.getCurrentLevel() + ".lvl");
buffer = new BufferedOutputStream(file);
output = new ObjectOutputStream(buffer);
output.writeObject(level);
output.close();
buffer.close();
file.close();
JOptionPane.showMessageDialog(null,
"Nivel guardado: " + level.getCurrentLevel(),
"Información",
JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e1) {
JOptionPane.showMessageDialog(null,
"Error al guardar el nivel: " + level.getCurrentLevel(),
"Error",
JOptionPane.ERROR_MESSAGE);
}
}
}
33. Clase Randomized
package org.brick_breaker.utils;
import org.brick_breaker.sprites.bonus.BonusType;
import java.util.Random;
public class Randomized {
private Randomized() {
}
public static boolean getRandomBoolean() {
return randomInt(0, 1) == 1;
}
public static boolean getRandomBoolean(int probability) {
return randomInt(0, 100) < probability;
}
public static BonusType getRandomBonusType() {
BonusType[] bonusTypes = BonusType.values();
int randomIndex = randomInt(0, bonusTypes.length - 1);
return bonusTypes[randomIndex];
}
public static int randomInt(int min, int max) {
Random random = new Random();
return random.nextInt(max - min + 1) + min;
}
}
Conclusión
Con esta referencia, puedes verificar si tu código está completo y si no, puedes completarlo. Recuerda que el código puede variar dependiendo de la versión de Java que estés utilizando y de las librerías que estés utilizando. Si tienes alguna duda, no dudes en preguntar.