2012-06-12 20 views
9

Mi aplicación requiere una consola embebida dentro de la ventana de la aplicación, un ejemplo sería en un programa como autoCAD donde la consola está en la parte inferior de la ventana esperando los comandos.Escribir una consola dentro de una aplicación

enter image description here

necesito la consola dentro de mi aplicación para que pueda cambiar variables y otras cosas, por lo que la consola no es tener que ser una cáscara totalmente apagada.

Por el momento tengo una consola simple dentro de mi aplicación, pero parece muy torpe en comparación con una terminal (shell) que es lo que quiero que sea la consola.

enter image description here

La forma en que lo he hecho con la consola lo que tengo es cuando el usuario presiona la tecla TAB muestra la consola, a continuación, se puede escribir en su comando/línea; Una vez que se presiona la tecla Return, se analiza la cadena que han tipeado y se procesa el comando.

Estoy utilizando sf::Text objetos para imprimir texto en la ventana de mi aplicación. Hay un total de 5 sf::Text objetos que se usan, 4 para los comandos/mensajes de error anteriores y 1 para la línea de comando actual. Cuando se presiona la tecla Return, la 4ª sf::Text cambia su cadena actual a la 3ª, a la 3ª a la 2ª, a la 2ª a la 1ª y a la 1ª a la cadena de comando actual, luego se borra la cadena de comando actual y vuelve a estar lista para la entrada. De esta forma, hay espacio para 4 'historial' de comandos y/o errores. No es el mejor, pero fue lo mejor que se me ocurrió. Por supuesto, la cantidad de historial podría cambiarse agregando más objetos sf::Text. Así que al final así es como me hacen la consola a la pantalla

sf::RectangleShape rectangle; 

rectangle.setSize(sf::Vector2f(App->getSize().x, App->getSize().y/3)); 
rectangle.setPosition(0, 0); 

rectangle.setFillColor(sf::Color::black); 

App->draw(rectangle); // This renders the console looking background rectangle 
App->draw(CLine); // This renders the current command line 

for(int i = 4; i >= 0; --i) // This renders the history as described above, their idevidual positions are setup earlier on in the program 
{ 
    CHistory[i].setString(CS[i]); 
    App->draw(CHistory[i]); 
} 

App es sólo un sf::RenderWindow*

Mi pregunta general es, ¿Hay una manera que puedo incrustar una consola en mi SFML ventana sin que tenga que ser simplemente una imagen de objetos de texto representados para parecerse a una consola como la anterior. Preferiría tener una consola/shell/terminal real dentro de mi aplicación. Como el shell bash estándar, pero por supuesto mi intérprete de shell.

Respuesta

-1

Si está implementando su propia consola personalizada, tendrá que escribir ese lote usted mismo. Una rápida revisión de the SMFL docs sugiere que no hay nada incorporado para ayudarlo. Esto es solo una biblioteca multimedia simple y rápida ;-)

Si necesita algo más completo, le sugiero que busque en OGRE.

+0

No estoy buscando que sfml lo haga por mí, agregué sfml como una etiqueta y en los ejemplos de código para mostrar que estaba usando sfml. Sé que tengo que escribirlo, lo que busco es instrucciones sobre cómo hacerlo. Cómo han pasado otras personas escribiendo una consola en su aplicación. : D – Elgoog

+0

Sugiero que refine su pregunta en algunas partes más pequeñas una vez que haya identificado qué es lo que quiere lograr. ¿Cuáles son sus requisitos? Intente usar el que está en Quake o Unreal Tournament o cualquier juego que le interese. –

+0

Wolfire desarrolló algo basado en Webkit aparentemente: http://www.youtube.com/watch?v=c-4WdtTGIkg&feature=relmfu –

0

