2012-06-12 5 views
9

Meine Anwendung erfordert eine Konsole embeded innerhalb des Anwendungsfensters, ein Beispiel wäre in einem Programm wie AutoCAD, wo die Konsole am unteren Rand des Fensters auf Befehle wartet.Eine Konsole innerhalb einer Anwendung schreiben

enter image description here

Ich brauche die Konsole in meiner Anwendung, so dass ich Variablen und andere Dinge ändern kann, so dass die Konsole benötigt keine vollständig ausgeblasen Schal zu sein.

Im Moment habe ich eine einfache Konsole in meiner Anwendung, aber es scheint sehr klobig im Vergleich zu einem Terminal (Shell), das ist, was ich will, dass die Konsole wie sein.

enter image description here

Die Art und Weise, dass ich es mit der Konsole getan habe, ich habe, ist, wenn der Benutzer die TAB Taste der Konsole zeigt drückt, dann können sie ihren Befehl/Zeile eingeben; Sobald der Schlüssel Return gedrückt wurde, wird die Zeichenfolge, die sie eingegeben haben, geparst und der Befehl wird verarbeitet.

Ich verwende sf::Text Objekte, um Text in meinem Anwendungsfenster auszudrucken. Es gibt insgesamt 5 sf::Text Objekte, die verwendet werden, 4 für die vorherigen Befehle/Fehlermeldungen und 1 für die aktuelle Befehlszeile. Wenn die Taste Return gedrückt wird, ändert die 4. sf::Text ihren aktuellen String auf den 3., den 3. auf den 2., den 2. auf den 1. und den 1. auf den aktuellen Befehlsstring, dann wird der aktuelle Befehlsstring gelöscht und ist wieder zur Eingabe bereit. Auf diese Weise gibt es Platz für 4 'Geschichte' von Befehlen und/oder Fehlern. Nicht das Beste, aber es war das Beste, was ich mir vorstellen konnte. Natürlich könnte die Menge der Historie durch Hinzufügen von weiteren sf::Text Objekten geändert werden. Also am Ende das ist, wie ich die Konsole mit der

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 ist nur ein sf::RenderWindow*

Mein Gesamt Frage ist, Gibt es eine Möglichkeit Bildschirm machen, dass ich eine Konsole in meine SFML einbetten Fenster, ohne dass es einfach ein Bild von Textobjekten sein muss, die wie eine Konsole wie oben aussehen. Ich würde lieber eine tatsächliche Konsole/Shell/Terminal in meiner Anwendung haben. Wie die Standard-Bash-Shell, aber natürlich mein eigener Shell-Interpreter.

Antwort

-1

Wenn Sie Ihre eigene benutzerdefinierte Konsole implementieren, dann müssen Sie das selbst schreiben. Ein schneller Skim von the SMFL docs schlägt vor, dass nichts eingebaut ist, um Ihnen zu helfen. Dies ist nur eine einfache und schnelle Multimedia-Bibliothek ;-)

Wenn Sie etwas mehr Funktionen benötigen, würde ich vorschlagen, in OGRE suchen.

+0

Ich bin nicht nach sfml, um es für mich zu tun, ich habe sfml als Tag und in den Codebeispielen hinzugefügt, um zu zeigen, dass ich sfml verwendet habe. Ich weiß, dass ich es selbst schreiben muss, was ich will, ist eine Richtung, wie es geht. Wie andere Leute eine Konsole in ihre Anwendung geschrieben haben. : D – Elgoog

+0

Ich würde vorschlagen, Ihre Frage in einige kleinere Teile zu verfeinern, sobald Sie identifiziert haben, was Sie erreichen möchten. Was sind Ihre Anforderungen? Versuchen Sie, das in Quake oder Unreal Tournament oder was auch immer Spiel, das Sie interessiert sind. –

+0

Wolfire entwickelt etwas basierend auf Webkit anscheinend: http://www.youtube.com/watch?v=c-4WdtTGIkg&feature=relmfu –

0

Nun, was Sie wahrscheinlich wollen, wenn Sie es wollen, wie eine Konsole mehr spüren ist:

  • der Lage, es auf wechseln und mit einem Knopfdruck, wahrscheinlich so etwas wie ~ aus, die verwendet wird, ein Menge.
  • Geben Sie die Zeile, die Sie eingeben, auf eine Hintergrundfarbe, vielleicht transparent, aber stellen Sie sicher, dass nicht nur Text auf dem RenderWindow schwebt. Wenn die Ausgabe des Befehls aus mehreren Zeilen besteht, stellen Sie sicher, dass sie alle sichtbar sind oder dass die Benutzer zumindest durch den Verlauf scrollen können.
  • Stellen Sie sicher, dass die Befehle einfach zu verstehen und konsistent sind. Zum Beispiel, wenn ich mich nicht irre, verwenden viele Spiele auf der Quell-Engine das Präfix cl_ für alles, was das Rendering betrifft. Siehe zum Beispiel cl_showfps 1.
  • Traditionelle Terminal-Eingang wäre eine nette Geste. Nach oben zeigt Ihnen den vorherigen Befehl, den Sie ausgefüllt haben. Wenn Sie sich abenteuerlustig fühlen, verwenden Sie Tab zur Vervollständigung.
  • Wenn Sie noch etwas Zeit haben, wäre auch eine Möglichkeit, die verfügbaren Befehle z. B. durch --help anzuzeigen, nett. Je nachdem wie kompliziert dein Spiel ist natürlich.

