Portada Blogs Álbumes Notas Herramientas Usuarios Ayuda
Blog de BLei couRT (cambiar): Página Principal Entradas Historial Estadísticas
Bienvenid@ a LoG85. Puedes registrarte o logearte.
Estás viendo la página 3 de BLei couRT, un blog de LaNsHoR. Escrita el 14/04/2009 a las 22:59:35.
Integrando C++ con LUA
Foto de la página 3 del blog de blei_court
Conforme pasó el tiempo, los juegos dejaron de ser esos pequeños programas triviales para convertirse, poco a poco, en el colmo de la complejidad computacional.

Un juego abarca casi todos los ámbitos informáticos y los lleva hasta los límites del tiempo real contemporáneo. Gráficos, Inteligencia Artificial, Redes, Sonido, Sintetizadores, Sistemas de Archivos Virtuales, Recursos, Arquitecturas, Generación Procedural, Ingeniosos Patrones de Diseño...

A mediados de los 90 la complejidad había crecido hasta el límite en el que poderosos motores quedaban desaprovechados por las limitaciones de las configuraciones que admitían. A pesar de ser bastante dinámicos y abiertos (para esa época) sólo se permitían configuraciones sencillas que fueran rápidas de procesar. Cualquier cambio mayor requería compilar el juego entero, algo que entonces suponía varios días y dificultaba extraordinariamente la creación de parches, por lo que las nuevas modificaciones eran difíciles de distribuir.

Así surgió la necesidad de incorporar motores de script a los videojuegos, haciendo que parte del código del juego fuese soportado en scripts de texto plano que se podían cambiar en cualquier momento sin necesidad de compilar. Muchos juegos tuvieron su propio lenguaje de script, a menudo muy limitado, pero que permitía con mayor o menor suerte lograr un entorno dinámico y configurable. Estos pioneros siguieron la evolución natural que sus motores pedían a gritos.

Por fortuna, en 1993 nació LUA, un lenguaje "semi"-interpretado que destacaba por su velocidad y su capacidad de extensión. No era puramente interpretado puesto que los scrips se podían compilar (al vuelo o no) a bytecode, usando una máquina virtual para su ejecución. Su flexibilidad, su licencia, y su genial integración con C hicieron que desde entonces miles de juegos lo adoptasen en masa.

LUA fue usado en toda la saga de Baldur's Gate, por ejemplo, y también es la base del motor de interfaz de World Of Warcraft. Heroes V, Crysis, Mods del Half-Life 2... etc, está avalado por muchos de los más grandes.

(***)

En mi opinión, la pega más importante de LUA es su integración con C++ (que no con C). Hacer los "bindings" entre clases LUA/C++ es un coñazo a menudo insoportable para la gente que, como yo, no quiere perder el tiempo escribiendo lo mismo 2 veces. Sin embargo, hace algunos años, cuando tenía en mente el desarrollo del motor para el juego de rol de-fi-ni-ti-vo, encontré una ingeniosa solución a este engorroso tema; y de eso voy a hablar hoy.

La solución implica renunciar al orientado a objetos en LUA, pero podremos usar las clases de C++ mediante funciones al estilo C en LUA. Por ejemplo, si tenemos una clase "Personaje" en C++, que tiene un método "Envenenar()", en LUA podremos hacer algo cómo:

Envenenar(personaje);

Es decir, renunciamos a la sintaxis de OO, a la herencia y a la encapsulación, pero podremos trabajar con todos los métodos originales de clases escritas en C++. Dado que los scrips en los juegos suelen ser muchos pequeños paquetes (recalco lo de pequeños), es bastante cómodo trabajar así y crear nuestros propios comandos KISS.

(***)

La idea:

La idea es que todas los objetos C++ tengan un identificador númerico único. En C++ crearemos los comandos que recibirán un número que será el ID del objeto con el que trabajaremos (además de otros argumentos necesarios para el comando en cuestión).

Por ejemplo, veamos el siguiente código en LUA:

personaje1=CrearPersonaje("LaNsHoR",100);

