Ejercicios de preparación para la 6ª convocatoria
Ejercicio 3
ENUNCIADO
En este caso se trata de hacer un “mini-videojuego”, algo parecido al “arkanoid” pero sin ladrillos que romper. Simplemente el jugador debe mantener una pelota rebotando de pared en pared sin dejarla caer al suelo.
Tenemos un tablero de juego dividido en dos zonas:
Área de juego: Habrá una pelota y un jugador que maneja una pala situada en la parte inferior mediante el teclado. Se trata de no dejar rebotar la pelota con el suelo, haciendo que rebote en la pala y vaya de una pared a otra. Si la pelota cae al suelo, significará que no hemos conseguido interceptarla con la pala y se acabará la partida.
Área del contador: En esta parte del tablero, únicamente habrá un marcador que lleve la cuenta de cuantas veces ha rebotado la pelota en la pala.
La partida comienza en cuanto se abre la aplicación, es decir, en ese momento, nos encontraremos ya con la pelota rebotando de pared en pared.
SOLUCION
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class RebotaLaPelota extends JFrame{
JPanel juego; //Panel de juego
JPanel marcador; //Panel del marcador donde sale el numero de rebotes.
int rebotes =0; [c1]//Contador del numero de rebotes
final int tamaño_y=300;
final int tam_casilla=1; [c2]//1pixel
JLabel tablero [] []=new JLabel [tamaño] [tamaño];[c3]
int xpe=tamaño_x/2; //Coordenadas pelota
int ype=tamaño_y/2;
int direccion_pelota = 0;//Si vale 0=diagonal derecha arriba; 1=diagonal izda arriba;2=diagonal dcha abajo; 3=diagonal izda abajo
int xpa=250;//Coordenadas pala(249,250,251)
int ypa=280;
Timer velocidad; //Timer que controla la velocidad con la q se mueve la pelota
public RebotaLaPelota(){ //Constructor
super (“Rebota la Pelota”);
JLabel numrebotes=new JLabel (“ Número de rebotes en la pala:”); //Etiqueta con el número de rebotes de la pelota en la pala
marcador.add (numrebotes, BorderLayout. NORTH[c5]);
Juego =new Jpanel (new GridLayout (tamaño[c6], tamaño));
Juego.setSize (tamaño * tamaño, tamaño * tamaño);
for (int i=0, i<tamaño x; i++){ //Pinto todo el tablero de color blanco
for (int j=0; j<tamaño [c7]y;j++){
Tablero [i][j]=new Jlabel();
Tablero [i][j].setOpaque(true);
Tablero [i][j].setBackground(Color.WHITE);
Juego.add (tablero[i][j]);
}
}
getContentPane().setLayout(new GridLayout(1,2));
getContentPane().add(juego);
getContentPane().add(marcador);
Escuchadores e1= new Escuchadores();
addKeyListener(e1);
velocidad =new Timer (250, e1);
velocidad.start();
}
public void DibujarPala(){
for (int k=xpa-1 ; k<xpa+1; k++){
for (int m=ypa-1; m < ypa ; m++){
tablero[k][m].setOpaque(true);[c8]
tablero[k][m].setBackground (Color.BLUE);
}
}
}
public void NuevasCoordenadasPala(int xpa, int ypa){
this.xpa=xpa;
this.ypa=ypa;
}
public void DibujarPelota(){
tablero[xpe][ype]=newJLabel();[c10]
tablero[xpe][ype].setOpaque(trae);
tablero[xpe][ype].setBackground(Color.YELLOW);
Juego.add(tablero[xpe][ype]);
}
public static void main (String args[]){
RebotaLaPelota rlp= new RebotaLaPelota();
rlp.setDefaultCloseOperation (EXIT_ON_CLOSE);
rlp.pack();
rlp.setSize (600,600);
rlp.setResizable (false);
rlp.setVisible(true);
rlp.requestFocus();
}
public class Escuchadores extends KeyAdapter implements ActionListener{ //Clase interna
public void KeyPressed(KeyEvent e){ //Eventos de teclado
int antigua_xpa=xpa;
int antigua_xpa+1=xpa+1;
int antigua_xpa-1=xpa-1;
char tecla=e.getKeyChar();
if ((tecla.equals(‘q’)){ //Que la pala se mueva hacia la izquierda porque he pulsado la q
if ( xpa-2>0){ //Compruebo que la próxima posición a la izquierda no es la pared.
xpa+1;
}
[c13]tablero(antigua_xpa).setBackground(Color.WHITE);//Las coordenadas antiguas las pinto de blanco.
tablero(antigua_xpa+1)[c14].setBackground(Color.WHITE);
tablero(antigua_xpa-1).setBackground(Color.WHITE);
tablero(xpa).setBackground(Color.BLACK);//Y las coordenadas nuevas las pinto de negro.
tablero(xpa+1).setBackground(Color.BLACK);
tablero(xpa-1).setBackground(Color.BLACK);
}
}
else {//que la pala se mueva a la izquierda porque he pulsado la p
if (xpa+2<500){ //Compruebo que la próxima posición a la derecha no es la pared
xpa+1;
else{
xpa-1;
}
}
}
}
NuevasCoordenadasPala([c15]); //Actualizamos las nuevas coordenadas de la pala una vez que se ha movido.
DibujarPala[c16](); //Y dibujamos la pala en la nueva posición.
}
private void fin(boolean ganador){ //Si la pelota toca el suelo, finaliza la partida
if(ype+1>300){ //La pelota ha tocado el suelo
ganador==false;
velocidad.stop();
System.exit(0);
}
}
public void actionPerformed (ActionEvent e){ //Hacia donde se mueve la pelota y comprobación de que puede moverse hacia un lado u otro.
int antigua_xpe=xpe;
int antigua_ype=ype;
case 0: //Diagonal arriba derecha
if(xpe+1<500) { //Hago todas las comprobaciones de que la pelota pueda moverse arriba y a la derecha siempre y cuando la próxima posición no sea el techo o la pared derecha; y esto con todos los casos hacia donde podría moverse la pelota
if (ype-1>0){
xpe+1;
ype-1;
}
else {
xpe+1;
ype+1;
}
else{
if (ype-1>0){
xpe-1;
ype-1;
}
else{
xpe-1;
ype+1;
}
}
}
case1: //Diagonal arriba izquierda
if (xpe-1>0){
if (ype-1>0){
xpe-1;
ype-1;
}
else{
xpe-1;
ype+1;
}
else{ //x-1<0
if(ype-1>0){
xpe+1;
ype-1;
else{
xpe+1;
ype+1;
}
}
}
case 2: //Diagonal abajo derecha
if (xpe+1<500){
if (ype+1<300){
xpe+1;
ype+1;
else{
xpe+1;
ype-1;
}
else{ //x+1>500
if(ype+1<300){
xpe-1;
ype+1;
}
}
else{
xpe-1;
ype-1;
}
}
}
if(ype+2=ypa){//Si choca con la pala
xpe+1;
ype-1;
numrebotes++;
}
case 3: //Diagonal abajo izquierda
if (xpe-1>0){
if (ype+1<300){
xpe-1;
ype+1;
}
else{
xpe-1;
ype+1;
}
else{
if (ype+1<300){
xpe+1;
ype+1;
}
}
else{
xpe+1;
ype-1;
}
}
if( ype+2=ypa){ //Si choca con la pala
xpe-1;
ype-1;
numrebotes++; [c18]
}
tablero [antigua_xpe,antigua_ype].setBackGround(Color.WHITE); //Pinto la antigua coordenada donde estaba situada la pelota antes de moverse de blanco.
tablero[xpe,ype].setBackGround(Color.YELLOW);//Y la nueva de amarillo.
DibujarPelota[c20]();//Y la dibujo.
NOTA:
De nuevo, no han sido dos horas, sino dos tardes enteras y una mañana… y aun así, sigo sin tener claras unas cuantas cosas.[c22]
Lo de conseguir que la pelota se moviera, ha sido lo más complicado y aun sigo sin saber si está bien. Lo he hecho con coordenadas, intentando comprobar que cada vez que vaya a chocar con alguna de las paredes, rebote justo en la dirección contraria. Y además, que borre la posición actual, es decir, que se pinte de blanco como está el resto del tablero, y que se pinte de amarillo la nueva posición donde se ha movido.[c23]
Con la pala he hecho algo parecido, pero una cosa que no tengo clara, es si, en el método que lee los eventos de teclado, muevo la pala hacia la izquierda o la derecha, si habría que mover las tres coordenadas que ocupa la pala, o con mover la del centro, ya se desplazaría la pala entera. [c24]Por esta razón, he creado los métodos de dibujar pala y nuevasCoordenadasPala para que una vez que compruebo si la pala puede moverse hacia un lado u otro, se modifiquen las tres coordenadas de la pala y no solo una.
Puede que esté todo fatal, pero aun así, creo que me ha servido bastante de práctica, aunque solo haya sido para intentar pensar, muy detenidamente, y por mi misma, como intentar solucionar los problemas que se plantean, aunque creo que ha sido un ejercicio un poco “exagerado”.[c25]
[c1]Cuidado: no se le puede dar el mismo nombre a dos variables distintas dentro del mismo ámbito J
[c2]Buena idea el usar constantes para estas cosas.
[c3]Si vas a programar el espacio de juego como una matriz de etiquetas, entonces es mejor darles un tamaño bastante mayor de un píxel. Porque para darles un píxel… para eso trabajo directamente con coordenadas de pantalla y me olvido de las etiquetas
[c4]No es necesario conocer el sentido de movimiento de la pala, puesto que se va a mover siempre donde diga el usuario.
[c5]Como siempre es difícil acordarse de cuál es el layout por defecto en un panel, es mejor casi poner explícitamente que queremos un BorderLayout y nos quitamos de problemas
[c6]¡Ojo! Esta variable no ha sido declarada previamente.
[c7]¡Pero estás definiendo entonces una matriz de 500×300 posiciones!!
[c8]No es necesario volver a crear las etiquetas: como ya las ha creado el constructor, bastaría con cambiarles el color
[c9]Es más, si luego añades las nuevas etiquetas al panel, no se añadirán en las coordenadas que tú quieres (k y m) sino al final del todo de la cuadrícula, pues así funciona GridLayout, recuerda.
[c10]Lo mismo que en el comentario de más arriba.
[c11]¡Ojo! En algún momento se debería invocar a los métodos que dibujan la pala y la pelota J
[c12]“xpa = xpa – 1”
[c13]¿Por qué?
[c14]Pero no puedes dar las coordenadas de un matriz con paréntesis, tienes que usar corchetes…
[c15]Se supone que a este método hay que pasarle argumentos…
De todas formas, estrictamente hablando no necesitas un método como éste: como los atributos de la clase exterior son visibles desde la clase interna, puedes modificar directamente su valor (como de hecho ya lo has hecho)
[c16]Pero se supone que en las líneas anteriores del método ya has estado dibujando la pala, ¿no?
[c17]¡Ojo! Cuando la pelota choca contra una pared, no olvides cambiar el valor de la variable dirección J
[c18]¿Cuándo se actualiza el valor del número de rebotes en la pantalla?
[c19]Esta forma de controlar el movimiento de la pelota es enrevesada, pero funciona. Prueba superada, sí señora J
[c20]De nuevo lo de antes: ¿no acabas de dibujarla?
[c21]¿Cuándo se llama al método “fin”?
[c22]Bueno, este ejercicio era digamos que “curtiente”. El más difícil de toda la tanda. Era esperable que os pudiese llevar más tiempo. Aunque quizá dos tardes y una mañana… no esperaba que tanto. Digamos que ambos tenemos que revisar cosas: yo tengo que controlar más a la hora de poner enunciados, y tú tienes que coger un poco de ritmo de cara a resolver problemas de programación complejos. Y en algún punto medio del camino nos encontraremos
[c23]En principio el planteamiento de algoritmo que has hecho para resolver esto es perfectamente válido.
[c24]Eso depende. Si la pala ocupa varias posiciones, entonces tendrás que repitar todas ellas. Que es lo que entiendo que haces en el propio método keyPressed, ¿no?
De hecho, estrictamente hablando, la estarías pintando dos veces, una en keyPressed y otra en pintarPala. A mí, personalmente, me parece más elegante la forma en que pintas en pintarPala, puestos a elegir.
[c25]Sí, por la parte que me toca, creo que me entusiasmé y se me fue la mano. De todas formas, espero haber logrado cierto efecto: haber doblado tanto el papel que ahora ya no se desdoble. ¿Me explico?