Für den Rest, schau dir an, wie andere Spiele dies getan haben. Du hast Quake erwähnt, das ein großartiges Beispiel für ein Ingame-Terminal hat. Ich für meinen Teil denke, dass das eine in vielen Source-Spielen auch einfach zu benutzen ist (siehe Half Life 2, Counter Strike Source, Team Fortress 2, Left 4 Dead, etc.). Ich denke nicht, dass es dafür Standard-Bibliotheken gibt, die kein anderes Framework wie OGRE oder IrrLicht enthalten.

4

Ich habe das Folgende als Konsole für ein OpenGL-Spiel implementiert, das ich vor einiger Zeit geschrieben habe. Es ist keineswegs eine endgültige Antwort auf Ihre Frage, aber es hat für mich funktioniert und Sie könnten etwas nützliches daraus bekommen.

Die 2 Dateien sind an dieser Unterseite dieses Beitrags. Es ist unwahrscheinlich, dass der Code direkt ausgeführt wird, da es einen für 2 Bibliotheksheaderdateien gibt, die ich nicht einschließen werde. Wenn Sie die vollständige Quelle wollen, lassen Sie es mich wissen.

Grundsätzlich ermöglicht die Konsolenklasse das Hinzufügen von Variablenzeigern, die zur Laufzeit geändert werden können. Es akzeptiert Eingaben von den Windows-Ereignisnachrichten. (Die eigentliche Eingabe erfolgt an anderer Stelle.) Das Parsen der Befehle erfolgt in der ProcessInput() -Methode, und Variablen werden in der ChangeVariable() -Methode aktualisiert.

Ein Wort der Warnung. Diese Methode gibt den Konsolenbenutzern im Wesentlichen direkten Zugriff auf die Speicherplätze der einzelnen Variablen. Dies erfordert eine gute Eingabeüberprüfung, um sicherzustellen, dass der Benutzer die Anwendung nicht zur Laufzeit zum Absturz bringen kann. Wenn ich mich jemals hinsetzen würde und versuchen würde, eine andere Konsole zu machen, würde ich die Dinge wahrscheinlich etwas anders machen. Ich hoffe aber, dass dir das ein wenig hilft.

Die Header-Datei:

#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 

Die CPP-Datei:

#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; 
} 

Edit 1: Added DrawConsole() ich den Text von der Konsole Klasse bekommen nur ein Bild machen, das aussah, Ähnlich wie das Quellengine-Konsolenfenster, das in jedem neueren Ventilspiel gefunden wurde, und dann wird der Text an den entsprechenden Stellen gezeichnet.

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

kann ich fragen Wie renderst du das in das OpenGL-Fenster? – Elgoog

+0

Ich habe die DrawConsole-Funktion zu meiner obigen Antwort hinzugefügt. Denken Sie daran, dass es wieder eine Menge Code gibt, der mit anderen Orten wie meiner Renderklasse verknüpft ist. – Brendan

2

Es gibt ein paar Dinge zu diesem Thema. Zuerst möchten Sie eine Art von Zeilenbearbeitung unterstützen. Es gibt Bibliotheken dafür, zum Beispiel NetBSDs editlinehttp://www.thrysoee.dk/editline/

Dann müssen Sie irgendwie Tasten drücken. Jetzt beginnt hier der Spaß. Anstatt zu versuchen, die Schlüsselereignisse direkt zu verarbeiten, füge ich sie in eine anonyme Leitung ein, die unter Verwendung der pipe on (POSIX)/CreatePipe unter Windows erstellt wurde. Am anderen Ende können Sie sie lesen, als kämen sie von stdin.Eine zweite anonyme Pipe verdoppelt die Funktion stdout und zeigt ihre Ausgabe auf der In-Game-Konsole an. Ich würde das resultierende Paar von FDs consolein und consoleout nennen. Ich würde auch eine consoleerr FD für dringende Fehlermeldungen hinzufügen; Die Konsole kann sie in einer anderen Farbe anzeigen oder filtern.

Das schöne an diesem Ansatz ist, dass Sie alle netten Standardbibliotheksfunktionen verwenden können, um mit Ihrer Konsole zu sprechen. Sie können fprintf(consoleout, ...), fscanf(consolein, ...) und so weiter verwenden; Es funktioniert natürlich auch mit C++ iostreams. Aber noch wichtiger, Sie können es direkt an Bibliotheken wie die oben genannten Editline anhängen.

Schließlich müssen Sie die Befehle verarbeiten, die der Benutzer in die Konsole eingegeben hat. Dort würde ich den faulen Weg gehen und nur einen Skriptsprachen-Interpreter einbetten, der die interaktive Bedienung unterstützt. Wie Python, oder sehr weit verbreitet in Spielen, Lua. Sie können natürlich auch einen eigenen Befehlsinterpreter implementieren.

+1

Dies ist, was ich suchte, einige Erläuterungen darüber, wie es geht. Ich bin sehr an der anonymen Rohrlösung interessiert, die Sie erwähnen. Es scheint der richtige Weg zu sein. Ich bin mir nur nicht sicher, wie ich es implementieren soll. Wenn Sie ein kleines Codebeispiel auf einem POSIX-System geben könnten, wäre ich sehr dankbar. Vielen Dank. – Elgoog