El método CrearPersonaje("LaNsHoR",100) presuntamente crea un objeto Personaje de C++ llamado "LaNsHoR" y que tiene 100 puntos de vida. La línea anterior da a entender que un objeto Personaje análogo en LUA se ha guardado en la variable "personaje1", pero realmente sólo se ha guardado un entero: el identificador único del objeto real que sólo existe en C++.

Es decir, en el motor en C++ existe un personaje llamado LaNshoR con 100 de vida que se ha creado con el comando "CrearPersonaje", y que además tiene, por ejemplo, un identificador numérico único (algo así como el DNI) y exclusivo que podría ser, por ejemplo "352".

El comando "CrearPersonaje" devuelve el ID del objeto que acaba de crear, así que la variable "personaje1" en LUA sólo contiene el número "352".

Esa es la idea, ahora escribiremos en C++ otro comando, por ejemplo "Envenenar()", que ha de recibir el personaje al cual queremos envenenar. Como desde LUA sólo tenemos los IDs de los objetos, realmente la función Envenenar recibirá un ID, y el motor, ya desde C++, buscará el objeto con ese ID y lo envenenará.

Obviamente, no vamos a ir preguntando uno por uno a todos los objetos que tengamos cual es su "DNI" y comparándolo para ver si ese es el objeto que buscamos. En un juego pueden haber 1000 monstruos, 100000 objetos, 200 casas, 50000 muebles... y buscar uno por uno sería bastante lento (y estúpido). Así que para evitar esto diseñamos una nueva estructura/patrón de diseño que yo llamo "el mega vector".

(***)

Para que todos los objetos tengan un ID único crearemos una clase "Identidad" de la que todas las demás heredarán. Esta clase simplemente asigna un atributo ID a cada objeto y va incrementando un contador estático para el siguiente:

  1. #ifndef __identidad__
  2. #define __identidad__
  3. class Identidad
  4. {
  5. private:
  6.   static unsigned int _contador;
  7.   unsigned int _id;
  8. public:
  9.   Identidad();
  10.   ~Identidad();
  11.   unsigned int getID();
  12. };
  13. unsigned int Identidad::_contador=0;
  14. Identidad::Identidad()
  15. {
  16.   _id=_contador;
  17.   _contador++;
  18. }
  19. Identidad::~Identidad() {}
  20. unsigned int Identidad::getID()
  21. {
  22.   return _id;
  23. }
  24. #endif // __identidad__

Con esto hemos conseguido 2 cosas: la primera, todos los objetos tendrán una ID automática sin que tengamos que hacer nada, y la segunda, todos los objetos tendrán una interfaz común como hijos de Identidad, de esta forma podremos aprovechar el polimorfismo para crear la siguiente clase clave: el megavector.

El megavector es un array de punteros a Identidad que contendrá todos los objetos, cada objeto estará en la posición de memoria del array que indique su ID, de esta forma podemos acceder a él directamente sin tener que buscarlo; sólo con un salto a memoria.

El nombre de "megavector" viene porque tendremos que reservar suficiente memoria como para contener todos los posibles objetos que puedan haber simultáneamente en la partida, si usamos por ejemplo medio millón de posiciones necesitaríamos unos 2 megas de ram (32*500000/(8*1024*1024)) sólo en punteros (el doble en sistemas de 64bits), lo cual, no es mucho si tenemos en cuenta la velocidad de acceso que ganamos, y la simplicidad de "hablar" con C++ desde LUA.

Por supuesto podemos hacer dinamismos para ampliar el vector si nos quedamos sin memoria, pero en este ejemplo simple no voy a tratar de hacer eso. Además, otro aspecto importante es la fragmentación: si estamos creando y destruyendo objetos de forma continua iremos avanzando en el contador del megavector dejando atrás casillas vacías. Para solucionar esto simplemente tendremos que tener otro vector de enteros del mismo tamaño en el que iremos guardando las posiciones libres, y con un par de marcadores controlaremos la asignación de IDs.

