La simulación física es una de las cosas más chulas que podemos ver en los juegos actuales. En muchos juegos puedes, por ejemplo,
coger una caja, tirarla, y ésta reaccionará de forma natural a su entorno rebotando, girando, etc, tal y como haría
en la vida real (o al menos de forma similar).
En un principio parece algo difícil de implementar en un juego. No os voy a mentir, lo es, pero se pueden hacer ciertas simplificaciones, y
por supuesto la dificultad dependerá del tipo de simulación que queramos hacer. Normalmente la física está muy unida a la detección de colisiones,
pero aquí tendrá mas relevancia la física en sí.
Vamos a utilizar Processing (que es básicamente Java acompañado de un puñado de funciones gréficas, de sonido, etc.) para
crear nuestra propia simulación física basada en masas y muelles.
Lo haremos en 2D, pero en 3D es básicamente lo mismo. Podéis ver el resultado de este tutorial aquí: (156kb, necesitas JAVA para verlo.)
Abrid Processing, cread un archivo nuevo, y guardadlo con el nombre que queráis.
Nuestro sistema usará dos clases: 'mass', una masa o punto en el espacio regido a simples leyes físicas,
y 'spring', un muelle que une dos masas.
Necesitamos también una forma sencilla de representar una fuerza. Una fuerza es simplemente una cantidad
de energia aplcada en cierta dirección. La forma más facil de representar esto es mediante un vector.
Puedes visualizarlos como flechas, la magnitud de la fuerza es la longitud de la flecha,
y la dirección de la flecha la dirección de la fuerza.
class vector2D
{
public float x, y;
vector2D(float a, float b)
{
x = a;
y = b;
}
public float module()
{
return sqrt(sq(x)+sq(y));
}
public vector2D normalize()
{
float m = module();
return new vector2D(x/m,y/m);
}
}
Esta clase es muy simple. Tiene dos valores, x e y (componentes horizontal y vertical de la fuerza)
y dos métodos: module() (devuelve la cantidad de fuerza aplicada, es decir la longitud del vector) y
normalize() (devuelve un vector con la misma direccioón pero longitud 1).
Ahora crearemos la clase "masa":
class mass
{
public vector2D position, force;
public float airFriction,floorFriction;
private boolean selected;
Tiene dos vectores: uno representa la posicion de la masa, el otro representa la fuerza
aplicada a ella. Por ahora ignorad los demás atributos.
Escribimos una constructora:
mass(float px, float py)
{
position = new vector2D(px,py);
force = new vector2D(0,0);
airFriction = 0.98;
floorFriction = 0.9;
selected = false;
}
Ignorando de nuevo los 3 atributos que no conocemos.
Necesitaremos dibujar la masa para poder verla, así que escribimos un sencillo método para dibujar un punto donde
esté la masa:
public void draw_mass()
{
ellipse(position.x, position.y, 5, 5);
}
Empezemos con la física. Añadiremos la fuerza a la posición en cada paso, de esta froma la masa se moverá de acuerdo a la fuerza aplicada sobre ella.
public void updatePos()
{
position.x += force.x;
position.y += force.y;
}
Ahora necesitamos actualizar la posicion y dibujar la masa en cada paso. 'selected' nos indica si la masa
está o no seleccionada por el ratón, en caso afirmativo haremos que la posición de la masa sea la posición del ratón.
Si no, aplicamos gravedad (un poco de fuerza vertical hacia abajo)
y frenamos un poco nuestro movimiento de acuerdo a la cantidad de fricción con el aire (airFriction).
Una vez hecho todo eso, dibujamos la masa.
public void step()
{
if (!selected)
{
//apply gravity and friction;
force.y += 0.4;
force.x *= airFriction;
force.y *= airFriction;
updatePos();
}
else
{
position.x = mouseX;
position.y = mouseY;
}
draw_mass();
}
Eso es. Hasta ahora lo único que hemos conseguido es una masa que cae y desaparece por la parte de abajo de la pantalla.
Es evidente que necesitamos un suelo. Si la masa baja más de 350 pixeles desde la parte superior de la pantalla, frenaremos un poco su fuerza
usando floorFriction, e invertiremos su fuerza vertical para que rebote.
Ahora el código será así:
public void step()
{
if (!selected)
{
if (position.y > 350){
position.y = 350;
force.x *= floorFriction;
if (force.y > 0)
force.y = -force.y*floorFriction;
}
//apply gravity and friction;
force.y += 0.4;
force.x *= airFriction;
force.y *= airFriction;
updatePos();
}
if (selected)
{
position.x = mouseX;
position.y = mouseY;
}
draw_mass();
}
Una vez hecho todo esto, probamos nuestro código:
mass m1; //declare a new mass.
void setup()
{
size(400,400); //create a 400x400 window.
smooth(); //draw smoothly.
stroke(20); //stroke color.
fill(30); //fill color.
background(200,200,200); //backgorund color.
m1 = new mass(100,40); //create a new mass.
}
void draw()
{
background(200,200,200); //clear the screen.
m1.step(); //simulate the mass´ physics.
line(0,350,400,350); //draw the floor.
}
Ejecución! Deberíais ver una pequeña pelota cayendo y rebotando contra el suelo.
No es gran cosa, ahora debemos hacer que se la pueda coger y tirar por ahí, así que añadiremos algunos métodos a la clase "masa":
CONTINUAR A LA PARTE 2: Control con ratón e implementación de muelles.