domingo, 26 de junio de 2011

Curso de programación: Capítulo 1. Entender código en C++


Hola a todos...

Aunque la encuesta sobre el nivel de programación no ha acabado ya he visto que hay un proporción de lectores apreciable que o bien no se ve capaz de defenderse con C/C++ o bien, sabe programar en estos lenguajes pero nunca ha hecho un videojuego.

Este primer capítulo del curso de programación (cuya extensión en número de entregas desconozco, a medida que vea que a la gente le hace falta iré escribiendo más) intentará ayudar un poco a todo el mundo a poder leer el código en C++ que describo en el game engine.

  1. Lo básico: Si ya sabes programar en algún lenguaje ves al siguiente punto que te aburrirás, sino, echale un ojo que no te hará daño. Este curso no está pensado para enseñar a programar (me veo totalmente incapaz de una tarea de tanta responsabilidad, además de que por la red hay tutoriales y manuales mucho mejores de los que yo podría escribir en mi vida) pero si en ayudar a poder leer un código más o menos en cualquier lenguaje de programación.
    Todos los lenguajes tienen un conjunto de instrucciones más o menos comunes:
    1. If / else . Condicional. "Si pasa esto hago aquello si no hago lo de más allá." Es la instrucción más sencilla que nos permite tomar decisiones. Un ejemplo en el caso de C/C++ sería (extraido del model manager http://lordpakus.blogspot.com/2011/06/gameengine-capitulo-8.html):
        if ( cad[i] == '=' ) 
        { 
          cad[i]='\0';
          sprintf(id,"%s",cad);
          sprintf(ruta,"%s",&cad[i+1]); 
          i = 257; 
        }
                     Los operadores de comparación pueden ser:
                         == (doble igual) para igualdad
                         != no igual
                        > mayor que
                        < menor que
                       >= mayor o igual
                       <= menor o igual
                       (hay más pero por ahora lo dejamos como está que ya nos sirve)

    1. For. Bucle controlado. "Haz x veces está operación". Es una instrucción que permite realizar un conjunto de instrucciones tantas veces como queramos. En la sintaxis C/C++ el for recibe tres parametros: inicialización, comparación de bucle e incremento de bucle. Un ejemplo de uso lo podemos encontrar en el file manager http://lordpakus.blogspot.com/2011/06/hola-todos-bienvenidos-una-nueva.html : 
      for ( i = 0 ; i < MAX_ELEMENTS_LOADED ; ++i ) //Para cada elemento cargado 
      {
      if( !strcmp(loaded[i],cad) ) //Hemos encontrado la cadena que nos interesa
      {
      return i; //Nos vamos devolviendo el valor que nos pedian
      } 
      }
    1. While. Bucle no controlado. "Mientras se cumpla esta condición repite la operación". Es una instrucción que permite realizar un conjunto de instrucciones tantas veces sea necesario para que deje de cumplirse una condición. Un ejemplo de su uso lo podemos ver en el texture manager http://lordpakus.blogspot.com/2011/06/gameengine-capitulo-6.html 
      while( filemanager.GetLine(cad) ) 
      { 
      for( int i = 0 ; i < strlen(cad); ++i) 
      { 
      if ( cad[i] == '=' ) 
      { 
      cad[i]='\0';
      sprintf(id,"%s",cad);
      sprintf(ruta,"%s",&cad[i+1]); 
      i = 257;
      }
      }balblablalbalalalb
      }
    1. switch / case . Selector. "En función de lo que valga esta variable haz esto, aquello o lo otro". Esta instrucción es parecida a tener varios if anidados. Permite evaluar a la vez los multiples valores de una variable y decidir que acción realizar. Un ejemplo lo podemos ver en la demo de show de sonido del coremanager http://lordpakus.blogspot.com/2011/05/gameengine-capitulo-1.html que luego en el capitulo 11 fue pasado al PlayState http://lordpakus.blogspot.com/2011/06/gameengine-capitulo-11statemanager.html:
        switch(rand()%5) 
        { 
          case 0 : 
             Core::singleton().PlaySound("SOUND_BOOST");
          break;
          case 1 :
            Core::singleton().PlaySound("SOUND_PASOS");
          break;
          case 2 :
            Core::singleton().PlaySound("SOUND_SALTO");
          break;
          case 3 :
            Core::singleton().PlaySound("SOUND_COIN");
          break;
          case 4 :
            Core::singleton().PlaySound("SOUND_DIE");
          break;
        }

    1. Operadores de salto de flujo. Los operadores de salto de flujo son aquellos que modifican la normal ejecución del flujo de programa. Hay tres usados a lo largo y ancho del game engine:
      1. return: Nos hace salir de la función donde estemos devolviendo algún valor si se lo pasamos. Es obligatorio que como mínimo haya uno al final en las funciones que devuelven algún valor.
      2. Continue : Hace que la ejecución del bucle actual se interrumpa (dentro de un for o un while por ejemplo) y se pase al siguiente ciclo.
      3. Break: Hace que el bucle actual se interrumpa totalmente (dejamos de ejecutar el for o while en cuestión.)


  1. El precompilador: Es posible que lleves un tiempo programando pero nunca te hayas tenido que pelear con el precompilador. El precompilador no es más que un programa externo al compilador que se encarga de preparar el código para que el compilador pueda realizar su tarea (elimina comentarios, traduce define, aplica macros, etc...). Todas las operaciones de precompilador se inician con el caracter # (al menos en C/C++). Las operaciones más típicas son:
    1. include. Incluye un fichero de cabecera externo. Esto permite que las funciones o variables definidas en ese fichero se puedan usar en el nuestro . Ejemplo #include "MyGL.h"
    2. define. Como su nombre indica define una “variable” de precompilación. Esta “variable” (que de variable no tiene nada) tiene múltiples aplicaciones:
      1. Almacenar un valor. #define GL_CLAMP_TO_EDGE 0x812F
      2. Almacenar una “macro” #define funcion(x) (0x01<<x)
      3. Marcar un hecho en la compilación #define __State__
    3. ifdef/endif. Se usa para saber si una “variable” implementada con define se ha definido o no. Acostumbra a usar muy ligada a la tercera aplicación.
        #if defined(__MACOS__) || (defined(__MACH__) && defined(__APPLE__))
#include <OpenGL/gl.h>
#else
#if defined(WIN32)
//#include <windows.h>
#endif
#include <GL/gl.h>
#endif
    1. pragma . Se usa para darle instrucciones al compilador de como queremos que nos genere el ejecutable: contra que librería tiene que linkar, como queremos que nos organice la memoria del .exe ,etc... es demasiado complejo para explicarlo aquí, pero os pondré un ejemplo para que os hagaís una idea:
        #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //Eliminamos consola
        #pragma comment(lib, "openal32.lib") //Linkamos librería openal

  1. Otros . Hay un conjunto de instrucciones que acostumbran a generar dudas cuando alguien lee código por primera vez y que sinceramente, no he sabido como agruparlas.
    1. Using namespace. No se muy bien como explicarlo, pero podriamos decir que es una directiva (creo recordar que propia de C++) que nos permite trabajar directamente como si estuvieramos en otro archivo. La diferencia entre usarla o no es la siguiente:
        using namespace std;
cout << "Inicializamos el Core Manager\n";

std::cout << "Inicializamos el Core Manager\n";

    1. Preincrementos ( ++i ) contra postincrementos (i++) . Ambos operadores incrementan en un unidad la variable sobre la que se aplican y en el mayoria de los casos se comportan igual, PERO, no hacen exactamente lo mismo. El caso divergente ocurre cuando se aplican sobre una comparación. El preincremento (++i) primero incrementa y después compara mientras que el postincremento (i++) primero evalua y después incrementa. Un ejemplo lo tenemos aquí:
        int i = 0;
if ( ++i == 1)
return OK;
else
return FAIL;
int i = 0;
if( i++ == 1)
return OK;
else
return FAIL;

El primer caso nos devoleria OK y el segundo caso FAIL.
    1. Singleton. Un singleton es un patrón de diseño (buscad por google si estais interesados) que no tiene nada que ver con un lenguaje específico sino con una manera de organizar el software. Un singleton nos dice que la clase en cuestión no se podrá usar para generar infinitos objetos diferentes sino que todos los objetos que se “generen” serán exactamente el mismo (con las mismas variables y funciones , conteniendo exactamente la misma inforamción). Los managers del game engine están implementados de esta manera para que sea más fácil la organización de código.




Con esto acaba la primera entrega de “como entender un programa en c++”. Si hay algo que me haya dejado o cualquier cosa que no os queda clara decidlo y lo incluiré próximos capítulos.

Espero que os haya servido, nos vemos.

LordPakusBlog

1 comentario :

Entradas populares