Un megavector básico y simple, a modo de ejemplo, que no controla la fragmentación ni es ampliable sería el siguiente:

  1. #ifndef __megavector__
  2. #define __megavector__
  3. #include "identidad.h"
  4. #include
  5. class MegaVector
  6. {
  7. private:
  8.   int _ocupados;
  9.   int _maximo;
  10.   Identidad** _vector;
  11.   MegaVector(int maximo=500);
  12.   static MegaVector* _instancia;
  13. public:
  14.   ~MegaVector();
  15.   Identidad* getElemento(int);
  16.   void Insertar(Identidad*);
  17.   static MegaVector* getInstancia();
  18. };
  19. MegaVector* MegaVector::_instancia=0x0;
  20. MegaVector::MegaVector(int maximo)
  21. {
  22.   _maximo=maximo;
  23.   _ocupados=0;
  24.   _vector=new Identidad*[_maximo];
  25.   for(int x=0;x<_maximo;x++)
  26.       _vector[x]=0x0;
  27. }
  28. MegaVector::~MegaVector()
  29. {
  30.   for(int x=0;x<_ocupados;x++)
  31.   {
  32.       if(_vector[x]!=0x0)
  33.         delete _vector[x];
  34.       _vector[x]=0x0;
  35.   }
  36.   delete[] _vector;
  37.   _vector=0x0;
  38.   _instancia=0x0;
  39. }
  40. Identidad* MegaVector::getElemento(int x)
  41. {
  42.   if(x>=_ocupados || x<0)
  43.       {
  44.       std::cerr<<"Error: No puedo acceder a "<endl;
  45.       return 0x0;
  46.       }
  47.   return _vector[x];
  48. }
  49. void MegaVector::Insertar(Identidad* elemento)
  50. {
  51.   if(_ocupados>=_maximo)
  52.   {
  53.       std::cerr<<"Error: No caben más elementos"<endl;
  54.       return;
  55.   }
  56.   _vector[_ocupados]=elemento;
  57.   _ocupados++;
  58. }
  59. MegaVector* MegaVector::getInstancia()
  60. {
  61.   if(!_instancia)
  62.       _instancia=new MegaVector();
  63.   return _instancia;
  64. }
  65. #endif // __megavector__

Perfecto, ahora hagamos un juego de ejemplo.

En muchos juegos cosas como los hechizos, o el propio combate esta 100% programado y controlado desde script. A modo de ejemplo voy a crear una clase "Personaje" y una clase "Arma" en C++ y luego escribiré una función en LUA para el combate; haré que los personajes luchen entre ellos con distintas armas en plan duelo, cada arma tendrá hará una cantidad determinada daño y todos los personajes y todas las armas las crearemos dinámicamente desde LUA.

Por supuesto como en todo juego que se precie, el azar debe jugar un papel importante, así que el daño de las armas estará definido por una tirada de un dado de X caras (sí ya sé, en la vida real no vamos a encontrar dados de, por ejemplo, 5 caras, pero en nuestro juego podremos tenerlos si queremos).

Creamos la clase Dado:

  1. #ifndef __dado__
  2. #define __dado__
  3. #include
  4. #include
  5. #include "identidad.h"
  6. #include "megavector.h"
  7. class Dado:public Identidad
  8. {
  9. private:
  10.   int _caras;
  11. public:
  12.   Dado(int caras=6);
  13.   ~Dado();
  14.   int getTirada();
  15. };
  16. Dado::Dado(int caras)
  17. {
  18.   _caras=caras;
  19.   MegaVector::getInstancia()->Insertar(this);
  20. }
  21. Dado::~Dado() {}
  22. int Dado::getTirada()
  23. {
  24.   return (rand()%_caras)+1;
  25. }
  26. #endif // __dado__

Fijaos en que la clase Dado, hereda de entidad. En el constructor de Dado hago que todos los dados creados "se registren" a si mismos en el megavector, no obstante hubiera sido más correcto hacer esta operación en el constructor de Identidad (así no tendríamos que hacerlo en todos los hijos de Identidad), pero no quería poneros nada del megavector hasta explicarlo, así que por esta vez... lo haremos así.