Bueno, lo que es probable que desee si quiere que se sienta más como una consola es:

  • Ser capaz de encenderlo y con sólo pulsar un botón, probablemente algo así como ~, que se utiliza una mucho.
  • Da la línea que estás escribiendo en un color de fondo, tal vez transparente, pero al menos asegúrate de que no sea solo texto flotante en RenderWindow. Si la salida del comando es de varias líneas, asegúrese de que estén todas visibles o de que la gente al menos pueda desplazarse por el historial.
  • Asegúrate de que los comandos sean fáciles de entender y constantes. Por ejemplo, si no me equivoco, muchos juegos en el motor de origen usan el prefijo cl_ para cualquier representación relacionada. Ver cl_showfps 1 por ejemplo.
  • La entrada de terminal tradicional sería un buen toque. Arriba le muestra el comando anterior que completó. Tal vez si se siente aventurero, use Tab para completarlo.
  • Si le queda algo de tiempo, una forma de mostrar los comandos disponibles a través de --help, por ejemplo, también sería bueno. Dependiendo de cuán complicado es tu juego, por supuesto.

Para el resto, vea cómo otros juegos hicieron esto. Mencionaste Quake, que tiene un gran ejemplo de una terminal dentro del juego. Por mi parte, creo que el que está en muchos juegos de Source también es fácil de usar (ver Half Life 2, Counter Strike Source, Team Fortress 2, Left 4 Dead, etc.). No creo que haya bibliotecas estándar para esto, sin incluir otro marco como OGRE o IrrLicht.

4

Implementé lo siguiente como una consola para un juego opengl que estaba escribiendo hace un tiempo. No es de ninguna manera una respuesta definitiva a su pregunta, pero funcionó para mí y es posible que obtenga algo útil de ella.

Los 2 archivos se encuentran al final de esta publicación. Es poco probable que el código se ejecute directamente, ya que hay uno para 2 archivos de encabezado de biblioteca que no voy a incluir. Si quieres la fuente completa, házmelo saber.

Básicamente, la clase de consola le permite agregar punteros variables que se pueden cambiar en tiempo de ejecución. Acepta la entrada de los mensajes de eventos de Windows. (El manejo real de la entrada se realiza en otro lugar) El análisis de comandos se realiza en el método ProcessInput() y las variables se actualizan en el método ChangeVariable().

Una palabra de advertencia. Este método esencialmente les da a los usuarios de la consola acceso directo a las ubicaciones de memoria de la variable individual. Esto requiere una buena validación de entrada para garantizar que el usuario no pueda causar que la aplicación se bloquee en el tiempo de ejecución. Si alguna vez me senté e intenté hacer otra consola, probablemente haría las cosas un poco diferente. Sin embargo, espero que esto te brinde un poco de ayuda.

El archivo de cabecera:

#ifndef CONSOLE_H 
#define CONSOLE_H 

#include <vector> 
#include <map> 
#include <string> 
#include "Singleton.h" 
#include <Windows.h> 
#include "Enumerations.h" 
#include "StringConversion.h" 

class Console 
{ 
public: 

    Console(); 
    ~Console(); 

    void Update(std::vector<WPARAM> pressedKeys); 

    void AddInt(std::string varName, int *ptrToInt); 
    void AddFloat(std::string varName, float *ptrToFloat); 
    void AddLong(std::string varName, long *ptrToLong); 
    void AddBool(std::string varName, bool *ptrToBool); 

    const std::string &GetCurrentText(); 
    const std::vector<std::string> &GetPreviousText(); 

private: 
    std::map<std::string, int *> m_Ints; 
    std::map<std::string, float *> m_Floats; 
    std::map<std::string, long *> m_Longs; 
    std::map<std::string, bool *> m_Bools; 

    std::map<std::string, std::string> m_Variables; 

    std::vector<std::string> m_PrevConsoleText; 
    std::string m_CurrInput; 

    int m_PrevSelection; 

    bool ProcessInput(); 
    void ChangeVariable(const std::string &varName, const std::string &value); 
}; 

typedef Singleton<Console> g_Console; 

#endif // CONSOLE_H 

El archivo CPP:

#include "Console.h" 

