Introduccrión a JavaMail (parte 2)
sábado, febrero 1, 2003 at 1:00AM JavaMail (parte 2): enviando mails de texto
Fecha de creación: 01.12.2002
Revisión 1.0 (17.1.2003)
Alberto Molpeceres
al AT javahispano DOT org
|
Introducción
En el artículo anterior [1] explicamos como recoger nuestros mails (de texto) por medio del API JavaMail, explicando también los preparativos necesarios para usar este API, así que si no lo habeis leido os lo recomiendo, varias de las cosas comentadas allí os serán necesarias.
En este artículo veremos como enviar mails de texto por medio del protocolo SMTP (Simple Mail Transport Protocol), sin duda el protocolo más usado, y seguiremos creando nuestras clases que nos faciliten el uso de JavaMail.
Al igual que hicimos al recoger el correo con POP, crearemos una clase sencilla que represente una session con un servidor de correo SMTP, cuyos métodos iremos creando a lo largo del artículo. Se esqueleto, más o menos autoexplicativo podría ser así:
package org.javahispano.mailer;
import javax.mail.internet.*;
import javax.mail.*;
import java.util.*;
import javax.activation.*;
public class SMTPSession {
private String host;
private String user;
private String password;
private int port = 25;
private boolean debug = false;
private static int DEFAULT_PORT = 25;
public SMTPSession(String host) {
this(host, DEFAULT_PORT , null, null);
}
public SMTPSession(String host, String user, String password) {
this(host, DEFAULT_PORT, user, password);
}
public SMTPSession(String host, int port, String user, String password) {
this.host = host;
this.user = user;
this.password = password;
this.port = port;
}
public void setDebug(boolean value) {
debug = value;
}
}
Ya que un mail se compone de diversas partes (remitente, distintos tipos de receptores, texto, archivos adjuntos, aunque estos últimos no los veremeos en este artículo), tendremos que crear también una clase que represente a un mail (sigo insistiendo que en este artículo sólo serán de texto, dejaremos los mensajes HTML, con attachments, o MIME, para el siguiente) que aún no se ha enviado. Algo así como esta:
package org.javahispano.mailer;
import java.util.*;
import java.io.File;
import javax.mail.internet.*;
public class SMTPMail {
protected Map toAddresses = new HashMap();
protected Map ccAddresses = new HashMap();
protected Map bccAddresses = new HashMap();
protected Map replyToAddresses = new HashMap();
protected String fromName = null;
protected String fromAddress = null;
protected String subject = null;
protected String messageText = new String();
protected SMTPMail(String fromName, String fromAddress, String subject) {
this.fromName = fromName;
this.fromAddress = fromAddress;
this.subject = subject;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
public void addToAddress(String name, String address) {
toAddresses.put(name, address);
}
public void addCcAddress(String name, String address) {
ccAddresses.put(name, address);
}
public void addBccAddress(String name, String address) {
bccAddresses.put(name, address);
}
public void addReplyToAddress(String name, String address) {
replyToAddresses.put(name, address);
}
}
A esta clase le faltan varios métodos que nos harán hacer falta, como por ejemplo para recuperar el asunto del mail o las direcciones de los destinatarios, pero en todo caso eso no es del todo necesario para nuestro ejemplo (supongo que os los podeis imaginar) y haría el mismo demasiado largo. Estos métodos os harían falta, por ejemplo, si quereis almacenar de alguna forma estos mails antes de enviarlos (algo así como el Enviar después de muchos clientes de correo) para su posterior envio o registro.
En general el uso de esta clase es bastante sencillo, hay los atributos clásicos (remitente, dirección del remitente y asunto del mail) que se recogen con el contructor, el texto del mensaje que se ajusta por medi ode un método (setMessageText), y diferentes Map para almacenar las distintas dirección de los destinatarios (para To, copia CC y copia ciega u oculta BCC) y de las respuestas (ReplyTo).
Ahora añadiremos un método a nuestra clase SMTPSession que será el que se encargue de enviar nuestro mensaje. Como he dicho, no prentendo hacer un cliente de correo completo, así que se enviará el mensaje inmediatamente, no esta implementada (aún) la opción de almacenar varios mensajes y enviarlos conjuntamente.
public void sendMail(SMTPMail mail)
throws Exception {
// creamos algunas de las propiedades y la sesión real en si misma
Properties props = new Properties();
props.put("mail.smtp.host", host);
Session session = Session.getDefaultInstance(props, null);
session.setDebug(debug);
//creamos un mensaje de JavaMail real!
MimeMessage message = new MimeMessage(session);
//rellenamos ese mail real con los datos de nuestra clase auxiliar
this.prepareMessage(message, mail);
//finalmente nos conectamos y enviamos el mail
Transport transport = session.getTransport("smtp");
if (user != null && password != null) {
transport.connect(host, user, password);
}
else {
transport.connect();
}
transport.send(message);
transport.close();
}
Este este código aparecen algunas clases del API JavaMail nuevas, Session, MimeMessage y Transport. Session es bastante sencilla de entender, el contexto donde se realizan todas las tareas de una conexión, y Transport es la conexión real al servidor, con los métodos típicos de conexión, envio y cierre de la conexión.
MimeMessage es también sencillo, es la representación que utiliza JavaMail para los mensaje electrónicos. Como podeis ver, se trata de MIME, por lo que usaremos esa clase también en artículos posteriores para enviar HTML con imagenes y/o archivos adjuntos.
Como habreís visto, en el método sendMail utilizamos otro método prepareMessage que se encarga de convertir nuestra representación partículas de los mensajes a la adecuada para JavaMail. Su código es el siguiente:
private MimeMessage prepareMessage(MimeMessage message, SMTPMail mail)
throws Exception {
// ajustamos las iferentes propiedades del mensaje
message.setFrom(mail.getFrom());
message.setSubject(mail.getSubject());
message.setSentDate(new java.util.Date());
message.setText(mail.getMessageText());
//ahora las distintas direcciones
InternetAddress[] addresses = mail.getReplyToAddresses();
if (addresses != null) {
message.setReplyTo(addresses);
}
addresses = mail.getToAddresses();
if (addresses != null) {
message.setRecipients(Message.RecipientType.TO, addresses);
}
addresses = mail.getCcAddresses();
if (addresses != null) {
message.setRecipients(Message.RecipientType.CC, addresses);
}
addresses = mail.getBccAddresses();
if (addresses != null) {
message.setRecipients(Message.RecipientType.BCC, addresses);
}
//almacenamos estos cambios en el mesnaje y listos
message.saveChanges();
return message;
}
Como podemos ver, este método recupera las direcciones de email de los destinatarios en forma de array de InternetAddress, pero nosotros las habiamos añadido en forma de parejas de String representando nombre y dirección de correo electrónico del destinatario.
Estas InternetAddress son la implementación Java de las direcciones de correo electrónico en internet tal y como marca el RFC822. De esta forma necesitaremos un método dentro de nuestra clase envoltorio SMTPMail para convertir las dirección de parejas de String a InternetAddress.
private InternetAddress[] getAddresses(HashMap addresses)
throws Exception {
InternetAddress address = null;
ArrayList list = new ArrayList();
String name = null;
String email = null;
Iterator i = addresses.keySet().iterator();
while (i.hasNext()) {
name = (String) i.next();
email = (String) addresses.get(name);
try {
list.add(new InternetAddress(email, name));
}
catch (Exception ex) {
System.out.println("Dirección de correo inválida: " + email);
}
}
if (list.isEmpty()) {
return null;
}
InternetAddress all[] = new InternetAddress[list.size()];
all = (InternetAddress[]) list.toArray(all);
return all;
}
Por supuesto podríamos haber optado por convertirlas al añadirlas pero he optado por hacerlo así simplemente por no incluir todos los conceptos a la vez. Además, JavaMail realiza comprobaciones sobre las existencia de los servidores destinatarios, por lo que es mejor hacerlas todas a la vez que una a una, sobre todo si no tenemos conexión permanente a internet.
Ahora simplemente crearemos una clase de prueba que envie un sencillo email de texto para ver si todo funciona.
package org.javahispano.mailer.test;
import org.javahispano.mailer.*;
public class SendMailTest {
public static void main(String[] args) {
SMTPSession smtp = new SMTPSession("serviodor.com", "usuario", "password");
// smtp.setDebug(true);
SMTPMail mail = new SMTPMail("yo", "yo@servidor.com", "Mail de prueba");
mail.addToAddress("Tu", "tu@servidor.com");
mail.setMessage("Mailer test. \r\n Simple ASCII text");
mail.setMessage(text);
try {
smtp.sendMail(mail);
System.out.println("Mail enviado con exito");
}
catch (Exception e) {
System.out.println("No se puedo enviar el mail: " + e.getMessage());
e.printStackTrace();
}
}
}
Perfecto, ¿no?.
Por supuesto tenéis disponible el código de este artículo para su descarga [2].
Conclusión
En estos dos primeros artículos hemos mostrado lo fácil que puede resultar usar JavaMail, e incluso hacer una pequenya libreria que nos facilite su reutilización siempre que lo necesitemos.
Hasta ahora nos hemos dedicado a los mensajes de texto, pero todos sabemos que no siempre es así, y que en muchos casos nos interesa leer/enviar mensajes que contengan HTML o archivos adjuntos. Esos artículos llegarán, espero (todo depende del tiempo), asi que si alguno de vosotros esta interesado en tomar el relevoque me lo haga saber
Recursos
[1] JavaMail (parte 1): recuperando mails con POP,
/articulos/ver_articulo.jsp?id=69
Acerca del autor
Alberto Molpeceres
Alberto es ahora mismo desarrollador de aplicaciones en ámbito cliente/servidor para la empresa T-Systems International GmbH en Dresden (Alemania). Cuando no está trabajando o "metiendo caña" al resto de los integrantes de javaHispano intenta pasear con su novia, buscar la desaparecida lógica del idioma alemán o intenta disfrutar del buen ambiente de la Neustadt.
Realizacrión de un videojuego
sábado, febrero 1, 2003 at 1:00AM Programar un videojuego en Java.
Fecha de creación: 10.01.2003
Revisión 1.0 (30.01.2003)
Raúl Bonachía Castillo
rbonachia AT educaline DOT com
|
Introducción
La intención de estos artículo es proporcionar una técnica para poder organizar la programación de un sencillo videojuego en 2D utilizando J2SE. El propósito es estructurar la mentalidad del programador para atacar el objetivo de emular un videojuego, un juego en movimiento. Para tal fin, desarrollaremos uno sencillo.
La idea es que todo lo escrito sea criticable. Mi experiencia en Java es reciente y seguramente todo sea muy mejorable, pueda optimizarse de alguna manera...etc. Pero como me suele gustar decir, lo importante es ofrecer PASIÓN a lo que dedicas tu tiempo. Con ello quiero animar a quienes puedan aportar conocimientos en estos temas a que escriban algo parecido a lo que se pretende con estos artículos, para favorecer la difusión y aprendizaje del desarrollo de juegos en java, como Java3D...etc Aunque Java no parece ser la plataforma adecuada para desarrollar juegos o videojuegos, ¿es impensable que algún día cambie tal situación?. Opinad e ilustrarnos.
En este artículo vamos a introducir los conceptos básicos que nos permitirán desarrollar un videojuego sencillo en posteriores entregas. Así, al acabar la lectura del presente tutorial habremos creado las siguiente clases (pequeñas y comprensibles): En este artículo vamos a introducir los conceptos básicos que nos permitirán desarrollar un videojuego sencillo en posteriores entregas. Así, al acabar la lectura del presente tutorial habremos creado las siguiente clases (pequeñas y comprensibles):
- Clase Molde
- Clase Dimension2D
- Clase Rectangulo
- Clase Estrella
- Clase Imagen
- Clase Avion
- Clase Disparo
- Interfez Colisionable
- Interfez Movimiento
no acentuaré nunca nombres de métodos ni de clases
No olvidemos nuestro objetivo. Programar un videojuego. Bien, para ello concentrémonos en uno. Necesitaremos:
- Un argumento
- Un componente gráfico en el que se produzca la acción del argumento. (eligiremos el applet).
- Objetos en movimiento que den vida al argumento.
Somos un avión, y nos ataca un numero incierto de aviones enemigos.
¡¡¡Nosotros tenemos que defendernos!!!. Ellos disparan fuego y nosotros tenemos la capacidad de disparar también, siempre en trayectoria horizontal. (Pero claro, eso es modificable. En una mejora del juego, ¿por qué no introducir trayectorias sinusoidales...?. Puede programarse...) Nuestra capacidad de movimiento se limita a ascender y descender en vertical, y a desplazarnos a izquierda y derecha horizontalmente. Nuestros enemigos se desplazan horizontalmente de derecha a izquierda, surgiendo del extremo derecho de la pantalla y desapareciendo por el extremo izquierdo de ella. Si alcanzamos a alguno con un solo disparo, desaparece de la pantalla. ¡Lo hemos eliminado!. Ello debiera suponer una puntuación, preestablecida anteriormente. Ahora bien, si sucede vicerversa, si son ellos lo que nos alcanzan...bueno, seamos generosos con nosotros mismos. Dispondremos de un nivel de vida. No nos eliminarán del juego hasta que hayamos recibido un cierto número de impactos.
Como cualquier batalla que se precie, debemos enmarcarla dentro de una escena amigable. Por ejemplo, una noche tachonada de estrellas. Programaremos estrellas que se desplazan también horizontalmente, independientemente de lo que suceda en el campo de batalla, proporcionando una mayor sensación de movimiento.
Hay ciertos elementos que no deben faltar, tales como avisos de mensajes (suponga que hemos eliminado un elevado número de aviones, como recompensa podríamos quedar inmunes a los disparos enemigos durante un cierto lapso de tiempo), indicándonos ciertas situaciones, un indicador gráfico del nivel de vida ( lo emularemos mediante una corona circular, cuyo color desciende progresivamente a medida que recibimos impactos), sonidos al disparar ...etc. Estos pequeños detalles configuran y dar cuerpo al videojuego, permitiendo que el usuario se sienta más protagonista e identificado con el argumento.
Este breve esquema resulta más que suficiente para clarificar cuál será nuestro objetivo y podría representar la primera pantalla del videojuego. En una segunda pantalla, el protagonista podría ser un tanque, con distintas posiciones para su cañón en un ángulo de 180 grados. Ahora los aviones nos bombardearían y sus proyectiles tendrían desplazamientos verticales.....Durante el desarrollo de este tutorial, en principio nos limitararemos a programar la primera pantalla, pero hecho esto lo demás no debiera tener mayor problema.
He aquí un tema crucial. Vamos a utilizar un applet. Por varias razones. La primera, porque es lo que recomendaba el libro que he seguido. Segundo, porque da la oportunidad de utilizarlo en los navegadores, y tercero porque la herramienta appletviewer permite visualizar casi siempre todos nuestras variaciones sin mayores problemas. Para programar el juego utilizé el IDE Jcreator. Con él u otro editor se hará la tarea más fácil. Si no, si estás empezando y no dispones de ninguno no te preocupes. El block de notas te sirve igualmente.
Alternativas: podría hacerse con otro componente gráfico distinto de un applet. No se alteraría demasiado la programación. Además, daría la oportunidad de utilizar Java Web Star... Una u otra alternativa parecen igualmente razonables. De todas formas, podría ser un buen punto para dar lugar a la discusión.
Como todo esto es muy opinable, yo opto por elegir un applet.
Denoto como objetos en movimiento a todos aquello que van a formar parte del videojuego y que son visibles o no, según la situación. Ejemplo de ellos serían las estrellas, los disparos, los aviones..... Todos ellos tienen un denominador común: son entes pertenecientes al juego, aunque su naturaleza y funcionalidad sean diferentes. Aquí entra el concepto de modularidad. Tenemos que diseñar el programa teniendo en cuenta que debemos poder eliminar o añadir elementos nuevos sin destrozar el código de forma severa. ¿Cómo se consigue esto?.Utilizando adecuadamente clases jerárquicas, extendiendo unas de otras, de tal manera que la relación de herencia nos permita sobreescibir métodos para la aparición de nuevos elementos. Del libro al que se hace referencia en la bibliografía extraje las siguientes ideas para organizar las clases
Una clase madre, que denotaremos Molde. Una instancia de Molde representa cualquier objeto participante en el videojuego, pero en su concepto más abstracto. Contendrá métodos para decidir si es visible o no y si está activo o no. Heredaremos de Molde para construir las clases Dimension2D y Imagen. Estas clases son fundamentales. De Dimension2D obtendremos aquellos objetos que queramos construir y que se visualizarán sin utilizar imágenes, usando procedimientos de pintura de Java. De Imagen obtendremos, en cambio, aquellos objetos que necesitarán una imagen para ser representados. Así, por ejemplo, si necesitamos que aparezca un rectángulo que ascienda y descienda (para simular una maza que aplaste aviones) heredaremos de Dimension2D. Y para simular a los aviones en sí necesitaremos heredar de Imagen, con una clase que se denote MolveAvion, por ejemplo. De Avion haremos que hereden diferentes tipos de aviones, si es necesario. Bombardero, Caza....lo que haga falta, cada uno con la imagen que le caracterice.
En nuestro esfuerzo por adquirir una destreza y una técnica lo más general posible, emplearemos interfaces. Las interfaces se denominarán: ![]()
¿Por qué hacer uso de ellas?. Porque para nosotros, todos los objetos van a ser objetos en movimiento, algunos de ellos con la capacidad de poder intersecar unos con otros. Luego en un procedimiento de abstracción, nos dará igual que sea una estrella que un avión. Lo cierto es que la estrella y el avión se mueven, luego debemos dotarles de métodos para que ACTUALICEN sus posiciones en el componente gráfico. Para eso se utilizan las interfaces: dotan de métodos de nombre común a las clases que las implementan y que deben realizar misiones similares. Veremos posteriormente que son ciertamente utiles en aras de una mayor organización en la programación.
interface Movimiento{
/* Actualizará (modificará) la antigua posición del Molde en cuestión
a la coordenada (x,y) */
public abstract void nuevaPosicion(int x, int y);
/* En caso de necesidad, podemos asignarle mayor velocidad aparente de
movimiento al molde, llamando a este método*/
public abstract void nuevaVelocidad(int x, int y);
}
Aclaraciones: cuando hablamos de colisión de moldes, cada molde está representado por una imagen (si hereda de Imagen), o una figura geométrica pintada por java (si hereda de Dimension2D). Aquí debemos trabajar con una limitación. Para imaginar la colisión de la forma más general posible, debemos considerar siempre el rectángulo más pequeño en el que se inscribe la imagen o figura, respectivamente, y trabajar con las coordenadas de la esquina superior izquierda e inferior derecha para poder hablar de intersección (colisión) de rectángulos, que es mucho más fácil que hablar de otro tipo de colisiones.
interface Colisionable{
/*indica al molde que implemente esta interfaz si ha colisionado con otro molde
ajeno encapsulado por el rectángulo de menor dimensión con bordes paralelos a
los ejes X e Y, de coordenadas (x1,y1) e (x2,y2)*/
public boolean intersecta(int x1, int y1, int x2, int y2);
// Llamaremos a este método si la intersección se ha producido.
public void colision();
}
Para determinar si dos moldes han colisionado, podemos aplicar esta caracterización, que es muy útil.Si { (x1,y1),(x2,y2) } y { (x3,y3),(x4,y4) } conforman los límites de dos moldes ambos intersecan si y solo si: (x2>=x3) && (x4>=x1) && //las x se solapan (y2>=y3)&&(y4>=y1)//las y se solapan

Poco a poco, vayamos diseñando las clases necesarias para poder desarrollar después aquellas que representen los aviones, disparos, estrellas....etc. Mostremos sus relaciones jerárquicas para ponernos en situación:
import java.awt.Graphics;
abstract class Molde{
protected boolean visible; //¿es visible el molde?
protected boolean activo; //¿ Es actualizable el molde?
//metodos abstractos
abstract void pintar(Graphics g);
//metodos basicos;
public boolean esVisible(){
return visible;
}
public void cambiarVisible(boolean b){
visible=b;
}
public boolean esActivo(){
return activo;
}
public void cambiarActivo(boolean b){
activo=b;
}
//Suspende el molde
public void suspender(){
cambiarVisible(false);
cambiarActivo(false);
}
//Reactiva el molde
public void restablecer(){
cambiarVisible(true);
cambiarActivo(true);
}
}
Los métodos son bastante claros. Con ellos determinamos si la clase que herede de Molde, y que representará un objeto visual, está visible y/o activo y nos damos la posiblidad de modificarlo cuando sea necesario. Además, contamos con dos métodos BÁSICOS, como son pintar y actualizar. Todos los objetos heredados de molde serán supceptibles de ser pintados, (de eso se trata...), y deberán actualizarse. Este concepto es FUNDAMENTAL. Cada objeto que pertenezca al videojuego estará en movimiento, aunque esté parado en algún instante. Eso significa que deberemos ACTUALIZAR su posición, es decir, modificarla. Esto habrá que hacerlo para todos los objetos. Aunque pueda parecer un poco complicado a priori (si hay mucho objetos...), se solucionará utilizando unas clases Controladoras. Habrá una ControladoraAviones, otra ControladoraDisparos...etc, que se encargarán de actualizar A LA VEZ, todos los objetos que controlen. Así, el repintado se realizará sin mayores problemas.
Poco a poco....
import java.awt.Color;
abstract class Dimension2D extends Molde{
protected int locx;//coordenada X del molde
protected int locy;//coordenada Y del molde
Color color;
boolean relleno;
//si tiene color de relleno o no
public boolean obtenerRelleno(){
return relleno;
}
public void cambiarRelleno(boolean b){
relleno=b;
}
public void cambiarColor(Color c){
color=c;
}
public Color obtenerColor(){
return color;
}
}
locx, locy representarán las coordenadas en el componente gráfico seleccionado del molde en cuestión. Tener en cuenta que todo videojuego, este en particular, depende continuamente de las posiciones y, por tanto, de las coordenadas. Habrá que hacer uso de estas variables continuamente, tanto aquí como en Imagen.
Para observar cómo utilizar la clase Dimension2D construyamos la clase Rectangulo.
import java.awt.Graphics;
import java.awt.Color;
class Rectangulo extends Dimension2D implements Movimiento,Colisionable{
public int anchura,altura;
//CONSTRUCTOR de Rectangulo
public Rectangulo(int x,int y,int anchura, int altura,Color c){
locx=x;
locy=y;
this.anchura=anchura;
this.altura=altura;
color=c; //Inicializamos variables de Dimension2D
relleno=false;
restablecer();
cambiarRelleno(true);
}
/* cambiarPosición es fundamental para ACTUALIZAR la posición.
( actualizar=modificar)*/
public void nuevaPosicion(int x, int y){
locx=x;
locy=y;
}
/* si en algún momento deseamos INCREMENTAR la ACTUALIZACION y, por tanto,
incrementar la velocidad aparente de movimiento.*/
public void nuevaVelocidad(int vx, int vy){
locx+=vx;
locy+=vy;
}
// Para saber si intersecta con un avion, un disparo...etc
public boolean intersecta(int x1, int y1, int x2, int y2){
return visible&&(x2>=locx)
&&(locx+anchura>=x1)&&(y2>=locy)
&&(locy+altura>=y1);
}
public void colision (){
suspender();
/*Si le damos la función de aplastar aviones, lo lógico es que
desaparezca al intersecar con uno.*/
}
public void cambiarOrigen(int x,int y){
locx=x;
locy=y; //Método Propio de Rectangulo
}
public void actualizar(int an,int al){//Método Propio de Rectangulo
anchura=an;
altura=al;
}
public void pintar(Graphics g){
if (visible){
g.setColor(color);
if (relleno){
g.fillRect(locx,locy,anchura,altura);
}
else{
g.drawRect(locx,locy,anchura,altura);
}
}
}
}
Con Rectangulo obtenemos un molde Dimension2D que define a un rectángulo: sus coordenadas x,y, respecto a la esquina superior izquierda por él definida, y su anchura y altura. Además, le asignamos un color y le proporcionamos la posibilidad de ACTUALIZAR su posición gracias a la interfaz Movimiento y de PINTARLO en un componente gráfico. Por otra parte, es supceptible de colisionar con otros objetos al implementarle la interfaz Colisionable. Paciencia, todavía no hemos realizado el código necesario para ver en acción su utilidad. Para ello necesitaremos un hilo dentro del applet, pero esto lo veremos en el/los siguientes artículos. Sigamos viendo más código.
Ahora, veamos cómo construir la clase Estrella, que representa a una estrella.
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Estrella extends Dimension2D implements Movimiento{
Random r;
int aleatorio;
boolean apagar;
Estrella(int x, int y){
r=new Random();
this.apagar=apagar;
/*para ofrecer vistosidad, otorgaremos color aleatorio a cada Estrella*/
color=new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255));
cambiarVisible(true);// Estrella nacerá visible
locx=x;
locy=y;
}
// cambiarPosición es fundamental para ACTUALIZAR la posición.
public void nuevaPosicion(int x, int y){
locx=x;
locy=y;
}
/* si en algún momento deseamos INCREMENTAR la ACTUALIZACION y, por tanto,
incrementar la velocidad aparente de movimiento.*/
public void nuevaVelocidad(int vx, int vy){
locx+=vx;
locy+=vy;
}
void pintar(Graphics g){
g.setColor(color);
/* Un punto se puede representar como una recta de mismo origen y final.*/
g.drawLine(locx,locy,locx,locy);
}
}
Aquí introducimos un aspecto fundamental que forma parte de cualquier videojuego: aleatoriedad. La clase Random (java.util.Random) proporcina números aleatorios. En concreto, el método nextInt(n) obtiene un número cualesquiera entre 0 y n. A la estrella vamos a darle color aleatorio. Volveremos a utilizarlo posteriormente.
import java.awt.Image;
import java.awt.Graphics;
class Imagen extends Molde{
protected int locx; //coordenada x respecto esquina superior izqda.
protected int locy; // coordenada y respecto esquina superior dcha
protected int anchura,altura; //dimensiones de la imagen
protected Image imagen; // la imagen
//CONSTRUCTORES
/*Construimos la imagen con coordenadas (0,0) en su esquina superior izquierda*/
public Imagen(Image i, int anchura, int altura){
locx=0;
locy=0;
imagen=i;
if (imagen!=null){
this.anchura=anchura;//obtener anchura de la imagen
this.altura=altura;// obtener altura de la imagen
}
restablecer();
}
/*Construimos la imagen con coordenadas (x,y) en su esquina superior izquierda */
public Imagen(int x, int y,Image i, int anchura, int altura){
locx=x;
locy=y;
imagen=i;
if (imagen!=null){
this.anchura=anchura;//obtener anchura de la imagen
this.altura=altura;// obtener altura de la imagen
}
restablecer();
}
public void actualizar(){}
public void pintar(Graphics g){
if (visible){
g.drawImage(imagen,locx,locy,anchura,altura,null);
}
}
}
Detengámonos con esta clase, porque es muy importante y de ella heredarán todos los objetos (aviones y disparos) de los que hará gala el videojuego. Lograremos pintar la imagen que deseemos, sea cual sea, en el applet que va a a ser soporte de nuestro videojuego. Así, en el applet original, cargaremos las imágenes que consideremos oportunas y a la hora de crear los objetos que hereden de Imagen se los pasaremos al constructor.
Veamos ahora ya cómo construir la clase Avion. Notemos ante de ello que será necesario implementarle la interfaz Colisionable, lo que posibilitará que un objeto de Avión detecte colisiones con objetos en movimiento de diferente naturaraleza como otros aviones, disparos, rectángulos....Percatarse de la importancia y el poder que ello proporcionará al videojuego. Por supuesto, también hay que dotarle de la interfaz Movimiento. He considerado oportuno poder dimensionar la imagen a la hora de dibujarla con g.drawImage(...,anchura,altura,null). Si se observase cierta lentitud en el juego es posible que esa fuese una de las causa. En ese caso intentad sustituir esa línea por g.drawImage(imagen,locx,locy,null) dotándole, pues, de su dimensión real.
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Image;
import java.awt.Graphics;
public class Avion extends Imagen implements Colisionable,Movimiento{
protected AudioClip audio;
//CONSTRUCTOR de Avion
public Avion(Image avion,AudioClip audio, int anchura, int altura){
super(avion,anchura,altura); //llama al constructor de Imagen
this.audio=audio; /* proporciona un sonido que utilizaremos cuando deseemos.*/
suspender(); /*cuando creamos el objeto Avion, no está activo.*/
}
// nuevaPosición es fundamental para ACTUALIZAR la posición.
public void nuevaPosicion(int x, int y){
locx=x;
locy=y;
}
/* si en algún momento deseamos INCREMENTAR la ACTUALIZACION y, por tanto,
incrementar la velocidad aparente de movimiento.*/
public void nuevaVelocidad(int vx, int vy){
locx+=vx;
locy+=vy;
}
/* devuelve cierto si un objeto delimitado por el rectángulo de coordenadas
(x1,y1),(x2,y2) interseca con nuestro Avion*/
public boolean intersecta(int x1, int y1, int x2, int y2){
return visible&&(x2>=locx)
&&(locx+anchura>=x1)&&(y2>=locy)
&&(locy+altura>=y1);
}
// Si nuestro Avion ha muerto, sonará un sonido y lo desactivaremos.
public void colision(){
audio.play();
suspender();
}
}
Notemos la pecualiaridad de esta clase: le hemos dotado de sonido. El sonido proporciona credibilidad y aumenta la interacción con el usuario. No deberían faltar este tipo de cuestiones, así que nosotros no vamos a ser menos. Ahora bien, procuremos que no ocupe mucho memoria el que elijamos. Podremos utilizar extensiones .wav, .au..etc.
import java.awt.Image;
public class Disparo extends Imagen implements Movimiento,Colisionable{
/* CONSTRUCTOR. */
public Disparo(Image disparo, int anchura, int altura){
super(disparo,anchura,altura);
suspender();/*Los disparos nacerán inactivos y no visibles.
Estarán en la recámara de los aviones*/
}
public void nuevaPosicion(int x, int y){
locx=x;
locy=y;
}
public void nuevaVelocidad(int vx, int vy){
locx+=vx;
locy+=vy;
}
public boolean intersecta(int x1, int y1, int x2, int y2){
return visible&&(x2>=locx)
&&(locx+anchura>=x1)&&(y2>=locy)
&&(locy+altura>=y1);
}
public void colision (){
suspender();/*Si un disparo alcanza a un avión aquel debe desaparecer.*/
}
}
Bien, llegados a este punto ya disponemos de las clases necesarias para simular a los elementos del videojuego, como son las estrellas, los aviones y los disparos que efectúen los aviones enemigos y el protagonista. Todos disponen de métodos para ACTUALIZAR su posición y algunos métodos para detectar colisiones.
Ahora, el siguiente paso será construir las clases Controladoras que hagan nacer a todos aquellos objetos en movimiento y determinen tanto las interacciones entre ellos como sus actualizaciones.
Necesitaremos pues a:
- Clase ControladorAviones
- Clase ControladorDisparos
- Clase ControladorEstrellas
Podremos añadir, posteriormente, para dar vistosidad al juego, una clase que emule una corona circular y que represente el nivel de vida del protagonista.
Podríamos, también, incluir una presentación, y dotar al juego de un menu incluyendo el applet en una frame (ventana), para Iniciar Juego, Finalizar Juego...e incluso, como meta final, dotarle de la opción Guardar Partida, haciendo uso de una Base de Datos. Eso lo decidiremos en el/los posteriores artículos.
Eso sí, más tarde, necesitaremos construir el applet principal mediante un hilo y dotarle de métodos de eventos de teclado para que el usuario pueda interaccionar con el videojuego. Queda mucho por hacer, pero si este artículo os han interesado seguro que vuestra PASIÓN hará el resto. ;).
Recursos
[1]
Programación de juegos en Java. (Black Art of Java in Game Programming),
Joel Fan,Eric Ries, Callin Tenitchi,
Acerca del autor
Raúl Bonachía Castillo
Raúl Bonachía Castillo (Ruly). Licenciado en Matemáticas por la U.R,
y un gran apasionado por el desarrollo de juegos en java, y deseoso
por emular juegos multijugador en red de calidad en esa plataforma.
Actualmente trabaja en el desarrollo de un proyecto para la creación
de un sistema de edición de acceso universal para la empresa Educaline S.L