Ahora crearemos la clase Arma:

  1. #ifndef __arma__
  2. #define __arma__
  3. #include "identidad.h"
  4. #include "megavector.h"
  5. #include "dado.h"
  6. #include
  7. using namespace std;
  8. class Arma:public Identidad
  9. {
  10. private:
  11.   string _nombre;
  12.   Dado* _dado;
  13. public:
  14.   Arma(string,Dado* dado=0x0);
  15.   ~Arma();
  16.   string getNombre();
  17.   int Usar();
  18. };
  19. Arma::Arma(string nombre,Dado* dado)
  20. {
  21.   _nombre=nombre;
  22.   if(!dado)
  23.       dado=new Dado(6);
  24.   _dado=dado;
  25.   MegaVector::getInstancia()->Insertar(this);
  26. }
  27. Arma::~Arma() {}
  28. string Arma::getNombre()
  29. {
  30.   return _nombre;
  31. }
  32. int Arma::Usar()
  33. {
  34.   return _dado->getTirada();
  35. }
  36. #endif // __arma__

Como podemos ver en el constructor, para crear un arma necesitamos 2 cosas: un nombre, por ejemplo "Espada Larga", y un dado para hacer las tiradas. Un arma a la que le asignemos un dado de 10 caras podrá hacer de 1 a 10 puntos de daño, etc. El método "Usar" lanza el dado del arma y nos devuelve el resultado.

Y por último, creamos la clase Personaje:

  1. #ifndef __personaje__
  2. #define __personaje__
  3. #include
  4. #include "identidad.h"
  5. #include "megavector.h"
  6. #include "arma.h"
  7. using namespace std;
  8. class Personaje:public Identidad
  9. {
  10. private:
  11.   string _nombre;
  12.   Arma* _arma;
  13.   int _vida;
  14. public:
  15.   Personaje(string,int vida=100);
  16.   ~Personaje();
  17.   void Equipar(int);
  18.   int addVida(int);
  19.   string getNombre();
  20.   Arma* getArma();
  21. };
  22. Personaje::Personaje(string nombre,int vida)
  23. {
  24.   _nombre=nombre;
  25.   _arma=0x0;
  26.   _vida=vida;
  27.   MegaVector::getInstancia()->Insertar(this)
  28. }
  29. Personaje::~Personaje() {}
  30. void Personaje::Equipar(int x)
  31. {
  32.   _arma=(Arma*)MegaVector::getInstancia()->getElemento(x);
  33. }
  34. int Personaje::addVida(int x)
  35. {
  36.   _vida+=x;
  37.   return _vida;
  38. }
  39. string Personaje::getNombre()
  40. {
  41.   return _nombre;
  42. }
  43. Arma* Personaje::getArma()
  44. {
  45.   return _arma;
  46. }
  47. #endif // __personaje__

La explicación de la clase personaje es simple. Para crear un personaje debemos indicar un nombre y una cantidad de vida inicial. Después tendremos varios métodos, como "Equipar": al que le pasamos un ID de un arma para que, metafóricamente, el personaje la tome y la use para atacar a sus enemigos.

Otro método interesante es "addVida", que recive una cantidad de vida para añadir (o quitar si la cantidad es negativa) y se la resta al personaje, además devuelve la vida final tras hacer la operación. De esta forma, si queremos consultar la vida del personaje podemos hacer "addVida(0)".