Console::Console() 
{ 
    m_PrevSelection = 0; 
} 

Console::~Console() 
{ 

} 

void Console::AddInt(std::string varName, int *ptrToInt) 
{ 
    m_Ints[varName] = ptrToInt; 
    m_Variables[varName] = "int"; 
} 

void Console::AddFloat(std::string varName, float *ptrToFloat) 
{ 
    m_Floats[varName] = ptrToFloat; 
    m_Variables[varName] = "float"; 
} 

void Console::AddLong(std::string varName, long *ptrToLong) 
{ 
    m_Longs[varName] = ptrToLong; 
    m_Variables[varName] = "long"; 
} 

void Console::AddBool(std::string varName, bool *ptrToBool) 
{ 
    m_Bools[varName] = ptrToBool; 
    m_Variables[varName] = "bool"; 
} 

void Console::ChangeVariable(const std::string &varName, const std::string &value) 
{ 
    //*(m_Bools[varName]) = value; 

    std::string temp = m_Variables[varName]; 

    if(temp == "int") 
    { 
     //*(m_Ints[varName]) = fromString<int>(value); 
    } 
    else if(temp == "float") 
    { 
     //*(m_Floats[varName]) = fromString<float>(value); 
    } 
    else if(temp == "long") 
    { 
     //*(m_Longs[varName]) = fromString<long>(value); 
    } 
    else if(temp == "bool") 
    { 
     if(value == "true" || value == "TRUE" || value == "True") 
     { 
      *(m_Bools[varName]) = true; 
     } 
     else if(value == "false" || value == "FALSE" || value == "False") 
     { 
      *(m_Bools[varName]) = false; 
     } 
    } 
} 

const std::string &Console::GetCurrentText() 
{ 
    return m_CurrInput; 
} 

