Programando en Java con Greenfoot
En el anterior post, donde explicábamos conceptos básicos de la POO como las clases, objetos, métodos y atributos, nos apoyamos principalmente en el interfaz visual que nos ofrece Greenfoot. En este artículo, vamos a entrar un poco más a fondo y explorar el programa Java que estamos creando con la ayuda de esta herramienta.
Para ello, con el proyecto abierto, nos situamos en la clase wombat en el diagrama de la derecha. Hacemos que aparezca el menú contextual (botón derecho del ratón) y elegimos ‘Abrir el editor‘. Lo que vemos es el código escrito en lenguaje Java que define esta clase.
Si tienes muy poca experiencia en programación quizás te resulte casi incomprensible, pero analizando por partes verás que no es tan difícil de entender.
En una primera revisión, como podemos ver en la imagen de abajo, se distinguen varias secciones enmarcadas con recuadros de distintos colores que explicaremos a continuación:
* Importación de clases: es la sección inicial, en fondo blanco. Mediante la cláusula ‘import‘, podemos incluir las clases que necesitemos en nuestro programa para que todo funcione correctamente. Las clases suelen encontrarse agrupadas de forma lógica en librerías o packages(paquetes). Un paquete es por tanto un conjunto de clases ya construidas que podemos utilizar en nuestra aplicación, lo que nos ahorra programar todas las funcionalidades desde cero:
1 2 3 |
import greenfoot.*; // (World, Actor, GreenfootImage, and Greenfoot) import java.util.List; import java.util.ArrayList; |
En el apartado ‘la API de Java’, en este mismo artículo, te explicamos más en detalle estos conceptos.
* Declaración de la clase: es el bloque donde definimos la clase Wombat en sí y que está delimitada visualmente por un cuadro verde. Comienza con unos comentarios (qué hace la clase, su autor, versión…), cuyo inicio y final se marcan con los caracteres ‘/* ‘ y ‘*/’ (es muy recomendable incluir comentarios a lo largo del programa para hacer más entendible nuestro código) y continúa con:
1 2 3 |
public class Wombat extends Actor { ............... ............... |
la expresión public class Wombat extends Actor’ la podríamos traducir literalmente por ‘la clase Wombat extiende la clase Actor , y encierra uno de los elementos fundamentales de la POO, la herencia. Explicaremos más detenidamente el concepto de herencia en un siguiente apartado en este artículo, pero básicamente estamos diciendo que creamos la clase Wombat (más específica) a partir de la clase Actor (más genérica) ‘heredando’ o teniendo disponibles sus atributos y métodos.
El término public es un modificador de acceso y define a esta clase como pública, siendo accesible por tanto por cualquier otra clase. Por defecto, si no incluímos ningún modificador (default), solo sería accesible por clases que están incluídas en su paquete.
Hay otros dos modificadores de acceso, que al igual que los anteriores, también son aplicables a los métodos y a los atributos: protected (similar a default aunque permite heredar a una subclase que no está en el paquete ) y private (si queremos que los métodos o atributos sean solo accesibles dentro de la clase). Si quieres saber más, puedes consultar este artículo sobre modificadores Java.
El código que define la clase se encierra entre llaves ‘{‘ y ‘}’ ‘. Vemos que a su vez tiene bloques diferenciados, Vamos a analizarlos:
* Declaración de atributos: es el recuadro inicial en fondo blanco:
1 2 3 4 5 6 7 |
private static final int EAST = 0; private static final int WEST = 1; private static final int NORTH = 2; private static final int SOUTH = 3; private int direction; private int leavesEaten; |
El grupo de los cuatro primeros: EAST, WEST, NORTH y SOUTH, son las direcciones a las que puede orientarse el Wombat. Son de tipo entero (int) y se han definido de forma que actúan como constantes para cualquier objeto Wombat al tener los modificadores final y static. El primero no permite que se modifiquen sus valores (0,1,2 y 3),y el segundo las define como variables de clase, es decir, no es necesario crear un objeto para utilizarlas. Recordemos en el anterior post el efecto que producía pasar uno de estos números como parámetro al método setDirection(int direction). ¡Hacían que el Wombat se girará a derecha, izquierda, arriba o abajo !. El otro modificador (private) indica que solo son accesibles dentro de la clase.
El grupo de los dos siguientes atributos, direction y leavesEaten, son variables de instancia y almacenarán la información en formato entero (int) de la dirección y las hojas que han comido cada uno de los objetos Wombat que creemos (instanciemos) a partir de esta clase. Se les denomina también campos y toman valores particulares a nivel de objetos.
* Constructor: es el proceso que crea un objeto a partir de una clase y que se ejecuta automáticamente cada vez que instanciamos un objeto (al seleccionar new WombatWordl, new Wombat() ó new Leaf()).
Su código aparece en el recuadro amarillo justo debajo de la definición de atributos:
1 2 3 4 5 |
<strong>public Wombat()</strong> { setDirection(EAST); leavesEaten = 0; } |
Tiene la misma estructura que un método aunque con algunas particularidades: debe tener siempre el mismo nombre que la clase y no tener ningún return type.
No es necesario declarar siempre un constructor, ya que toda clase tiene uno que inicializa los campos del objeto con valores por defecto. Si queremos personalizar ese proceso de creación, entonces podemos escribir uno o varios constructores, a los que a su vez podemos pasar o no parámetros. Por ejemplo, en este caso, el constructor inicializa los campos direction con el valor ‘EAST’ y leavesEaten con el valor ‘0’.
* Métodos: todo el código restante de esta clase, son las definiciones de sus métodos. Cada uno aparece en un recuadro amarillo, con una estructura muy parecida:
- Comentario indicando que realiza ese método
- Declaración del método: ‘public void act()‘, ‘public boolean foundLeaf()‘, ‘public void eatLeaf()‘,…
- encerrados entre llaves {}, las instrucciones que han de ejecutarse
Aunque no podemos entrar en detalle de todo el código, vamos a revisar por ejemplo el primer método que aparece: act()
1 2 3 4 5 6 7 8 9 10 11 12 |
<strong>public void act()</strong> { if(foundLeaf()) { eatLeaf(); } else if(canMove()) { move(); } else { turnLeft(); } } |
Sabemos por un post anterior, que la palabra reservada void nos indica que el método no devolverá ningún valor. Interpretando o traducciendo del inglés el código encerrado entre las llaves, vemos que hay una estructura condicional:
if(condición 1) {
instruccion 1…..
}
else if (condición 2) {
instrucción 2
}
else {
instrucción 3
}
If, es el ‘si’ condicional en inglés y el ‘else’ nos permite evaluar una alternativa. Podríamos leer todo este código literalmente como:
«si se cumple la condición 1, ejecuta la instrucción 1, si en cambio se cumple la condición 2, ejecuta la instrucción 2 y en cualquier otro caso (no se cumple ninguna de las dos anteriores), ejecuta la instrucción 3»
¿Qué condiciones e instrucciones hay? Pues vemos que son precisamente llamadas a otros métodos de este mismo objeto. Si seguimos traduciendo literalmente sería algo así como:
«si se cumple la condición ‘he encontrado una hoja’, ejecuta ‘comer hoja’, si en cambio se cumple la condición ‘puedo moverme’, ejecuta la instrucción ‘mover’ y en cualquier otro caso, ejecuta la instrucción ‘gira a la izquierda’ »
Si queremos cambiar el comportamiento de los Wombat que creemos, solo tenemos que modificar los métodos adecuados. Lo iremos viendo en próximos posts, pero puedes practicar un poco con cambios sencillos. Por ejemplo, si modificas este método act() en su primer condicional de forma que añadas llamadas a los otros dos métodos de la forma:
1 2 3 4 5 |
if(foundLeaf()) { eatLeaf(); turnLeft; move(3); } |
¿qué ocurrirá cuando el Wombat encuentre una hoja? Es fácil de adivinar ¿verdad?. Prueba las variantes que quieras y no olvides compilar siempre después de realizar los cambios. Puede ser buena idea que al hacer el primer cambio guardes el proyecto con otro nombre (save as…) y continuar ya con éste, de forma que mantegas el proyecto y código original de referencia.
La API de Java
Si analizamos más en detalle el primer bloque, vemos que estamos importando las clases List y ArrayList que están contenidas en el package java.util. Estos paquetes están organizados de forma jerárquica a modo de directorios/carpetas, agrupando clases relacionadas entre sí y la notación ‘.’ nos indica que estamos descendiendo en la jerarquía de esa estructura. La librería java.util se suministra en el entorno de desarrollo Java SE que si recordamos, instalamos al inicio. Podemos ver todo el conjunto de paquetes disponibles para la versión 7 en lo que se denomina la API de Java: http://docs.oracle.com/javase/7/docs/api/index.html
Si accedemos a ella, podemos pinchar en cualquier package y nos mostrará todas las clases que contiene. Por ejemplo en java.util, veremos que tanto List como ArrayList son dos interfaces (una construcción en Java similares a las clases que veremos más adelante). Si pinchamos a su vez en cualquiera de ellas, nos mostrará todos sus métodos y los detalles de implementación. Esta documentación es de gran utilidad, con miles de clases y sus métodos correspondientes. Es una herramienta con la que un programador Java debe familiarizarse para localizar clases y métodos que puedan tener funcionalidades que necesite para su programa y saber cómo utilizarlas.
Por defecto, en cualquier clase que construyamos se incluirá el package java.lang, que contiene clases fundamentales para el funcionamiento de cualquier programa Java. En concreto, el paquete java.lang,contiene la clase Object, que es la superclase de todas las clases existentes en Java, es decir la que se encuentra en la raíz de esta estructura jerarquica.
La otra libreria que importamos es la de Greenfoot. Esta es una colección de clases que han sido desarrolladas por los creadores de este entorno para facilitar la creación de los juegos y simulaciones.
En este caso no viene incluída en el Java SE 7, sino que se instalan con el propio programa, y la denominaremos API de Greenfoot. También podemos acceder a su documentación con información muy valiosa sobre las clases que contiene (descripción de su funcionalidad, métodos y atrbutos) en: http://www.greenfoot.org/files/javadoc/
Herencia
El mecanismo de herencia de la POO facilita la creación de una nueva clase a partir de una ya existente. Esta nueva clase tendrá (heredará) los atributos y métodos de la anterior, pudiendo mantener o modificar dichos elementos o añadir unos nuevos si así lo necesita, para que se adapten a sus características y comportamiento particulares (recordemos la expresión ‘extiende a…’).
Si nos fijamos en las flechas del diagrama de clases que nos aparece en el lado derecho del proyecto Wombat, veremos que nos indican la jerarquía o relaciones de herencia entre ellas. Decimos que ‘la clase Actor es padre o es una superclase de las clases Wombat y Leaf(hoja)’ o viceversa, ‘Wombat y Leaf son subclases o clases hijas de la clase Actor‘. Estamos indicando que ésta es más genérica que las clases Wombat o Leaf y que hay una relación ‘is-a‘ (‘Wombat es un Actor’, ‘Leaf es un Actor‘).
Un aspecto que debemos tener en cuenta es que estas relaciones de jerarquía deben tener sentido, pero no tienen que ser idénticas y recrear todas las que existen en el mundo real. Ya comentamos que en la POO, modelamos una situación que queremos tratar con nuestro programa en base a abstracciones esquemáticas (clases) que van a sernos útiles en ese contexto. En el mundo real, existiría, de manera simplificada, la relación de herencia: Animal –> Mamífero –> Wombat, pero en el contexto del juego que estamos creando, esas características (animal, mamífero) no son relevantes. Preferimos crear una clase Actor que define a cualquier elemento que podemos poner en el juego para que ‘actúe’ (juegue). La clases en este caso modelan los ‘roles’ que van a desempeñar los objetos.
Para ver como funciona todo esto en Greenfoot, podemos hacer una prueba. Elige con el ratón la clase Actor y en el menú contextual (botón derecho ratón) elige ‘Nueva subclase‘. Puedes crear la que quieras, por ejemplo Roca, y elegir la imagen ‘rock.gif’. Verás que aparece en el diagrama de clases, al mismo nivel que Wombat y Leaf. Compila el escenario (botón en la parte inferior derecha) y si abres el editor de código, ¿qué características tiene esta nueva subclase?. Como ves solo cuenta con el método act(), pero si creas un objeto Roca con newRoca() y la situas en el cuadrado del juego, puedes seleccionarla y con el menú contextual ver que en la opción ‘heredado de Actor‘ aparecen muchos otros métodos. Si eliges por ejemplo move(), y pones ‘3’ en el cuadro de diálogo que te aparece a continuación, ¡la roca se mueve tres cuadrados a la derecha!. Es decir, funciona un método que no veíamos en el código de esta clase al haberlo heredado de su superclase Actor.
¿Qué ocurre con los atributos?. Si seleccionas en la clase la opción ‘Inspeccionar’ del menú contextual verás que dice que no hay, lo que indica que la clase Actor no tiene variables de clase. Si haces lo mismo en el objeto Roca que creamos antes, verás que sí aparecen una serie de variables de instancia (objeto) como son su posición en los ejes X e Y. Arrastra la roca a otra celda y verás como los valores de esas variables van cambiando.
Si quieres, puedes dar un paso más y crear ahora una subclase de Wombat. Por ejemplo Hormiga. Si sigues los mismos pasos que antes verás que ahora sí aparecen las variables de clase EAST, WEST, NORTH y SOUTH y al seleccionar un objeto Hormiga en el cuadro de juego comprobarás que ahora no solo hereda todos los métodos de Actor, sino también de Wombat. En ese mismo objeto, al inspeccionar los atributos, aparecerán también las variables de instancia ‘direction‘ y ‘leavesEaten‘.
Esta entrada tiene 4 comentarios
Los comentarios están cerrados.
Hola, Buenas noches, puedes poner el codigo del Wombat para que una vez que toque el filo del mundo, gire hacia el lado contrario?
Por favor, es urgente.
Te lo agradeceria mucho.
Puedes probar con el metodo isAtEdge() que es un método que detecta cuando un actor, en este caso el Wombat, está en los límites del Mundo que se ha creado. Si lo ha alcanzado (o traspasado), te devuelve un ‘true’ por lo que es muy parecido al método ‘foundLeave’ que poníamos en el ejemplo, utilizando un if:
if ( isAtEdge() )
{ turn(…..
}
Aquí tienes la descripción del método: http://www.greenfoot.org/files/javadoc/greenfoot/Actor.html#isAtEdge()
De todas maneras, lo mejor es que busques en los foros de Greenfoot donde seguro encontrarás diversas soluciones según sea el escenario y el juego en particular: http://www.greenfoot.org/topics/
Hola, este ejemplo es un juego en 2 dimensiones donde vemos los jugadores/objetos desde arriba. Pueden moverse en ese terreno, de forma Norte-Sur ó Este-Oeste, pero no saltar porque necesitaríamos una tercera dimensión.
Podrías hacer un juego donde se pudieran dar ‘saltos’ si las 2 dimensiones fueran atrás-adelante y arriba-abajo. El método ‘salto’, haría que el Wombat se desplazara una o varias cuadrículas hacia arriba.
como haces para que el objeto salte?