Y por fin tenemos todas las clases de nuestro juego! Ahora hagamos los comandos que queremos que estén disponibles desde LUA, los he juntado todos de forma "guarra" en el main para que se vea más claro.

  1. #include
  2. #include
  3. #include
  4. #include "identidad.h"
  5. #include "megavector.h"
  6. #include "personaje.h"
  7. #include "arma.h"
  8. extern "C"
  9. {
  10.   #include "lua/lua.h"
  11.   #include "lua/lualib.h"
  12.   #include "lua/lauxlib.h"
  13. }
  14. using namespace std;
  15. //Comandos LUA
  16. int CrearDado(lua_State *L)
  17. {
  18.   Dado* dado=new Dado(lua_tointeger(L,1));
  19.   lua_pushnumber(L,dado->getID()); //Devuelve el ID del dado
  20.   return 1; //Devuelve el número de valores devueltos
  21. }
  22. int CrearArma(lua_State *L)
  23. {
  24.   Dado* dado=(Dado*) MegaVector::getInstancia()->getElemento(lua_tointeger(L,2));
  25.   Arma* arma=new Arma(lua_tostring(L,1),dado);
  26.   lua_pushnumber(L,arma->getID()); //Devuelve el ID del arma creada
  27.   return 1; //Devuelve el número de valores devueltos
  28. }
  29. int CrearPersonaje(lua_State *L)
  30. {
  31.   Personaje* personaje=new Personaje(lua_tostring(L,1),lua_tointeger(L,2));
  32.   lua_pushnumber(L,personaje->getID()); //Devuelve el ID del personaje creado
  33.   return 1; //Devuelve el número de valores devueltos
  34. }
  35. int Equipar(lua_State *L)
  36. {
  37.   Personaje* personaje=(Personaje*)MegaVector::getInstancia()->getElemento(lua_tointeger(L,1));
  38.   personaje->Equipar(lua_tointeger(L,2));
  39.   return 0; //Devuelve el número de valores devueltos
  40. }
  41. int Vida(lua_State *L)
  42. {
  43.   Personaje* personaje=(Personaje*)MegaVector::getInstancia()->getElemento(lua_tointeger(L,1));
  44.   int vida=lua_tointeger(L,2);
  45.   lua_pushnumber(L,personaje->addVida(vida));
  46.   return 1; //Devuelve el número de valores devueltos
  47. }
  48. int Tirada(lua_State *L)
  49. {
  50.   Dado* dado=(Dado*)MegaVector::getInstancia()->getElemento(lua_tointeger(L,1));
  51.   lua_pushnumber(L,dado->getTirada());
  52.   return 1; //Devuelve el número de valores devueltos
  53. }
  54. int UsarArma(lua_State *L)
  55. {
  56.   Arma* arma=(Arma*)MegaVector::getInstancia()->getElemento(lua_tointeger(L,1));
  57.   lua_pushnumber(L,arma->Usar());
  58.   return 1; //Devuelve el número de valores devueltos
  59. }
  60. int getArma(lua_State *L)
  61. {
  62.   Personaje* personaje=(Personaje*)MegaVector::getInstancia()->getElemento(lua_tointeger(L,1));
  63.   Arma* arma=personaje->getArma();
  64.   lua_pushnumber(L,arma->getID());
  65.   return 1; //Devuelve el número de valores devueltos
  66. }
  67. int getNombre(lua_State *L)
  68. {
  69.   Personaje* personaje=(Personaje*)MegaVector::getInstancia()->getElemento(lua_tointeger(L,1));
  70.   string nombre=personaje->getNombre();
  71.   lua_pushstring(L,nombre.c_str());
  72.   return 1; //Devuelve el número de valores devueltos
  73. }
  74. //Main
  75. int main(int argc, char **argv)
  76. {
  77.   srand(time(0x0));
  78.   const char file[]="script.txt";
  79.   lua_State *L=lua_open();
  80.   luaL_openlibs(L);
  81.   lua_register(L,"CrearDado",CrearDado);
  82.   lua_register(L,"CrearArma",CrearArma);
  83.   lua_register(L,"CrearPersonaje",CrearPersonaje);
  84.   lua_register(L,"Equipar",Equipar);
  85.   lua_register(L,"Vida",Vida);
  86.   lua_register(L,"Tirada",Tirada);
  87.   lua_register(L,"UsarArma",UsarArma);
  88.   lua_register(L,"Arma",getArma);
  89.   lua_register(L,"Nombre",getNombre);
  90.   int s=luaL_loadfile(L,file);
  91.   if(s==0)
  92.   {
  93.       //Ejecución del archivo
  94.       s = lua_pcall(L, 0, LUA_MULTRET, 0);
  95.   }
  96.   lua_close(L);
  97.   cout<"Pulsa ENTER para salir"<
  98.   cin.get();
  99.   return 0;
  100. }

