martes, 22 de septiembre de 2015

Crear juego RPG en C++ y Allegro 4 (13) Dialogos

Aqui esta una nueva entrega del curso Crea tu juego RPG en C++ y Allegro, esta vez nos vamos a centrar en los diálogos.







Objetivo del curso

En este curso, se creará una nueva clase que se encargue de los diálogos del juego. Siguiendo el estilo de juego RPG, cuando se muestre el texto por pantalla se hará de forma pausada para que de tiempo a leerlo y cuando el usuario pulse una tecla, salte al siguiente texto. En esta primera entrega estará centrada en la clase dialogo.


Programación

Se va a programar las siguientes funciones: menor, mayor, numlineas, crea, pinta, y cambia_texto.

La funcion menor, compara dos variables y devuelve el contenido de la variable de menor valor.

int menor(int x, int y){
    if ( x < y ){
       return x;  
    }else{
       return y;   
    }       
}


La función mayor, compara dos variables y devuelve el contenido de la variable de mayor valor.

int mayor(int x, int y){
    if ( x < y ){
       return y;  
    }else{
       return x;   
    }       
}


La función numlineas, calcula el número de lineas que se necesita para mostrar un texto dentro de un rectángulo determinado.

void MENSAJE::numlineas(){
     int cont;
     int espacio = 0;
     char* mtexto = (char*)stexto.c_str();
     nlineas=1;
     
     if ( tancho+espaciado > ancho ){
        // no cabe en una linea  
        string resto = stexto;
        string trozo;
        char caracter[] = " ";
        char* caracter2;
        int nuevoancho = 0;
        int nc = 0;
        int restoancho = 0;
        do{     
                cont=1;
                trozo = resto.substr(0,cont);
                mtexto = (char*)trozo.c_str();
                nuevoancho = text_length( fuente, mtexto ); 
                espacio = 0;                   
                while ( nuevoancho+espaciado < ancho ){
                      trozo = resto.substr(cont,1);
                      caracter2 = (char*)trozo.c_str();
                      if ( strcmp(caracter2,caracter)==0 ){
                           espacio = cont;
                      }
                      cont++;
                      trozo = resto.substr(0,cont);
                      mtexto = (char*)trozo.c_str();
                      nuevoancho = text_length( fuente, mtexto ); 
                } 
                nc = resto.length();
                trozo = resto.substr(cont,1);
                caracter2 = (char*)trozo.c_str();        
                nlineas++;
                if ( espacio >0 && cont < nc && strcmp(caracter2,caracter)!=0 )
                {
                     resto = resto.substr(espacio);
                }else{
                     resto = resto.substr(cont); 
                }        
                restoancho = text_length( fuente, resto.c_str() );
        }while( restoancho+espaciado > ancho );      
               
     }          
}

Esta función sería mucho mas simple en el caso de que la fuente utilizada tenga la cualidad de que todos sus caracteres ocupen el mismo ancho, es decir, que por ejemplo la "W" ocupe lo mismo que la "l". Pero como la mayoría no es así, se complica un poco para averiguar cuantos caracteres caben en un ancho determinado. Por ello, se va cogiendo carácter a carácter hasta completar una linea sin llegar a sobrepasar el limite dado por el rectángulo. Debido a esto es algo complejo averiguar cuantas lineas van a ocupar, aunque si es fácil averiguar cuantas lineas es el máximo que cabe en el espacio definido por el rectángulo. El número total de lineas que ocupará el texto es almacenado en la variable nlineas.

La función crea, se encarga de inicializar las variables de la clase y define texto que se va a mostrar y las dimensiones del rectángulo de texto.

void MENSAJE::crea(const char* t, FONT* f, int x1, int y1, int x2, int y2){
   stexto = t;
   tancho = text_length( f, t );   
   talto  = text_height(f) + espaciado;
   fuente = f;
   
   mx1 = menor(x1,x2);
   mx2 = mayor(x1,x2);
   my1 = menor(y1,y2);
   my2 = mayor(y1,y2);
   ancho = abs( mx1 - mx2 );
   alto  = abs( my1 - my2 );
   
   numlineas();

};


