4,52/5
(83 opiniones)
|12480 alumnos|Fecha publicación: 07/09/2004
Vamos a realizar una pequeña librería (y cuando digo pequeña, quiero decir realmente pequeña) para el manejo de los sprites. Luego utilizaremos esta librería en nuestro juego, por supuesto, también puedes utilizarla en tus propios juegos, así como ampliarla, ya que cubrirá sólo los aspectos básicos en lo referente a sprites.
Dotaremos a nuestra librería con capacidad para movimiento de sprites, animación (un soporte básico) y detección de colisiones.
Para almacenar el estado de los Sprites utilizaremos las siguientes variables.
private int posx,posy;
private boolean active;
private int frame,nframes;
private Image[] sprites;
Necesitamos la coordenada en pantalla del sprite (que almacenamos
en posx y posy. La variable active nos servirá para saber si el
sprite está activo. La variable frame almacena el frame actual del
sprite, y nframes el número total de frames de los que está
compuesto. Por último, tenemos un array de objetos Image que
contendrá cada uno de los frames del juego.
Como puedes observar no indicamos el tamaño del array, ya que aún no sabemos cuantos frames tendrá el sprite. Indicaremos este valor en el constructor del sprite.
// constructor. 'nframes' es el número de frames
del Sprite.
public Sprite(int nframes) {
// El Sprite no está activo por defecto.
active=false;
frame=1;
this.nframes=nframes;
sprites=new Image[nframes+1];
}
El constructor se encarga de crear tantos elementos de tipo Image
como frames tenga el sprite. También asignamos el estado inicial
del sprite.
La operación más importante de un sprite es el movimiento por la pantalla. Veamos los métodos que nos permitirán moverlo.
public void setX(int x) {
posx=x;
}
public void setY(int y) {
posy=y;
}
int getX() {
return posx;
}
int getY() {
return posy;
}
Como puedes observar, el código para
posicionar el sprite en la pantalla no puede ser más simple. Los
métodos setX() y setY() actualizan las variables de estado del
sprite (posx,posy). Los métodos getX() y getY() realizan la
operación contraria, es decir, nos devuelve la posición del sprite.
Además de la posición del sprite, nos va a interesar en
determinadas condiciones conocer el tamaño del mismo.
int getW() {
return sprites[nframes].getWidth();
}
int getH() {
return sprites[nframes].getHeight();
}
Los métodos getW() y getH() nos devuelven el ancho y el alto del
sprite en píxeles. Para ello recurrimos a los métodos getWidth() y
getHeigth() de la clase Image.
Otro dato importante del sprite es si está activo en un momento determinado.
public void on() {
active=true;
}
public void off() {
active=false;
}
public boolean isActive() {
return active;
}
Necesitaremos un método que active el sprite, al que
llamaremos on(), y otro para desactivarlo, que como podrás
imaginar, llamaremos off(). Nos resta un método para conocer el
estado del sprite. Hemos llamado al método isActive().
En lo referente al estado necesitamos algún método para el control de frames, o lo que es lo mismo, de la animación interna del sprite.
public void selFrame(int frameno) {
frame=frameno;
}
public int frames() {
return nframes;
}
public void addFrame(int frameno, String path)
{
try {
sprites[frameno]=Image.createImage(path);
} catch (IOException e) {
System.err.println("Can`t load the image " + path
+ ": " + e.toString());
}
}
El método selFrame() fija el frame actual del
sprite, mientras que el método frame() nos
devolverá el número de frames del sprite.
El método addFrame() nos permite añadir frames al sprite. Necesita dos parámetros. El parámetro frameno, indica el número de frame, mientras que el parámetro path indica el camino y el nombre del gráfico que conformará dicho frame.
Para dibujar el sprite, vamos a crear el método draw(). Lo único que hace este método es dibujar el frame actual del sprite en la pantalla.
public void draw(Graphics g) {
g.drawImage (sprites[frame], posx, posy,
Graphics.HCENTER|Graphics.VCENTER);
}
Nos resta dotar a nuestra librería con la capacidad de detectar
colisiones entre sprites. La detección de colisiones entre sprites
puede enfocarse desde varios puntos de vista. Imaginemos dos
sprites, nuestro avión y un disparo enemigo. En cada vuelta del
game loop tendremos que comprobar si el disparo ha colisionado con
nuestro avión. Podríamos considerar que dos sprites colisionan
cuando alguno de sus píxeles visibles (es decir, no transparentes)
toca con un píxel cualquiera del otro sprite. Esto es cierto al
100%, sin embargo, la única forma de hacerlo es comprobando uno por
uno los píxeles de ambos sprites. Evidentemente esto requiere un
gran tiempo de computación, y es inviable en la práctica. En
nuestra librería hemos asumido que la parte visible de nuestro
sprite coincide más o menos con las dimensiones de la superficie
que lo contiene. Si aceptamos esto, y teniendo en cuenta que una
superficie tiene forma cuadrangular, la detección de una colisión
entre dos sprites se simplifica bastante. Sólo hemos de detectar el
caso en el que dos cuadrados se solapen.
En la primera figura no existe colisión, ya que no se solapan las superficies (las superficies están representadas por el cuadrado que rodea al gráfico). La segunda figura muestra el principal problema de este método, ya que nuestra librería considerará que ha habido colisión cuando realmente no ha sido así. A pesar de este pequeño inconveniente, este método de detección de colisiones es el más rápido. Es importante que la superficie tenga el tamaño justo para albergar el gráfico. Este es el aspecto que tiene nuestro método de detección de colisiones.
boolean collide(Sprite sp) {
int w1,h1,w2,h2,x1,y1,x2,y2;
w1=getW(); // ancho del
sprite1
h1=getH(); // altura del sprite1
w2=sp.getW(); // ancho del sprite2
h2=sp.getH(); // alto del sprite2
x1=getX(); // pos. X del sprite1
y1=getY(); // pos. Y del sprite1
x2=sp.getX(); // pos. X del sprite2
y2=sp.getY(); // pos. Y del sprite2
if
(((x1+w1)>x2)&&((y1+h1)>y2)&&((x2+w2)>x1)&&((y2+h2)>y1))
{
return true;
} else {
return false;
}
}
Se trata de comprobar si el cuadrado (superficie) que contiene el primer sprite, se solapa con el cuadrado que contiene al segundo.
Hay otros métodos más precisos que nos permiten detectar
colisiones. Consiste en dividir el sprite en pequeñas superficies
rectangulares tal y como muestra la próxima figura.
Se puede observar la mayor precisión de este método. El proceso de detección consiste en comprobar si hay colisión de alguno de los cuadros del primer sprite con alguno de los cuadrados del segundo utilizando la misma comprobación que hemos utilizado en el primer método para detectar si se solapan dos rectangulos. Se deja como ejercicio al lector la implementación de este método de detección de colisiones. A continuación se muestra el listado completo de nuestra librería.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
import java.io.*;
class Sprite {
private int posx,posy;
private boolean active;
private int frame,nframes;
private Image[] sprites;
// constructor. 'nframes' es el número de frames
del Sprite.
public Sprite(int nframes) {
// El Sprite no está activo por defecto.
active=false;
frame=1;
this.nframes=nframes;
sprites=new Image[nframes+1];
}
public void setX(int x) {
posx=x;
}
public void setY(int y) {
posy=y;
}
int getX() {
return posx;
}
int getY() {
return posy;
}
int getW() {
return sprites[nframes].getWidth();
}
int getH() {
return sprites[nframes].getHeight();
}
public void on() {
active=true;
}
public void off() {
active=false;
}
public boolean isActive() {
return active;
}
public void selFrame(int frameno) {
frame=frameno;
}
public int frames() {
return nframes;
}
// Carga un archivo tipo .PNG y lo añade al sprite
en
// el frame indicado por 'frameno'
public void addFrame(int frameno, String path) {
try {
sprites[frameno]=Image.createImage(path);
} catch (IOException e) {
System.err.println("Can`t load the image " + path
+ ": " + e.toString());
}
}
boolean collide(Sprite sp) {
int w1,h1,w2,h2,x1,y1,x2,y2;
w1=getW(); // ancho del
sprite1
h1=getH(); // altura del sprite1
w2=sp.getW(); // ancho del sprite2
h2=sp.getH(); // alto del sprite2
x1=getX(); // pos. X del sprite1
y1=getY(); // pos. Y del sprite1
x2=sp.getX(); // pos. X del sprite2
y2=sp.getY(); // pos. Y del sprite2
if
(((x1+w1)>x2)&&((y1+h1)>y2)&&((x2+w2)>x1)&&((y2+h2)>y1))
{
return true;
} else {
return false;
}
}
// Dibujamos el Sprite
public void draw(Graphics g) {
g.drawImage(sprites[frame],posx,posy,Graphics.HCENTER|Graphics.VCENTER);
}
Veamos un ejemplo práctico de uso de nuestra librería. Crea un nuevo proyecto en KToolBar, y añade el programa siguiente en el directorio `src´, junto con la librería Sprite.java. Por supuesto necesitarás incluir el gráfico hero.png en el directorio `res´.
En los siguientes capítulos vamos a basarnos en esta librería para el control de los Sprites del juego que vamos a crear.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class SpriteTest extends MIDlet implements CommandListener {
private Command exitCommand, playCommand,
endCommand;
private Display display;
private SSCanvas screen;
public SpriteTest() {
display=Display.getDisplay(this);
exitCommand = new
Command("Salir",Command.SCREEN,2);
screen=new SSCanvas();
screen.addCommand(exitCommand);
screen.setCommandListener(this);
}
public void startApp() throws
MIDletStateChangeException {
display.setCurrent(screen);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}
}
class SSCanvas extends Canvas {
private Sprite miSprite=new Sprite(1);
public SSCanvas() {
// Cargamos los sprites
miSprite.addFrame(1,"/hero.png");
// Iniciamos los Sprites
miSprite.on();
}
public void paint(Graphics g) {
// Borrar pantalla
g.setColor(255,255,255);
g.fillRect(0,0,getWidth(),getHeight());
// situar y dibujar sprite
miSprite.setX(50);
miSprite.setY(50);
miSprite.draw(g);
}
}
Hay 83 opiniones. Opina sobre este curso.
| Cursos | Valoración | Alumnos | Vídeo | |
|---|---|---|---|---|
|
Funciones en C (primera parte) Curso de programacíon informática sobre Funciones en C en el orden de los fundamentos de la programación. Comprende el desarrollo de un software utilizando funciones y a ... [17/10/08] |
|
2.816 | ||
|
Procesos en C. Sincronización (primera parte) Curso de informática sobre sincronización de procesos en C y señales en linux que te ofrece la posibilidad de comprender los mecanismos de comunicación entre procesos inf... [21/10/08] |
|
993 | ||
|
Java Java nació con el deseo por parte de Sun de buscar un lenguaje de programación enfocado a electrodomésticos. Se necesitaba alg&uacu... [23/04/04] |
|
49.291 | ||
Publicar en
del.icio.us
digg
meneame