Este programa, ejecutará el archivo "script.txt" con código LUA, desde el que tendremos disponibles lo siguientes comandos:
  • CrearDado(x): Donde x es el número de caras.

  • CrearArma(nombre,dado): Donde nombre es el nombre del arma, y dado el dado que se usará para calcular el daño.

  • CrearPersonaje(nombre,vida): Donde nombre es el nombre del personaje y vida la cantidad de vida inicial.

  • Equipar(personaje,arma): Equipar al personaje con el arma indicada.

  • Vida(personaje,x): Añade una cantidad X de vida al personaje, y devuelve la vida restante.

  • UsarArma(personaje): Hace que el personaje use el arma y devuelve cuanto daño a quitado en esa tirada.

  • Arma(personaje): Devuelve el arma del personaje.

  • Nombre(personaje): Devuelve el nombre del personaje.
Una vez compilado tendremos nuestro .exe que buscará un archivo de texto "script.txt" y lo ejecutará. En ese archivo que podremos modificar siempre que queramos sin tener que volver a generar el binario ejecutable, crearemos una función de combate con algunos personajes y sus armas, ¡todo usando las clases de C++ desde LUA! ^^.

Este es el archivo script.txt que he escrito con el combate.

El combate funciona de la siguiente manera: por turnos, los personajes usan sus armas para atacarse alternativamente, el primero que llega a 0 puntos de vida (o menos) muere y pierde. Además antes de atacar, cada personaje tira un dado de 20, si saca 20 hace un golpe crítico y el personaje hace el doble de daño, si saca 1 hace un fallo crítico y ese turno no hace nada de daño, y si saca cualquier otra cosa hace el daño normal del arma.

En el archivo creo varios personajes y varias armas para que podáis cambiar el código con cuidado y hacer pruebas. Si lo ejecuta tal y como está... este es el resultado:



Por supuesto el resultado varía en cada ejecución. Podéis editar con cuidado los personajes, las armas, la cantidad de vida, etc del .txt y probar vosotros mismos.

Aquí tenéis el ejecutable con el script.

NOTA: Este programa básico no comprueba errores en la sintaxis o en la ejecución de LUA ni nada parecido. Si editáis algo mal en el archivo de texto el programa no funcionará :P (no uséis acentos, eñes, etc, sólo caracteres de la "a" a la "z" y de "0" a "9"; espacios, guiones bajos, puntos, etc también están permitidos).

PD: He de decir a favor de Gex, que quien ataca primero tiene ventaja ;)
Archivado en: C , LUA, Spanish.
Entrada 2 de 2
Esta página no estaba prevista. Simplemente en vez de estudiar... para variar, estaba haciendo estas pruebas con LUA y he sentido la necesidad de contarlo y explicarlo.

No espero que entendáis mucho, pero quizás el "juego" pueda seros divertido o al menos, quizás seáis capaces de haceros una idea aproximada del tema. Con el aumento de potencia de los ordenadores cada vez más cosas son interpretadas o pseudo-interpretadas (por supuesto los binarios estáticos son mil veces más rápidos, si no, todo sería en lenguajes de este estilo), y la importancia de estos lenguajes crece cada día.