Esta función recibe como parámetro el texto a mostrar, la fuente ( tipo de letra ), y las dimensiones del cuadro de dialogo. Inicializa los valores que utiliza la clase, y se asegura que las coordenadas para definir el tamaño del rectángulo estén de forma correcta. Y calcula el ancho y alto del rectángulo.

La función pinta, se encarga de mostrar por pantalla el rectángulo de dialogo. Coloca en lo mejor posible el texto ajustado al tamaño del rectángulo.

void MENSAJE::pinta(BITMAP* b){
     int cont;
     int espacio = 0;
     char* mtexto = (char*)stexto.c_str();
     int linea=0;
     int ni;
     int altura = 0;
     float exacto;
     BITMAP *cuadro = create_bitmap(ancho,alto);

     
    clear_to_color(cuadro, 0x222222);
    set_trans_blender(0,0,0,130);         
    draw_trans_sprite(b, cuadro, mx1, my1);               
    set_trans_blender(0,0,0,255);         
    
    rect(b, mx1-1, my1-1, mx2-1, my2-1, 0xfcf902);
    rect(b, mx1+1, my1+1, mx2+1, my2+1, 0x363712);      
    rect(b, mx1, my1, mx2, my2, 0x222222);
       
     
     if ( tancho+espaciado > ancho ){
        // no cabe en una linea  
        string resto = stexto;
        string trozo;
        char caracter[] = " ";
        char* caracter2;
        int nuevoancho = 0;
        int nc = 0;
        int restoancho = 0;
        
         do{      
                cont=1;
                trozo = resto.substr(0,cont);
                mtexto = (char*)trozo.c_str();
                nuevoancho = text_length( fuente, mtexto ); 
                espacio = 0;                   
                while ( nuevoancho+espaciado < ancho ){
                      trozo = resto.substr(cont,1);
                      caracter2  = (char*)trozo.c_str();
                      if ( strcmp(caracter2,caracter)==0 ){
                           espacio = cont;
                      }
                      cont++;
                      trozo = resto.substr(0,cont);
                      mtexto = (char*)trozo.c_str();
                      nuevoancho = text_length( fuente, mtexto ); 
                } 
                nc = resto.length();
                trozo = resto.substr(cont,1);
                caracter2  = (char*)trozo.c_str();
                
                if ( espacio >0 && cont < nc && strcmp(caracter2,caracter)!=0 ){
                     trozo = resto.substr(0,espacio);
                     mtexto = (char*)trozo.c_str();             
                     resto = resto.substr(espacio);
                }else{
                     trozo = resto.substr(0,cont);
                     mtexto = (char*)trozo.c_str();              
                     resto = resto.substr(cont); 
                }
                
                     
                altura = alto - (talto*nlineas);
                exacto = ( alto / nlineas );
                ni = int( exacto );
        
                textout_centre_ex(b, fuente, mtexto, mx1+1+int(ancho/2), my1+2+(ni*linea)-(talto/2)+(ni/2) , 0x363712, -1);                        
                textout_centre_ex(b, fuente, mtexto, mx1+int(ancho/2), my1+1+(ni*linea)-(talto/2)+(ni/2) , 0xffffff, -1);            
                
                linea++;   
                restoancho = text_length( fuente, resto.c_str() );
         }while( restoancho+espaciado > ancho ); 
        
        mtexto = (char*)resto.c_str(); 
        textout_centre_ex(b, fuente, mtexto, mx1+1+int(ancho/2), my1+2+(ni*linea)-(talto/2)+(ni/2) , 0x363712, -1);                        
        textout_centre_ex(b, fuente, mtexto, mx1+int(ancho/2), my1+1+(ni*linea)-(talto/2)+(ni/2) , 0xffffff, -1);            
                
     }else{
           
         textout_centre_ex(b, fuente, mtexto, mx1+(ancho/2)+1, my1+1+((alto-talto)/2), 0x363712, -1);          
         textout_centre_ex(b, fuente, mtexto, mx1+(ancho/2), my1+((alto-talto)/2), 0xffffff, -1);           
          
     }   
     

     destroy_bitmap(cuadro);    
   
};     