void Console::Update(std::vector<WPARAM> pressedKeys) 
{ 
    for(int x = 0; x < (int)pressedKeys.size(); x++) 
    { 
     switch(pressedKeys[x]) 
     { 
     case KEY_A: 
      m_CurrInput.push_back('a'); 
      break; 
     case KEY_B: 
      m_CurrInput.push_back('b'); 
      break; 
     case KEY_C: 
      m_CurrInput.push_back('c'); 
      break; 
     case KEY_D: 
      m_CurrInput.push_back('d'); 
      break; 
     case KEY_E: 
      m_CurrInput.push_back('e'); 
      break; 
     case KEY_F: 
      m_CurrInput.push_back('f'); 
      break; 
     case KEY_G: 
      m_CurrInput.push_back('g'); 
      break; 
     case KEY_H: 
      m_CurrInput.push_back('h'); 
      break; 
     case KEY_I: 
      m_CurrInput.push_back('i'); 
      break; 
     case KEY_J: 
      m_CurrInput.push_back('j'); 
      break; 
     case KEY_K: 
      m_CurrInput.push_back('k'); 
      break; 
     case KEY_L: 
      m_CurrInput.push_back('l'); 
      break; 
     case KEY_M: 
      m_CurrInput.push_back('m'); 
      break; 
     case KEY_N: 
      m_CurrInput.push_back('n'); 
      break; 
     case KEY_O: 
      m_CurrInput.push_back('o'); 
      break; 
     case KEY_P: 
      m_CurrInput.push_back('p'); 
      break; 
     case KEY_Q: 
      m_CurrInput.push_back('q'); 
      break; 
     case KEY_R: 
      m_CurrInput.push_back('r'); 
      break; 
     case KEY_S: 
      m_CurrInput.push_back('s'); 
      break; 
     case KEY_T: 
      m_CurrInput.push_back('t'); 
      break; 
     case KEY_U: 
      m_CurrInput.push_back('u'); 
      break; 
     case KEY_V: 
      m_CurrInput.push_back('v'); 
      break; 
     case KEY_W: 
      m_CurrInput.push_back('w'); 
      break; 
     case KEY_X: 
      m_CurrInput.push_back('x'); 
      break; 
     case KEY_Y: 
      m_CurrInput.push_back('y'); 
      break; 
     case KEY_Z: 
      m_CurrInput.push_back('z'); 
      break; 
     case KEY_0: 
      m_CurrInput.push_back('0'); 
      break; 
     case KEY_1: 
      m_CurrInput.push_back('1'); 
      break; 
     case KEY_2: 
      m_CurrInput.push_back('2'); 
      break; 
     case KEY_3: 
      m_CurrInput.push_back('3'); 
      break; 
     case KEY_4: 
      m_CurrInput.push_back('4'); 
      break; 
     case KEY_5: 
      m_CurrInput.push_back('5'); 
      break; 
     case KEY_6: 
      m_CurrInput.push_back('6'); 
      break; 
     case KEY_7: 
      m_CurrInput.push_back('7'); 
      break; 
     case KEY_8: 
      m_CurrInput.push_back('8'); 
      break; 
     case KEY_9: 
      m_CurrInput.push_back('9'); 
      break; 
     case KEY_QUOTE: 
      m_CurrInput.push_back('\"'); 
      break; 
     case KEY_EQUALS: 
      m_CurrInput.push_back('='); 
      break; 
     case KEY_SPACE: 
      m_CurrInput.push_back(' '); 
      break; 
     case KEY_BACKSPACE: 
      if(m_CurrInput.size() > 0) 
      { 
       m_CurrInput.erase(m_CurrInput.end() - 1, m_CurrInput.end()); 
      } 
      break; 
     case KEY_ENTER: 
      ProcessInput(); 
      break; 
     case KEY_UP: 
      m_PrevSelection--; 
      if(m_PrevSelection < 1) 
      { 
       m_PrevSelection = m_PrevConsoleText.size() + 1; 
       m_CurrInput = ""; 
      } 
      else 
      { 
       m_CurrInput = m_PrevConsoleText[m_PrevSelection - 1]; 
      } 

      break; 
     case KEY_DOWN: 
      if(m_PrevSelection > (int)m_PrevConsoleText.size()) 
      { 
       m_PrevSelection = 0; 
       m_CurrInput = ""; 
      } 
      else 
      { 
       m_CurrInput = m_PrevConsoleText[m_PrevSelection - 1]; 
      } 
      m_PrevSelection++; 
      break; 
     } 
    } 
} 

bool Console::ProcessInput() 
{ 
    int x; 
    std::string variable = "NULL", value; 
    bool ok = false; 
    std::string::iterator it; 

    //Split up the input from the user. 
    //variable will be the variable to change 
    //ok will = true if the syntax is correct 
    //value will be the value to change variable to. 
    for(x = 0; x < (int)m_CurrInput.size(); x++) 
    { 
     if(m_CurrInput[x] == ' ' && variable == "NULL") 
     { 
      variable = m_CurrInput.substr(0, x); 
     } 
     else if(m_CurrInput[x] == '=' && m_CurrInput[x - 1] == ' ' && m_CurrInput[x + 1] == ' ') 
     { 
      ok = true; 
     } 
     else if(m_CurrInput[x] == ' ') 
     { 
      value = m_CurrInput.substr(x + 1, m_CurrInput.size()); 
     } 
    } 

    if(ok) 
    { 
     m_PrevConsoleText.push_back(m_CurrInput); 
     m_PrevSelection = m_PrevConsoleText.size(); 

     if(m_PrevConsoleText.size() > 10) 
     { 
      m_PrevConsoleText.erase(m_PrevConsoleText.begin(), m_PrevConsoleText.begin() + 1); 
     } 
     m_CurrInput.clear(); 


     ChangeVariable(variable, value); 
    } 
    else 
    { 
     m_PrevConsoleText.push_back("Error invalid console syntax! Use: <variableName> = <value>"); 
     m_CurrInput.clear(); 
    } 

    return ok; 
} 

