2012-06-12 3 views
9

Мое приложение требует, чтобы консоль была в вложенной в окне приложения, пример был бы в такой программе, как autoCAD, где консоль находится в нижней части окна, ожидая команд.Написание консоли в приложении

enter image description here

мне нужна консоль в моем приложении, так что я могу изменить переменные и другие вещи, так что консоль не нуждаясь быть полностью выдувается оболочкой.

На данный момент у меня есть простая консоль в моем приложении, но она кажется очень неуклюжей по сравнению с терминалом (оболочкой), что и является желанием консоли.

enter image description here

То, как я сделал это с консолью у меня есть, когда пользователь нажимает TAB ключ на консоль показывает, то они могут ввести в свою команду/строку; После нажатия клавиши Return строка, которую они набрали, обрабатывается и команда обрабатывается.

Я использую sf::Text объекты для распечатки текста в окне приложения. В общей сложности используются 5 sf::Text объектов, 4 для предыдущих команд/сообщений об ошибках и 1 для текущей командной строки. Когда клавиша Return нажата, четвертый sf::Text изменяет свою текущую строку на 3-й, 3-й на 2-й, 2-й и 1-й на текущую командную строку, после чего текущая строка команд очищается и снова готова к вводу. Таким образом, есть место для 4 'истории' команд и/или ошибок. Не самое большое, но это было лучшее, что я мог придумать. Конечно, количество истории можно было бы изменить, добавив еще sf::Text объектов. Таким образом, в конце концов, это, как я сделать консоль на экран

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 просто sf::RenderWindow*

Мой общий вопрос, Есть ли способ, что я могу встроить консоль в мой SFML window без необходимости просто быть образ текстовых объектов, которые выглядят как консоль, как я уже говорил выше. Я бы предпочел иметь реальную консоль/оболочку/терминал в своем приложении. Как и стандартная оболочка bash, но, конечно, мой собственный интерпретатор оболочки.

ответ

-1

Если вы реализуете свою собственную консоль, то вам придется писать эту партию самостоятельно. Быстрый снимок the SMFL docs предполагает, что нет ничего, что могло бы вам помочь. Это просто простая и быстрая мультимедийная библиотека ;-)

Если вам нужно что-то более полнофункциональное, я бы предложил посмотреть на OGRE.

+0

Я не после sfml, чтобы сделать это для меня, я добавил sfml в качестве тега и в примерах кода, чтобы показать, что я использовал sfml. Я знаю, что я должен написать это сам, то, что мне нужно, - это руководство о том, как это сделать. Как другие люди начали писать консоль в своем приложении. : D – Elgoog

+0

Я бы предложил уточнить ваш вопрос на несколько более мелких частей, как только вы определили, чего именно вы хотите достичь. Каковы ваши требования? Попробуйте использовать ту, что есть в Quake или Unreal Tournament или в любой другой игре, в которой вы заинтересованы. –

+0

Вольфрих разработал что-то, основанное на Webkit, по-видимому: http://www.youtube.com/watch?v=c-4WdtTGIkg&feature=relmfu –

0

Ну, что вы, вероятно, хотите, если вы хотите, чтобы чувствовать себя как консоли:

  • Будучи в состоянии переключить его включения и выключения с одним нажатием, вероятно, что-то вроде ~, который используется много.
  • Дайте строку, которую вы печатаете на цвет фона, возможно, прозрачный, но, по крайней мере, убедитесь, что это не только текст, плавающий на RenderWindow. Если вывод команды несколько строк, убедитесь, что они все видны или люди могут, по крайней мере, прокручивать историю.
  • Убедитесь, что команды понятны и понятны. Например, если я не ошибаюсь, многие игры на исходном движке используют префикс cl_ для всего, что связано с рендерингом. См. Например, cl_showfps 1.
  • Традиционный вход терминала будет приятным прикосновением. Вверх показывает вам предыдущую команду, которую вы заполнили. Возможно, если вы чувствуете себя авантюрно, используйте Tab для завершения.
  • Если у вас есть какое-то время, способ показать доступные команды через --help, например, также будет приятным. В зависимости от сложности вашей игры, конечно.