Esta función se encarga de pintar un rectángulo, y sobre el se pinta el texto de manera que este centrado dentro de el todo el texto. En el caso de que el texto no cabe en una linea según el ancho del rectángulo, se realiza un bucle que se encarga de cojer carácter a carácter y va formando las lineas para que no superen el ancho del rectángulo. Una vez que llega al ancho muestra la linea, y vuelve a repetir el proceso con lo que queda por mostrar. Esta forma de hacerlo tiene un inconveniente, y es que cada vez que se muestra se vuelve a calcular todo, por tanto es un proceso algo lento.


Aquí en este video se muestra un ejemplo utilizando la librería, creando varios cuadros de diálogos de distintos tamaño, mostrando un texto que se ajusta según va variando el tamaño de estos rectángulos.


La función cambia_texto, como su nombre indica sirve para cambiar el texto.

void MENSAJE::cambia_texto( const char* t){
   stexto = t;
   tancho = text_length( fuente, t );   
   
   numlineas();  
};  

Esta función se encarga de cambiar el texto a mostrar, actualizando los valores del texto.

La clase se define de la siguiente manera

class MENSAJE{
      
      string stexto;
      
      FONT *fuente;

      // ancho total del texto enviado
      int tancho;    
      int talto; 
    
      // para delimitar el rectangulo de vision     
      int mx1,my1;
      int mx2,my2;
      // ancho y alto del rectangulo
      int ancho, alto;     
      
      // numero de lineas totales q caben en el rectangulo
      int nlineas;

      void numlineas(); 
      
   public:  

      void crea(const char* t, FONT* f, int x1, int y1, int x2, int y2);
           
      void pinta(BITMAP* b);
      
      void cambia_texto( const char* t );
      
};


La clase creada se ha llamado MENSAJE.

  • stexto : contiene el texto a mostrar
  • fuente : contiene el tipo de letra que se utilizará para escribir el texto.
  • tancho y talto : son dimensiones del texto según la fuente utilizada.
  • mx1,my1 y mx2,my2 : son las coordenadas que delimitan nuestro cuadro de dialogo.
  • ancho, alto: son las dimensiones del cuadro de dialogo.
  • nlineas: indica el numero de líneas que ocupa el texto para mostrarse en el cuadro de dialogo.
  • numlineas() : función privada que se encarga de contar el número de lineas.

También se debe de añadir al principio del código los #include, para incluir las librerías que se utilizan "allegro.h" y la de "string". Y definir una constate llamada  "espaciado", con un valor 10. ( este valor se puede modificar según se quiera dejar mas o menos margen entre el border del cuadro y el texto), para ello debes escribir:
#define espaciado 10


Para que no tengan problema con esta nueva librería, les dejo el siguiente link para descargar dialogos.h en archivo RAR.


Recuerda

Si tienes algún problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de programación:  http://creatusjuegosdecero.webege.com/index.php

2 comentarios:

  1. Donde se declara la variable "espaciado"? Me sale un error porque esta no sé detecta, y cuando busco la palabra en la página, solo sale siendo usada pero nunca declarada

    ResponderEliminar
    Respuestas
    1. Hola Satoshi, tienes razón no esta declarado "espaciado", es una constante que le puse el valor de 10 con el siguiente comando:

      #define espaciado 10

      Muchas gracias por avisar del error.

      Eliminar

Antes de publicar un comentario

Todos los comentarios que se realicen en el blog son moderados.

Debido a esto es muy probable que tu comentario no aparezca de inmediato ya que previamente debe ser revisado para evitar un mal uso (SPAM).

Podrán realizar comentario cualquiera que tenga una cuenta de Google.

Related Posts Plugin for WordPress, Blogger...