public class Tablero extends JPanel {
public Tablero(int ancho, int alto) {
setPreferredSize(new Dimension(ancho, alto));
setBorder(BorderFactory.createEtchedBorder(0));
setBackground(Color.WHITE);
}
}public class PanelBotones extends JPanel {
public JButton btIniciar;
public JButton btAcelerar;
public JButton btFrenar;
public JButton btDetenContinuar;
public JButton btReset;
public PanelBotones(int ancho, int alto) {
btIniciar = new JButton("Iniciar");
btAcelerar = new JButton("Acelerar");
btFrenar = new JButton("Frenar");
btDetenContinuar = new JButton("Detener/Continuar");
btReset = new JButton("Reset");
setPreferredSize(new Dimension(ancho, alto));
setLayout(new GridLayout(5, 1));
add(new PanelBoton(btIniciar));
add(new PanelBoton(btAcelerar));
add(new PanelBoton(btFrenar));
add(new PanelBoton(btDetenContinuar));
add(new PanelBoton(btReset));
}
private class PanelBoton extends JPanel {
public PanelBoton(JButton boton) {
add(boton);
}
}
}public class Juego extends JFrame {
private Dimension dim;
private Tablero tablero;
private PanelBotones botones;
public Juego() {
dim = Toolkit.getDefaultToolkit().getScreenSize();
int ancho = (int)(dim.getWidth() / 2);
int alto = (int)(dim.getHeight() / 2);
tablero = new Tablero((ancho / 5 * 4), alto);
botones = new PanelBotones(ancho / 5, alto);
setLayout(new FlowLayout());
add(tablero);
add(botones);
setTitle("Bola rebota");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Juego();
}
});
}
}public class Bola extends Ellipse2D {
private int posX;
private int posY;
private double ancho;
private double alto;
public Bola(int x, int y, int w, int h) {
posX = x;
posY = y;
ancho = w;
alto = h;
}
@Override
public Rectangle2D getBounds2D() {
Rectangle2D rect = new Rectangle2D.Double(posX, posY, ancho, alto);
return rect.getBounds2D();
}
@Override
public double getX() {
return posX;
}
@Override
public double getY() {
return posY;
}
@Override
public double getWidth() {
return ancho;
}
@Override
public double getHeight() {
return alto;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void setFrame(double x, double y, double w, double h) {
}
}public class Tablero extends JPanel {
private int ancho;
private int alto;
private Bola bola;
public Tablero(int ancho, int alto) {
this.ancho = ancho;
this.alto = alto;
bola = new Bola(ancho / 2, alto / 2,
ancho / 30, alto / 25);
setPreferredSize(new Dimension(ancho, alto));
setBorder(BorderFactory.createEtchedBorder(0));
setBackground(Color.WHITE);
}
public int getAncho() {
return ancho;
}
public int getAlto() {
return alto;
}
public Bola getBola() {
return bola;
}
public void reset() {
bola = new Bola(ancho / 2, alto / 2,
ancho / 30, alto / 25);
repaint();
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D G2D = (Graphics2D)g;
//Limpiamos panel con un rectangulo blanco
G2D.setColor(Color.WHITE);
G2D.fillRect(0, 0, ancho, alto);
//Ahora pintamos bola
G2D.setColor(Color.BLUE);
G2D.fill(bola);
G2D.draw(bola);
}
}public class GestorJuego {
private Tablero tablero;
public GestorJuego(Tablero tablero) {
this.tablero = tablero;
}
public ActionListener getAccionReset() {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tablero.reset();
}
};
}
}public class Juego extends JFrame {
private Dimension dim;
private Tablero tablero;
private PanelBotones botones;
private GestorJuego gestor;
public Juego() {
dim = Toolkit.getDefaultToolkit().getScreenSize();
int ancho = (int)(dim.getWidth() / 2);
int alto = (int)(dim.getHeight() / 2);
tablero = new Tablero((ancho / 5 * 4), alto);
botones = new PanelBotones(ancho / 5, alto);
gestor = new GestorJuego(tablero);
botones.btReset.addActionListener(gestor.getAccionReset());
setLayout(new FlowLayout());
add(tablero);
add(botones);
setTitle("Bola rebota");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Juego();
}
});
}
}public class Bola extends Ellipse2D {
private int posX;
private int posY;
private double ancho;
private double alto;
//Atributos necesario para calcular colisiones y rebotes
private int desviacionX;
private int desviacionY;
private final int LIMITE_X;
private final int LIMITE_Y;
private Random azar;
public Bola(int x, int y, int w, int h, int limiteX, int limiteY) {
posX = x;
posY = y;
ancho = w;
alto = h;
desviacionX = 0;
desviacionY = 0;
//Los límites se calculan restando las dimensiones de la bola a las dimensiones del tablero
LIMITE_X = (int) (limiteX - ancho);
LIMITE_Y = (int) (limiteY - alto);
azar = new Random();
}
public void mover() {
//Antes de mover, comprobamos si la bola está detenida (desviaciones = 0)
//Si está detenida, calcularemos nuevas desviaciones.
//Si no, la bola se moverá según las desviaciones ya existentes
if (desviacionX == 0 && desviacionY == 0) {
//Calculamos nuevas desviaciones al azar para empezar a mover
desviacionX = azar.nextInt(6);
desviacionY = azar.nextInt(6);
//Elegimos al azar si son negativas o positivos
if (azar.nextBoolean())
desviacionX *= -1;
if (azar.nextBoolean())
desviacionY *= -1;
}
//El movimiento consiste en alterar la posicion según las "desviaciones"
posX += desviacionX;
posY += desviacionY;
}
public void detener() {
desviacionX = 0;
desviacionY = 0;
}
public void comprobarColision() {
//Comprobamos si hemos alcanzado algún limite
//en cuyo caso calcularemos nuevas desviaciones según donde hayamos colisionado
//Colisión en el borde oeste
if (posX <= 0) {
//La desviacion de X ha de ser positiva y mayor que 0
desviacionX = azar.nextInt(5) + 1;
//Desviacion de Y varía, pero ha de mantener la misma dirección actual
if (desviacionY < 0) {
desviacionY = azar.nextInt(5) + 1;
desviacionY *= -1;
}
else
desviacionY = azar.nextInt(5) + 1;
}
//Colisión en el borde este
if (posX >= LIMITE_X) {
//La desviacion de X ha de ser negativa
desviacionX = azar.nextInt(5) + 1;
desviacionX *= -1;
//Desviacion de Y varía, pero ha de mantener la misma dirección actual
if (desviacionY < 0) {
desviacionY = azar.nextInt(5) + 1;
desviacionY *= -1;
}
else
desviacionY = azar.nextInt(5) + 1;
}
//Colisión en el borde norte
if (posY <= 0) {
//La desviacion de Y ha de ser positiva
desviacionY = azar.nextInt(5) + 1;
//Desviacion de X varía, pero ha de mantener la misma dirección actual
if (desviacionX < 0) {
desviacionX = azar.nextInt(5) + 1;
desviacionX *= -1;
}
else
desviacionX = azar.nextInt(5) + 1;
}
//Colisión en el borde sur
if (posY >= LIMITE_Y) {
//La desviacion de Y ha de ser negativa
desviacionY = azar.nextInt(5) + 1;
desviacionY *= -1;
//Desviacion de X varía, pero ha de mantener la misma dirección actual
if (desviacionX < 0) {
desviacionX = azar.nextInt(5) + 1;
desviacionX *= -1;
}
else
desviacionX = azar.nextInt(5) + 1;
}
}
@Override
public Rectangle2D getBounds2D() {
Rectangle2D rect = new Rectangle2D.Double(posX, posY, ancho, alto);
return rect.getBounds2D();
}
@Override
public double getX() {
return posX;
}
@Override
public double getY() {
return posY;
}
@Override
public double getWidth() {
return ancho;
}
@Override
public double getHeight() {
return alto;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void setFrame(double x, double y, double w, double h) {
}
}public class Tablero extends JPanel {
private int ancho;
private int alto;
private Bola bola;
public Tablero(int ancho, int alto) {
this.ancho = ancho;
this.alto = alto;
bola = new Bola(ancho/2, alto/2,
ancho/30, alto/25, ancho, alto);
setPreferredSize(new Dimension(ancho, alto));
setBorder(BorderFactory.createEtchedBorder(0));
setBackground(Color.WHITE);
}
public int getAncho() {
return ancho;
}
public int getAlto() {
return alto;
}
public Bola getBola() {
return bola;
}
public void reset() {
bola = new Bola(ancho / 2, alto / 2,
ancho / 30, alto / 25, ancho, alto);
repaint();
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D G2D = (Graphics2D)g;
//Limpiamos panel con un rectangulo blanco
G2D.setColor(Color.WHITE);
G2D.fillRect(0, 0, ancho, alto);
//Ahora pintamos bola
G2D.setColor(Color.BLUE);
G2D.fill(bola);
G2D.draw(bola);
}
}public class GestorJuego {
private Tablero tablero;
private PanelBotones botones;
private long velocidad;
private boolean mover;
private MovimientoBola movimiento;
public GestorJuego(Tablero tablero, PanelBotones botones) {
this.tablero = tablero;
this.botones = botones;
botones.btIniciar.addActionListener(new AccionIniciar());
botones.btAcelerar.addActionListener(new AccionAcelerar());
botones.btFrenar.addActionListener(new AccionFrenar());
botones.btDetenContinuar.addActionListener(new AccionIniciaDetiene());
botones.btReset.addActionListener(new AccionReset());
velocidad = 25;
mover = false;
}
private class MovimientoBola extends SwingWorker {
@Override
protected Object doInBackground() throws Exception {
while(mover) {
tablero.getBola().mover();
tablero.getBola().comprobarColision();
tablero.repaint();
Thread.sleep(velocidad);
}
return null;
}
}
private class AccionReset implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
mover = false;
tablero.getBola().detener();
tablero.reset();
botones.btIniciar.setEnabled(true);
botones.btAcelerar.setEnabled(false);
botones.btFrenar.setEnabled(false);
botones.btDetenContinuar.setEnabled(false);
botones.btReset.setEnabled(false);
}
}
private class AccionIniciar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (!mover) {
mover = true;
velocidad = 25;
movimiento = new MovimientoBola();
movimiento.execute();
botones.btIniciar.setEnabled(false);
botones.btAcelerar.setEnabled(true);
botones.btFrenar.setEnabled(true);
botones.btDetenContinuar.setEnabled(true);
botones.btReset.setEnabled(true);
}
}
}
private class AccionAcelerar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (mover) {
if (velocidad > 5)
velocidad -= 5;
}
}
}
private class AccionFrenar implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (mover)
velocidad += 5;
}
}
private class AccionIniciaDetiene implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (mover) {
mover = false;
botones.btAcelerar.setEnabled(false);
botones.btFrenar.setEnabled(false);
}
else {
mover = true;
movimiento = new MovimientoBola();
movimiento.execute();
botones.btAcelerar.setEnabled(true);
botones.btFrenar.setEnabled(true);
}
}
}
}
public class PanelBotones extends JPanel {
public JButton btIniciar;
public JButton btAcelerar;
public JButton btFrenar;
public JButton btDetenContinuar;
public JButton btReset;
public PanelBotones(int ancho, int alto) {
btIniciar = new JButton("Iniciar");
btAcelerar = new JButton("Acelerar");
btAcelerar.setEnabled(false);
btFrenar = new JButton("Frenar");
btFrenar.setEnabled(false);
btDetenContinuar = new JButton("Detener/Continuar");
btDetenContinuar.setEnabled(false);
btReset = new JButton("Reset");
btReset.setEnabled(false);
setPreferredSize(new Dimension(ancho, alto));
setLayout(new GridLayout(5, 1));
add(new PanelBoton(btIniciar));
add(new PanelBoton(btAcelerar));
add(new PanelBoton(btFrenar));
add(new PanelBoton(btDetenContinuar));
add(new PanelBoton(btReset));
}
private class PanelBoton extends JPanel {
public PanelBoton(JButton boton) {
add(boton);
}
}
}public class Juego extends JFrame {
private Dimension dim;
private Tablero tablero;
private PanelBotones botones;
private GestorJuego gestor;
public Juego() {
dim = Toolkit.getDefaultToolkit().getScreenSize();
int ancho = (int)(dim.getWidth() / 2);
int alto = (int)(dim.getHeight() / 2);
tablero = new Tablero((ancho / 5 * 4), alto);
botones = new PanelBotones(ancho / 5, alto);
gestor = new GestorJuego(tablero, botones);
setLayout(new FlowLayout());
add(tablero);
add(botones);
setTitle("Bola rebota");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Juego();
}
});
}
}public class Bola extends Ellipse2D {
private int posX;
private int posY;
private double ancho;
private double alto;
//Atributos necesario para calcular colisiones y rebotes
private int desviacionX;
private int desviacionY;
private final int LIMITE_X;
private final int LIMITE_Y;
private Random azar;
final int VELOCIDAD;
public Bola(int x, int y, int w, int h, int limiteX, int limiteY) {
posX = x;
posY = y;
ancho = w;
alto = h;
desviacionX = 0;
desviacionY = 0;
//Los límites se calculan restando las dimensiones de la bola a las dimensiones del tablero
LIMITE_X = (int) (limiteX - ancho);
LIMITE_Y = (int) (limiteY - alto);
azar = new Random();
VELOCIDAD = 5; //Una de las desviaciones ha de tener siempre ese valor
}
public void mover() {
//Antes de mover, comprobamos si la bola está detenida (desviaciones = 0)
//Si está detenida, calcularemos nuevas desviaciones.
//Si no, la bola se moverá según las desviaciones ya existentes
if (desviacionX == 0 && desviacionY == 0) {
//Una desviación se calcula al azar, para decidir el ángulo
//Otra usará el valor de velocidad
desviacionX = VELOCIDAD;
desviacionY = azar.nextInt(6);
//Elegimos al azar si son negativas o positivos
if (azar.nextBoolean())
desviacionX *= -1;
if (azar.nextBoolean())
desviacionY *= -1;
}
//El movimiento consiste en alterar la posicion según las "desviaciones"
posX += desviacionX;
posY += desviacionY;
}
public void detener() {
desviacionX = 0;
desviacionY = 0;
}
public void comprobarColision() {
//Comprobamos si hemos alcanzado algún limite
//en cuyo caso calcularemos nuevas desviaciones según donde hayamos colisionado
//Colisión en el borde oeste
if (posX <= 0) {
//A la desviación de X le damos el valor de velocidad
desviacionX = VELOCIDAD;
//Desviacion de Y varía, pero ha de mantener la misma dirección actual
if (desviacionY < 0) {
desviacionY = azar.nextInt(5) + 1;
desviacionY *= -1;
}
else
desviacionY = azar.nextInt(5) + 1;
}
//Colisión en el borde este
if (posX >= LIMITE_X) {
//La desviacion de X ha de ser negativa
desviacionX = VELOCIDAD * -1;
//Desviacion de Y varía, pero ha de mantener la misma dirección actual
if (desviacionY < 0) {
desviacionY = azar.nextInt(5) + 1;
desviacionY *= -1;
}
else
desviacionY = azar.nextInt(5) + 1;
}
//Colisión en el borde norte
if (posY <= 0) {
//La desviacion de Y será la velocidad
desviacionY = VELOCIDAD;
//Desviacion de X varía, pero ha de mantener la misma dirección actual
if (desviacionX < 0) {
desviacionX = azar.nextInt(5) + 1;
desviacionX *= -1;
}
else
desviacionX = azar.nextInt(5) + 1;
}
//Colisión en el borde sur
if (posY >= LIMITE_Y) {
//La desviacion de Y ha de ser negativa
desviacionY = VELOCIDAD * -1;
//Desviacion de X varía, pero ha de mantener la misma dirección actual
if (desviacionX < 0) {
desviacionX = azar.nextInt(5) + 1;
desviacionX *= -1;
}
else
desviacionX = azar.nextInt(5) + 1;
}
}
@Override
public Rectangle2D getBounds2D() {
Rectangle2D rect = new Rectangle2D.Double(posX, posY, ancho, alto);
return rect.getBounds2D();
}
@Override
public double getX() {
return posX;
}
@Override
public double getY() {
return posY;
}
@Override
public double getWidth() {
return ancho;
}
@Override
public double getHeight() {
return alto;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void setFrame(double x, double y, double w, double h) {
}
}public class PanelBotones extends JPanel {
public MiBoton btIniciar;
public MiBoton btAcelerar;
public MiBoton btFrenar;
public MiBoton btDetenContinuar;
public MiBoton btReset;
public PanelBotones(int ancho, int alto) {
btIniciar = new MiBoton("Iniciar", ancho);
btAcelerar = new MiBoton("Acelerar", ancho);
btAcelerar.setEnabled(false);
btFrenar = new MiBoton("Frenar", ancho);
btFrenar.setEnabled(false);
btDetenContinuar = new MiBoton("Detener/Continuar", ancho);
btDetenContinuar.setEnabled(false);
btReset = new MiBoton("Reset", ancho);
btReset.setEnabled(false);
setPreferredSize(new Dimension(ancho, alto));
setLayout(new GridLayout(5, 1));
add(new PanelBoton(btIniciar));
add(new PanelBoton(btAcelerar));
add(new PanelBoton(btFrenar));
add(new PanelBoton(btDetenContinuar));
add(new PanelBoton(btReset));
}
private class PanelBoton extends JPanel {
public PanelBoton(JButton boton) {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
add(Box.createVerticalGlue());
add(boton);
add(Box.createVerticalGlue());
}
}
public class MiBoton extends JButton {
public MiBoton(String txt, int ancho) {
super(txt);
//Tamaño fuente se calcula según el ancho del panel
float sizeFuente = getFont().getSize() * ancho * 0.7f /100;
setFont(getFont().deriveFont(sizeFuente));
}
}
}por qué introduces los cinco botones dentro de JPanel, que a su vez se introducen en el JPanel PanelBotones en lugar de colocar los botones directamente sobre el JPanel PanelBotones.Es para maquetar un poco mejor los botones.
public PanelBotones(int ancho, int alto) {Verás que el aspecto de los botones cambia:
btIniciar = new MiBoton("Iniciar", ancho);
btAcelerar = new MiBoton("Acelerar", ancho);
btAcelerar.setEnabled(false);
btFrenar = new MiBoton("Frenar", ancho);
btFrenar.setEnabled(false);
btDetenContinuar = new MiBoton("Detener/Continuar", ancho);
btDetenContinuar.setEnabled(false);
btReset = new MiBoton("Reset", ancho);
btReset.setEnabled(false);
setPreferredSize(new Dimension(ancho, alto));
setLayout(new GridLayout(5, 1));
/*add(new PanelBoton(btIniciar));
add(new PanelBoton(btAcelerar));
add(new PanelBoton(btFrenar));
add(new PanelBoton(btDetenContinuar));
add(new PanelBoton(btReset));*/
add(btIniciar);
add(btAcelerar);
add(btFrenar);
add(btDetenContinuar);
add(btReset);
}
setPreferredSize(new Dimension(ancho, alto));
setLayout(new GridLayout(5, 1));
JPanel pn1 = new JPanel();
pn1.add(btIniciar);
JPanel pn2 = new JPanel();
pn2.add(btAcelerar);
JPanel pn3 = new JPanel();
pn3.add(btFrenar);
JPanel pn4 = new JPanel();
pn4.add(btDetenContinuar);
JPanel pn5 = new JPanel();
pn5.add(btReset);
add(pn1);
add(pn2);
add(pn3);
add(pn4);
add(pn5);
setLayout(new GridLayout(5, 1));
add(new PanelBoton(btIniciar));
add(new PanelBoton(btAcelerar));
add(new PanelBoton(btFrenar));
add(new PanelBoton(btDetenContinuar));
add(new PanelBoton(btReset));
}
private class PanelBoton extends JPanel {
public PanelBoton(JButton boton) {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
add(Box.createVerticalGlue());
add(boton);
add(Box.createVerticalGlue());
}
}
Por otro lado por qué usar pack() y por qué usar setLocationRelativeTo(null).
Por otro lado lo que yo entiendo es que SwingUtilities.invokeLater(new Runnable() lo que hace es crear un hilo de ejecucion, no sé si es así y también me gustaria preguntar qué pasa si no creas un hilo de ejecución.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Juego();
}
});¿Por que se incluye @SuppressWarnings("serial") en el codigo?Esto está relacionado con la "serialización" de objetos.
y el método paint(Graphics g) entiendo que pinta un elemento grafico como un JPanel, ¿la g representa el interior del elemento grafico para poder manipularlo? Y esto que entiendo es una conversión de tipos: Graphics2D G2D = (Graphics2D)g; ¿Por qué se hace así en lugr de manipular directamente el objeto g?
drawOval(int x, int y, int width, int height)Pero fíjate que cada vez que lo llamamos, tenemos que darle coordenadas y dimensiones.public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Juego();
}
});
}
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.swing;
import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.beans.BeanProperty;
import java.beans.JavaBean;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serial;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;
import javax.swing.plaf.PanelUI;
/**
* <code>JPanel</code> is a generic lightweight container.
* For examples and task-oriented documentation for JPanel, see
* <a
href="https://docs.oracle.com/javase/tutorial/uiswing/components/panel.html">How to Use Panels</a>,
* a section in <em>The Java Tutorial</em>.
* <p>
* <strong>Warning:</strong> Swing is not thread safe. For more
* information see <a
* href="package-summary.html#threading">Swing's Threading
* Policy</a>.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Arnaud Weber
* @author Steve Wilson
* @since 1.2
*/
@JavaBean(defaultProperty = "UI", description = "A generic lightweight container.")
@SuppressWarnings("serial") // Same-version serialization only
public class JPanel extends JComponent implements Accessible
{
/**
* @see #getUIClassID
* @see #readObject
*/
private static final String uiClassID = "PanelUI";
/**
* Creates a new JPanel with the specified layout manager and buffering
* strategy.
*
* @param layout the LayoutManager to use
* @param isDoubleBuffered a boolean, true for double-buffering, which
* uses additional memory space to achieve fast, flicker-free
* updates
*/
public JPanel(LayoutManager layout, boolean isDoubleBuffered) {
setLayout(layout);
setDoubleBuffered(isDoubleBuffered);
setUIProperty("opaque", Boolean.TRUE);
updateUI();
}
/**
* Create a new buffered JPanel with the specified layout manager
*
* @param layout the LayoutManager to use
*/
public JPanel(LayoutManager layout) {
this(layout, true);
}
/**
* Creates a new <code>JPanel</code> with <code>FlowLayout</code>
* and the specified buffering strategy.
* If <code>isDoubleBuffered</code> is true, the <code>JPanel</code>
* will use a double buffer.
*
* @param isDoubleBuffered a boolean, true for double-buffering, which
* uses additional memory space to achieve fast, flicker-free
* updates
*/
public JPanel(boolean isDoubleBuffered) {
this(new FlowLayout(), isDoubleBuffered);
}
/**
* Creates a new <code>JPanel</code> with a double buffer
* and a flow layout.
*/
public JPanel() {
this(true);
}
/**
* Resets the UI property with a value from the current look and feel.
*
* @see JComponent#updateUI
*/
public void updateUI() {
setUI((PanelUI)UIManager.getUI(this));
}
/**
* Returns the look and feel (L&F) object that renders this component.
*
* @return the PanelUI object that renders this component
* @since 1.4
*/
public PanelUI getUI() {
return (PanelUI)ui;
}
/**
* Sets the look and feel (L&F) object that renders this component.
*
* @param ui the PanelUI L&F object
* @see UIDefaults#getUI
* @since 1.4
*/
@BeanProperty(hidden = true, visualUpdate = true, description
= "The UI object that implements the Component's LookAndFeel.")
public void setUI(PanelUI ui) {
super.setUI(ui);
}
/**
* Returns a string that specifies the name of the L&F class
* that renders this component.
*
* @return the string "PanelUI"
* @see JComponent#getUIClassID
* @see UIDefaults#getUI
*/
@BeanProperty(bound = false, expert = true, description
= "A string that specifies the name of the L&F class.")
public String getUIClassID() {
return uiClassID;
}
/**
* See readObject() and writeObject() in JComponent for more
* information about serialization in Swing.
*/
@Serial
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
if (getUIClassID().equals(uiClassID)) {
byte count = JComponent.getWriteObjCounter(this);
JComponent.setWriteObjCounter(this, --count);
if (count == 0 && ui != null) {
ui.installUI(this);
}
}
}
/**
* Returns a string representation of this JPanel. This method
* is intended to be used only for debugging purposes, and the
* content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be <code>null</code>.
*
* @return a string representation of this JPanel.
*/
protected String paramString() {
return super.paramString();
}
/////////////////
// Accessibility support
////////////////
/**
* Gets the AccessibleContext associated with this JPanel.
* For JPanels, the AccessibleContext takes the form of an
* AccessibleJPanel.
* A new AccessibleJPanel instance is created if necessary.
*
* @return an AccessibleJPanel that serves as the
* AccessibleContext of this JPanel
*/
@BeanProperty(bound = false)
public AccessibleContext getAccessibleContext() {
if (accessibleContext == null) {
accessibleContext = new AccessibleJPanel();
}
return accessibleContext;
}
/**
* This class implements accessibility support for the
* <code>JPanel</code> class. It provides an implementation of the
* Java Accessibility API appropriate to panel user-interface
* elements.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*/
@SuppressWarnings("serial") // Same-version serialization only
protected class AccessibleJPanel extends AccessibleJComponent {
/**
* Constructs an {@code AccessibleJPanel}.
*/
protected AccessibleJPanel() {}
/**
* Get the role of this object.
*
* @return an instance of AccessibleRole describing the role of the
* object
*/
public AccessibleRole getAccessibleRole() {
return AccessibleRole.PANEL;
}
}
}Mi intención era inicializar la Bola por separado en la clase GestorJuego (que veremos después) y luego pintarla en Tablero. Pero no he conseguido/sabido hacer que el tablero la pinte.
¿Si la clase Bola hubiera sido independiente del Tablero habría tenido un método paint? No veo método paint en la clase Bola, pero supongo que podría existir metodo paint si fuera independiente?
¿la g en la clase Tablero representa el interior del elemento grafico Jpanel para poder manipularlo?La g más bien es el "pincel" que pinta el elemento gráfico.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class BolaDeEjemplo implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new BolaDeEjemplo());
}
private final BolaDeEjemploModel model;
private final DrawingPanel drawingPanel;
public BolaDeEjemplo() {
this.model = new BolaDeEjemploModel();
this.drawingPanel = new DrawingPanel(model);
}
@Override
public void run() {
JFrame frame = new JFrame("Bola Ejemplo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
Timer timer = new Timer(30, new BallMotionListener(this, model));
timer.start();
}
public void repaint() {
drawingPanel.repaint();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final BolaDeEjemploModel model;
public DrawingPanel(BolaDeEjemploModel model) {
this.model = model;
this.setPreferredSize(model.getDrawingPanelDimension());
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Ball ball = model.getBall();
Point2D point = ball.getPosition();
int x = (int) Math.round(point.getX());
int y = (int) Math.round(point.getY());
int radius = ball.getRadius();
int diameter = radius + radius;
g.setColor(Color.BLUE);
g.fillOval(x - radius, y - radius, diameter, diameter);
}
}
public class BallMotionListener implements ActionListener {
private final BolaDeEjemplo view;
private final BolaDeEjemploModel model;
public BallMotionListener(BolaDeEjemplo view, BolaDeEjemploModel model) {
this.view = view;
this.model = model;
}
@Override
public void actionPerformed(ActionEvent event) {
model.getBall().calculateNewPosition();
view.repaint();
}
}
public class BolaDeEjemploModel {
private final Ball ball;
private final Dimension drawingPanelDimension;
public BolaDeEjemploModel() {
this.drawingPanelDimension = new Dimension(400, 400);
this.ball = new Ball(4, 325, drawingPanelDimension);
}
public Ball getBall() {
return ball;
}
public Dimension getDrawingPanelDimension() {
return drawingPanelDimension;
}
}
public class Ball {
private double xPosition, yPosition;
private int angle, radius, speed;
private final Dimension drawingPanelDimension;
public Ball(int speed, int angle, Dimension drawingPanelDimension) {
this.speed = speed;
this.radius = 12;
this.angle = angle;
this.drawingPanelDimension = drawingPanelDimension;
this.xPosition = drawingPanelDimension.width / 2;
this.yPosition = drawingPanelDimension.height / 2;
}
public int getAngle() {
return angle;
}
public void setAngle(int angle) {
this.angle = angle;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getRadius() {
return radius;
}
public void calculateNewPosition() {
double theta = Math.toRadians(angle);
double xDifference = Math.cos(theta) * speed;
double yDifference = Math.sin(theta) * speed;
xPosition += xDifference;
yPosition += yDifference;
if (xPosition < radius) {
angle = 540 - angle;
}
if (xPosition > drawingPanelDimension.width - radius) {
angle = 540 - angle;
}
if (yPosition < radius) {
angle = 360 - angle;
}
if (yPosition > drawingPanelDimension.height - radius) {
angle = 360 - angle;
}
angle %= 360;
}
public Point2D getPosition() {
return new Point2D.Double(xPosition, yPosition);
}
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Ball ball = model.getBall();
Point2D point = ball.getPosition();
int x = (int) Math.round(point.getX());
int y = (int) Math.round(point.getY());
public void calculateNewPosition() {
double theta = Math.toRadians(angle);
double xDifference = Math.cos(theta) * speed;
double yDifference = Math.sin(theta) * speed;
xPosition += xDifference;
yPosition += yDifference;
if (xPosition < radius) {
angle = 540 - angle;
}
if (xPosition > drawingPanelDimension.width - radius) {
angle = 540 - angle;
}
if (yPosition < radius) {
angle = 360 - angle;
}
if (yPosition > drawingPanelDimension.height - radius) {
angle = 360 - angle;
}
if (xPosition < radius) { //Bola rebota con pared izquierda
angle = 540 - angle;
}
if (xPosition > drawingPanelDimension.width - radius) { //Bola rebota con pared derecha
angle = 540 - angle;
}
if (yPosition < radius) { //Bola rebota con pared inferior
angle = 360 - angle;
}
if (yPosition > drawingPanelDimension.height - radius) { //Bola rebota con pared superior
angle = 360 - angle;
}
angle %= 360; // Operador modulo resto de division entre 360