const std::vector<std::string> &Console::GetPreviousText() 
{ 
    return m_PrevConsoleText; 
} 

Edición 1: Agregado DrawConsole() acabo de recibir el texto de la clase consola de rendir una imagen que parecía similar a la ventana de la consola del motor de origen que se encuentra en cualquier juego de válvulas reciente y luego el texto se dibuja en los lugares apropiados.

void View::DrawConsole() 
{ 
    Square console; 
    std::vector<std::string> temp; 
    temp = g_Console::Instance().GetPreviousText(); 

    console.top = Vector3f(0.0, 0.0, 1.0); 
    console.bottom = Vector3f(640, 480, 1.0); 

    g_Render::Instance().SetOrthographicProjection(); 
    g_Render::Instance().PushMatrix(); 
    g_Render::Instance().LoadIdentity(); 

    g_Render::Instance().BindTexture(m_ConsoleTexture); 
    g_Render::Instance().DrawPrimative(console, Vector3f(1.0f, 1.0f, 1.0f)); 
    g_Render::Instance().DisableTexture(); 

    g_Render::Instance().SetOrthographicProjection(); 
    //Draw the current console text 
    g_Render::Instance().DrawString(g_Console::Instance().GetCurrentText(), 0.6f, 20, 465); 

    //Draw the previous console text 
    for(int x = (int)temp.size(); x > 0; x--) 
    { 
     g_Render::Instance().DrawString(temp[x-1], 0.6f, 20, (float)(425 - (abs((int)temp.size() - x) * 20))); 
    } 

    g_Render::Instance().SetPerspectiveProjection(); 

    g_Render::Instance().PopMatrix(); 
    g_Render::Instance().SetPerspectiveProjection(); 
} 
+0

puedo preguntar ¿Cómo renderizas esto en la ventana de OpenGL? – Elgoog

+0

Agregué la función DrawConsole a mi respuesta anterior. Tenga en cuenta que una vez más hay un poco de código que se vincula a otros lugares como mi clase de renderizado. – Brendan

2

Hay algunas cosas a esto. Primero quieres algún tipo de soporte de edición de línea. Hay bibliotecas para esto, por ejemplo, la línea de edición de NetBSDhttp://www.thrysoee.dk/editline/

Luego, de alguna manera, necesita procesar las pulsaciones de teclas. Ahora aquí es donde comienza la diversión. En lugar de tratar de procesar los eventos clave directamente, los alimentaría en una tubería anónima creada usando el pipe en (POSIX)/CreatePipe en Windows. Luego en el otro extremo puede leerlos, como si vinieran desde stdin.Un segundo tubo anónimo dobla la función de stdout y obtiene su resultado en la consola del juego. Llamaría al par resultante de FDs consolein y consoleout. También agregaría un consoleerr FD para los mensajes de error urgentes; la consola puede mostrarlos en otro color o filtrarlos.

Lo bueno de este enfoque es que puede usar todas las características de la biblioteca estándar para hablar con su consola. Puede usar fprintf(consoleout, ...), fscanf(consolein, ...) y así sucesivamente; también funciona con C++ iostreams, por supuesto. Pero lo más importante es que puede adjuntarlo directamente a bibliotecas como la ya mencionada línea de edición.

Finalmente, debe procesar los comandos que el usuario ha escrito en la consola. Ahí iría por la ruta perezosa y solo insertaría un intérprete de lenguaje de scripting, uno que admite operaciones interactivas. Como Python, o muy extendido en todos los juegos, Lua. También puede implementar su propio intérprete de comandos, por supuesto.

+1

Esto es lo que estaba buscando, algunas aclaraciones sobre cómo hacerlo. Estoy muy interesado en la solución de tubería anónima que mencionas, parece que es el camino correcto a seguir. Simplemente no estoy seguro de cómo implementarlo, si pudiera dar un pequeño ejemplo de código en un sistema POSIX estaría muy agradecido. Gracias. – Elgoog

Cuestiones relacionadas