Для остальных посмотрим, как это сделали другие игры. Вы упомянули Quake, который имеет отличный пример игрового терминала. Я считаю, что один из многих исходных игр также прост в использовании (см. Half Life 2, Counter Strike Source, Team Fortress 2, Left 4 Dead и т. Д.). Я не думаю, что для этого есть стандартные библиотеки, не считая других фреймов, таких как OGRE или IrrLicht.

4

Я реализовал следующее как консоль для игры opengl, которую я писал некоторое время назад. Это отнюдь не окончательный ответ на ваш вопрос, но он работал для меня, и вы могли бы извлечь из этого что-то полезное.

2 файла находятся внизу этого сообщения. Код вряд ли будет работать напрямую, поскольку есть один для 2 файлов заголовков библиотеки, которые я не буду включать. Если вам нужен полный источник, дайте мне знать.

В принципе, хотя класс консоли позволяет добавлять к нему переменные указатели, которые можно изменять во время выполнения. Он принимает входные данные из сообщений событий Windows. (Фактическая обработка ввода выполняется в другом месте) Разбор команды выполняется в методе ProcessInput(), а переменные обновляются в методе ChangeVariable().

Слово предупреждения. Этот метод предоставляет пользователям консоли прямой доступ к ячейкам памяти отдельной переменной. Это требует хорошей проверки ввода, чтобы убедиться, что пользователь не может заставить приложение сбой во время выполнения. Если бы я когда-нибудь сел и попытался создать другую консоль, я бы, скорее всего, делал все немного иначе. Однако я надеюсь, что это немного поможет.

Заголовочный файл:

#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 

Файл каст:

#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: Добавлено DrawConsole() Я просто получить текст из класса консоли рендеринга изображения, который выглядел аналогично окну консоли исходного ядра, найденному в любой недавней игре с клапаном, а затем текст набирается в соответствующих местах.

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

Могу ли я спросить как вы визуализируете это в окне OpenGL? – Elgoog

+0

Я добавил функцию DrawConsole к моему ответу выше. Имейте в виду, что еще раз есть довольно много кода, который ссылается на другие места, такие как класс рендеринга. – Brendan

2

Есть несколько вещей. Сначала вам нужна поддержка редактирования строк. Для этого существуют библиотеки, например, NetBSD's editlinehttp://www.thrysoee.dk/editline/

Тогда вам как-то нужно обработать нажатие клавиш. Теперь здесь начинается веселье. Вместо того чтобы пытаться напрямую обрабатывать ключевые события, я бы их подавал в анонимный канал, созданный с помощью pipe (POSIX)/CreatePipe в Windows. Затем на другом конце вы можете прочитать их, как если бы они вошли с stdin.Вторая анонимная труба удваивает функцию stdout и получает ее вывод, отображаемый на игровой консоли. Я бы назвал полученную пару FD consolein и consoleout. Я также добавил бы consoleerr FD для срочных сообщений об ошибках; консоль может отображать их в другом цвете или фильтровать их.

Приятная вещь в этом подходе заключается в том, что вы можете использовать все удобные стандартные функции библиотеки, чтобы разговаривать с вашей консолью тогда. Вы можете использовать fprintf(consoleout, ...), fscanf(consolein, ...) и так далее; Конечно, он также работает с C++ iostreams. Но что более важно, вы можете напрямую прикрепить его к библиотекам, например, вышеупомянутой editline.

Наконец, вам нужно обработать команды, введенные пользователем в консоли. Там я бы пошел на ленивый маршрут и просто вставлял интерпретатор языка сценариев, поддерживающий интерактивную работу. Как Python, или очень распространенный во всех играх, Lua. Конечно же, вы можете реализовать собственный интерпретатор команд.

+1

Это то, что я был после, некоторые разъяснения о том, как это сделать. Меня очень интересует анонимное решение для труб, которое вы упомянули. Кажется, это правильный путь. Я просто не уверен, как его реализовать. Если бы вы могли дать небольшой пример кода в системе POSIX, я был бы очень благодарен. Спасибо. – Elgoog