Con LUA podemos combinar lo mejor de C++ con lo mejor de los lenguajes "de máquina virtual", dejando para C++ las estructuras de datos y las partes de código críticas que requieran mucha velocidad, en LUA escribiremos los algoritmos que sean susceptibles de recibir cambios en el tiempo, y de esta forma... estaremos un poco más cerca de haber diseñado el sistema perfecto.
Archivado en: Others, Spanish.
6 PostsPrimogénita: granaína
** ** ** ** -- Cargando...
Post 1 de 6 // 17/04/2009 a las 02:41:29 Divertido x 24.3c.
granaína
Dama De Las Tinieblas
Avatar de granaina
****--
16.00 culombios
71328 p.d.exp.
Un eón
Doncella
cuando me consigo una gramática inglesa vas tú y escribes en español... me voy a la cama de la decepción!
la poesía no es de quien la escribe, sino de quien la necesita...
Post 2 de 6 // 17/04/2009 a las 14:57:37 Divertido x 86.22c.
LaNsHoR
Eviscerador Perpetuo
Avatar de lanshor
*****-
43.59 culombios
439761 p.d.exp.
-
Caballero
cLicK
xDDDDD Total, se entiende igual o menos...
For the good of all of us (except the ones who are dead).
Post 3 de 6 // 18/04/2009 a las 17:01:27 24.3c.
granaína
Dama De Las Tinieblas
Avatar de granaina
****--
16.00 culombios
71328 p.d.exp.
Un eón
Doncella
y q lo digas, me qde en la segunda tanda de asteriscos xD xo sq no eran horas como xa dedicarle time, ya lo retomaré... me preocupa más mi próximo microscopio ;)
la poesía no es de quien la escribe, sino de quien la necesita...
Post 4 de 6 // 04/08/2009 a las 23:20:25 ACK! x 13.54c.
Verso
Eviscerador Perpetuo
Avatar de verso
*****-
9.88 culombios
138475 p.d.exp.
Un eón
Caballero
cLicK
cLicK
Gex muere y sufre la peor de todas las humillaciones jajajajajaaja

Qué juegos tan divertidos, ¡piensa cuál dejarme!

***

A la derecha deeste blog está lo de "sorry for my poor English", como aviso está bien, pero el hecho de que parpadee es un poco incómodo mientras se lee el texto de al lado. Además, este blog tiene pocas páginas en English.
DDK
Post 5 de 6 // 23/08/2009 a las 05:12:22 14c.
granaína
Dama De Las Tinieblas
Avatar de granaina
****--
16.00 culombios
71328 p.d.exp.
Un eón
Doncella
pensaba probarlo antes de darte una puntuación, xo voy a tardar en pillarle el truco y seguramente necesite tu ayuda... la explicación está muy bien hecha, lo q no entiendo en realidad son las partes de programación en plan los corchetes, las comillas, el =, todo eso ya "sabéis dónde ponerlo"? lo aprendéis de memoria con la práctica o copiáis/pegáis? perdón x mi absoluto desconocimiento :S
la poesía no es de quien la escribe, sino de quien la necesita...
Post 6 de 6 // 23/08/2009 a las 13:07:10 ACK! x 42.67c.
LaNsHoR
Eviscerador Perpetuo
Avatar de lanshor
*****-
43.59 culombios
439761 p.d.exp.
-
Caballero
cLicK
Jeje, no esperaba que entendieses eso. Cada símbolo tiene una función y cuando aprendes el lenguaje aprendes las reglas que hay que seguir y su significado. De la misma forma que aprendes las normas de acentuación en Español, aprendes, por así decirlo, las normas de ese lenguaje concreto.

Es algo bastante parecido a un idioma, no hay nada de memoria ni c&p, tú escribes lo que quieres expresar desde cero conociendo el lenguaje de la misma forma que yo escribo ahora este post, sólo que de una forma muy formalizada y bastante matemática, y teniendo muy en cuenta la arquitectura de la plataforma para la que escribes el programa.
For the good of all of us (except the ones who are dead).

Anónimo

Páginas
Vista Previa de la página 10 del blog de blei_courtPágina 10
Vista Previa de la página 9 del blog de blei_courtPágina 9
Vista Previa de la página 8 del blog de blei_courtPágina 8
Vista Previa de la página 7 del blog de blei_courtPágina 7
Vista Previa de la página 6 del blog de blei_courtPágina 6
Puntuación
WARNING!!!
Sorry for my poor English

llorandollorandollorandollorandollorando
Estadísticas
5699 Días
1397 Visitas
6 Posts
Postea una de
cada 233 visitas
Licencia

"Es un error creer que uno está rodeado de tontos, aunque sea verdad." Noel Clarasó