Simulación de Ecosistemas
En este apartado se verá como construir agentes independientes que simulen el comportamientos de organismos. Aquí sólo trataremos el tema del movimiento y nuetra finaidad es lograr tener organismos virtuales que sean capaces de manejarse en forma autónoma y que expongan cierta naturalidad en su movimiento. también veremos como implementar el espacio de la escena de la mejor forma para aprovechar los recursos del sistema.
Organismos y autonomía
Ejemplo Vida 00 Como ya se ha dicho, aquí trataremos la creación de varios organismos independientes unos de otros. La autonomía de estos organismos se logra a través de la creación de una clase Organismo, la cual permite crear las variables internes necesarias para tener registro de la evolución de cada individuo por separado. A la hora de programar necesitamos que tratamientos homogéneos a la hora del diseño, manifiesten autonomía e independencia a la hora de la ejecución. esto se logra creando un arreglo de objetos de tipo Organismo. Así desde el código principal del programa se les pide a cada organismo las mismas acciones, pero cada organismo responde a estas ordenes según su propia historia.
A continuación podemos ver el código de la clase ogranismo:
class Organismo{ float x, y; //posicion del organismo //---------------------------- Organismo( float x_ , float y_ ){ //inicializa el organismo iniciar( x_ , y_ ); } //---------------------------- Organismo( ){ //inicializa el organismo iniciar( random(width) , random(height) ); //si no recibe parametros inicia con x e y al azar } //---------------------------- void iniciar( float x_ , float y_ ){ //inicialización del organismo x = x_; y = y_; } //---------------------------- void dibujar(){ //dibuja el organismo rectMode(CENTER); rect(x,y,10,10); } }Hecho en Processing
En esta primera implementación de la clase Organismo, un organismo posee las variables necesarias para ubicarlas en un espacio bidimensional (x e y). Por el momento lo único que pueden hacer nuestro organismos es ocupar un lugar en el espacio. El código que ejecuta las acciones se muestra a continuación:
Organismo[] animales; //Un arreglo de animales int cantAnimales; //Define la cantidad de animales void setup(){ ... cantAnimales = 20; //Se establece la cantidad de animales iniciar(); //ejecuta la inicialización ... } void draw(){ ... for(int i=0;i$lt;cantAnimales;i++){ //se recorre cada animal y animales[i].dibujar(); //dibuja cada animal } ... } ... void iniciar(){ animales = new Organismo[cantAnimales]; //Se inicia el arreglo for(int i=0;i$lt;cantAnimales;i++){ //se recorre cada animal animales[i] = new Organismo(); // se inicia cada animal } }Hecho en Processing
Los puntos suspensivos indican que existe código que no está siendo mostrado.
Como muestra el ejemplo anterior: 1) la cantidad de organismos es de 20, que es el valor de la variable cantAnimale. 2) Dos ciclos for son los encargados de ordenar los comportamientos de los organismos, por ejemplo, cuando se les envía a inicializarse con el constructor de la clase ( animales[ i ] = new Organismo( ) ), o cuando se les pide que se dibujen ( animales[ i ].dibujar( ) ).
Como dijimos anteriormente, las ordenes generales tiene un tratamiento homogéneo, por ejemplo se les pide a todos los organismos, que se dibujen; pero la forma en que cada organismo responde a esta acción depende de su propia historia. En este caso la historia está dado por la inicialización, que determina una posición al azar:
... Organismo( ){ //inicializa el organismo iniciar( random(width) , random(height) );//si no recibe parametros inicia con x e y al azar } void iniciar( float x_ , float y_ ){ //inicialización del organismo x = x_; y = y_; } ...Hecho en Processing
Movimiento y desplazamiento
Ejemplo Vida 01 Una vez que hemos ubicado nuestro organismos en diferentes posiciones, es momento de que cada organimo se mueva. la forma de implementar este movimiento, es declarando dos variable dx y dy que representan los desplazamentos en x e y, respectivamente. Así se les asigna valor al azar al inicio, para luego ser usadas en la acción mover:
class Organismo{ ... float dx,dy; //desplazamiento en x e y ... void iniciar( float x_ , float y_ ){ //inicialización del organismo x = x_; y = y_; dx = random(-10,10); dy = random(-10,10); } void mover(){ //actualiza la ubicación del organismo x += dx; //aplica los desplazamiento y += dy; //aplica los desplazamiento } ... }Hecho en Processing
El problema que surge en este ejempo es que la velocidad varía según la conjunción de los valores de dx y dy. Además, los valores de estos desplazamiento, nos dificulta prever la dirección exacta en la que avanzará un organismo. Seria deseable poder controlar la velocidad independientemente de la dirección, y viceversa.
Coordenadas polares y rectangulares
Ejemplo Vida 02 Cuando hablamos de movimiento en informática tenemos que hablar de la representación del movimiento y por ende tenemos que hablar del sistema de coordenadas que se utiliza. El sistema de Coordenadas Cartesianas (ver en Wikipedia), también llamado Coordenadas Rectangulares, permite utilizar las distancia con respecto a dos ejes (X e Y) para definir un punto en el espacio bidimensional.
Este sistema de coordenadas es muy útil para representar posiciones. Nos resulta intuitivo trabajar con este sistema sin embargo no nos es tan útil a la hora de pensar movimientos, dado que en general, a la hora de pensar el movimiento, pensamos en dirección y velocidad. Afortunadamente existe un tipo de cordenadas que pueden representar este tipo de variables, son las Coordenadas Polares (ver en Wikipedia). En este sistema, un punto en el espacio se representa como la distancia respceto a un punto de origen, medida como un ángulo y una distancia .
Debido a que la computadora domina un sistema de Coordenadas Rectangulares, pero a nuestro fin, nos es más útil el sistema de Coordenadas Polares, es necesario contar con un sistema de transformación de un sistema a otro. Las ecuaciones que permiten dicha transformación son las siguientes:
x = distancia * cos( angulo ); y = distancia * sin( angulo );Hecho en Processing
En el ejemplo Vida 02, el ángulo está representado por la dirección y la distancia por la velocidad. En vez de x e y se emplean dx y dy, dado que en este caso, las primeras representan la posición mientras que dx y dy los respectivos desplazamientos en cada uno de estos ejes. Así en el ejemplo la operación sería:
dx = velocidad * cos(direccion); dy = velocidad * sin(direccion); x += dx; y += dy;Hecho en Processing
De esta forma, se agregaron en la clase organismo las variables y operaciones necesarios para trabajar con velocidad y dirección:
class Organismo{ ... float x, y; //posicion del organismo float direccion; //dirección en la que avanza float velocidad; //velocidad a la que avanza float dx,dy; //desplazamiento en x e y deducido de la dirección y velocidad. ... void iniciar( float x_ , float y_ ){ //inicialización del organismo x = x_; y = y_; direccion = random(TWO_PI); //inicia con una dirección al azar velocidad = 5; //inicia la velocidad en 5 pixels por fotograma } void mover(){ //actualiza la ubicación del organismo dx = velocidad * cos(direccion); //deduce el desplazamiento en X dy = velocidad * sin(direccion); //deduce el desplazamiento en Y x += dx; //aplica los desplazamiento y += dy; //aplica los desplazamiento } ... }Hecho en Processing
Variación azarosa de dirección
Ejemplo Vida 03 En este ejemplo se ha agregado un función que permite hacer una variación angular de la dirección. Es decir, en cada paso se varía levemente la dirección para que la trayectoria de nuetros organismo sea más natural. Esto se hace tirando un número al azar (en realidad un número pseudo-aleatorio) y sumando ese valor a la dirección:
class Organismo{ ... void variarAngulo( float amplitud ){ //varia la dirección con una amplitud determinada float radi = radians( amplitud ); //transforma los grados en radianes direccion += random( -radi , radi ); //aplica un valor al azar dentro del rango } ... void mover(){ //actualiza la ubicación del organismo variarAngulo( 30 ); //caria la direccion en un rango de 30 grados para cada lado dx = velocidad * cos(direccion); //deduce el desplazamiento en X dy = velocidad * sin(direccion); //deduce el desplazamiento en Y x += dx; //aplica los desplazamiento y += dy; //aplica los desplazamiento } ... }Hecho en Processing
Es importante destacar que los ángulos en informática se miden en radianes (ver en Wikipedia), sin embargo a nosostros nos resulta más sencillo manejarnos con grados. Debido a eso la función void variarAngulo( float amplitud) recibe el parámetro amplitud expresado en grados, pero interamente convierte este ángulo en radianes con la función de Processing radians( ).
Para aplicar la función variarAngulo se la invoca desde dentro de la acción mover, pasándole como parámetro el valor 30 (en este caso), lo que significa que la dirección puede variar hasta 30 grados en ambos sentidos (horario y antihorario).
Espacio toroidal
Ejemplo Vida 04 En este ejemplo se implementó un espacio toroidal, esto es un espacio continuo en el sentido vertical y horizontal. este término viene de la curva llamada toroide (ver en Wikipedia). Sencillamente, esto significa que cuando algo sale de la escena por el borde derecho, entonces reaparece por el izquierdo, lo mismo sucede en sentido contrario, es decir que cuando sale por la izquierda vuele a ingresar por la derecha, y así con los bordes superior e inferior. De esta forma la escena no tiene límite (o mejor dicho sus límites se tocan), como cuando un circunda una esfera (como nuestro planeta).
La forma en que se realiza esto revisando si los organismos se salen de los bordes, y entonces haciéndolos reingresar por el borde opuesto:
class Organismo{ ... void mover(){ //actualiza la ubicación del organismo variarAngulo( 30 ); //caria la dirección en un rango // de 30 grados para cada lado dx = velocidad * cos(direccion); //deduce el desplazamiento en X dy = velocidad * sin(direccion); //deduce el desplazamiento en Y x += dx; //aplica los desplazamiento y += dy; //aplica los desplazamiento if( toroidal ){ //si el espacio es toroidal // entonces revisa si se pasó de límite x = ( x>width ? x-width : x ); x = ( x<0 ? x+width : x ); y = ( y>height ? y-height : y ); y = ( y<0 ? y+height : y ); } } ... }Hecho en Processing
En el código anterior se utiliza una variable booleana para poder configurar el espacio como toroidal o no.