History: OgreConsole
Source of version: 5 (current)
Copy to clipboard
{maketoc}
!!Description
A graphical ingame console is an essential part of any game. Today I've desided to put together this code for a simple graphical console. This console class is a singleton class that renders a console on the auto created window. To make things simple, it takes a single parameter in the ''init()'' method and retreives all other things from the root (including scene manager and rendering window). To pull down the console you just call ''setVisible(true)'' and you call ''setVisible(false)'' to hide it. The console automatically animates when it shows and hides. That is done automatically by the console class since it registers itself as a frame listener. It also registers itself as a log listener so that it can display log messages.
Here is how the console looks ingame:
{img src="img/wiki_up/Screenshot.png" alt="screenshot.png"}
!!Usage
In your scene initialisation, you can add something like this:
{CODE(wrap="1", colors="c++")} new OgreConsole;
OgreConsole::getSingleton().init(mRoot);
OgreConsole::getSingleton().setVisible(true);
OgreConsole::getSingleton().addCommand("quit",&CMD_Quit);
OgreConsole::getSingleton().addCommand("screenshot",&CMD_Screenshot);
{CODE}
Now you will just have to feed the input events to the ''onKeyPressed()'' method to get the console working properly. The console will not handle input event when it is closed.
!!ogreconsole.h
{CODE(wrap="1", colors="c++")} #pragma once
#include <OgreFrameListener.h>
#include <Ogre.h>
#include <OIS/OIS.h>
#include <list>
#include <vector>
using namespace Ogre;
using namespace std;
class OgreConsole: public Singleton<OgreConsole>, FrameListener, LogListener
{
public:
OgreConsole();
~OgreConsole();
void init(Ogre::Root *root);
void shutdown();
void setVisible(bool visible);
bool isVisible(){return visible;}
void print(const String &text);
virtual bool frameStarted(const Ogre::FrameEvent &evt);
virtual bool frameEnded(const Ogre::FrameEvent &evt);
void onKeyPressed(const OIS::KeyEvent &arg);
void addCommand(const String &command, void (*)(vector<String>&));
void removeCommand(const String &command);
//log
void messageLogged( const String& message, LogMessageLevel lml, bool maskDebug, const String &logName ) {print(logName+": "+message);}
private:
bool visible;
bool initialized;
Root *root;
SceneManager *scene;
Rectangle2D *rect;
SceneNode *node;
OverlayElement *textbox;
Overlay *overlay;
float height;
bool update_overlay;
int start_line;
list<String> lines;
String prompt;
map<String,void (*)(vector<String>&)> commands;
};
{CODE}
!!ogreconsole.cpp
{CODE(wrap="1", colors="c++")} #include "Console.h"
template<> OgreConsole *Singleton<OgreConsole>::ms_Singleton=0;
#define CONSOLE_LINE_LENGTH 85
#define CONSOLE_LINE_COUNT 15
OgreConsole::OgreConsole(){
start_line=0;
}
OgreConsole::~OgreConsole(){
}
void OgreConsole::init(Ogre::Root *root){
if(!root->getSceneManagerIterator().hasMoreElements())
OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "No scene manager found!", "init" );
this->root=root;
scene=root->getSceneManagerIterator().getNext();
root->addFrameListener(this);
height=1;
// Create background rectangle covering the whole screen
rect = new Rectangle2D(true);
rect->setCorners(-1, 1, 1, 1-height);
rect->setMaterial("console/background");
rect->setRenderQueueGroup(RENDER_QUEUE_OVERLAY);
rect->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE));
node = scene->getRootSceneNode()->createChildSceneNode("#Console");
node->attachObject(rect);
textbox=OverlayManager::getSingleton().createOverlayElement("TextArea","ConsoleText");
textbox->setCaption("hello");
textbox->setMetricsMode(GMM_RELATIVE);
textbox->setPosition(0,0);
textbox->setParameter("font_name","Console");
textbox->setParameter("colour_top","1 1 1");
textbox->setParameter("colour_bottom","1 1 1");
textbox->setParameter("char_height","0.03");
overlay=OverlayManager::getSingleton().create("Console");
overlay->add2D((OverlayContainer*)textbox);
overlay->show();
LogManager::getSingleton().getDefaultLog()->addListener(this);
}
void OgreConsole::shutdown(){
if(!initialized)
return;
delete rect;
delete node;
delete textbox;
delete overlay;
}
void OgreConsole::onKeyPressed(const OIS::KeyEvent &arg){
if(!visible)
return;
if (arg.key == OIS::KC_RETURN)
{
//split the parameter list
const char *str=prompt.c_str();
vector<String> params;
String param="";
for(int c=0;c<prompt.length();c++){
if(str[c]==' '){
if(param.length())
params.push_back(param);
param="";
}
else
param+=str[c];
}
if(param.length())
params.push_back(param);
//try to execute the command
map<String,void(*)(vector<String>&)>::iterator i;
for(i=commands.begin();i!=commands.end();i++){
if((*i).first==params[0]){
if((*i).second)
(*i).second(params);
break;
}
}
print(prompt);
prompt="";
}
if (arg.key == OIS::KC_BACK)
prompt=prompt.substr(0,prompt.length()-1);
if (arg.key == OIS::KC_PGUP)
{
if(start_line>0)
start_line--;
}
if (arg.key == OIS::KC_PGDOWN)
{
if(start_line<lines.size())
start_line++;
}
else
{
char legalchars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890+!\"#%&/()=?[]\\*-_.:,; ";
for(int c=0;c<sizeof(legalchars);c++){
if(legalchars[c]==arg.text){
prompt+=arg.text;
break;
}
}
}
update_overlay=true;
}
bool OgreConsole::frameStarted(const Ogre::FrameEvent &evt){
if(visible&&height<1){
height+=evt.timeSinceLastFrame*2;
textbox->show();
if(height>=1){
height=1;
}
}
else if(!visible&&height>0){
height-=evt.timeSinceLastFrame*2;
if(height<=0){
height=0;
textbox->hide();
}
}
textbox->setPosition(0,(height-1)*0.5);
rect->setCorners(-1,1+height,1,1-height);
if(update_overlay){
String text;
list<String>::iterator i,start,end;
//make sure is in range
if(start_line>lines.size())
start_line=lines.size();
int lcount=0;
start=lines.begin();
for(int c=0;c<start_line;c++)
start++;
end=start;
for(int c=0;c<CONSOLE_LINE_COUNT;c++){
if(end==lines.end())
break;
end++;
}
for(i=start;i!=end;i++)
text+=(*i)+"\n";
//add the prompt
text+="] "+prompt;
textbox->setCaption(text);
update_overlay=false;
}
return true;
}
void OgreConsole::print(const String &text){
//subdivide it into lines
const char *str=text.c_str();
int start=0,count=0;
int len=text.length();
String line;
for(int c=0;c<len;c++){
if(str[c]=='\n'||line.length()>=CONSOLE_LINE_LENGTH){
lines.push_back(line);
line="";
}
if(str[c]!='\n')
line+=str[c];
}
if(line.length())
lines.push_back(line);
if(lines.size()>CONSOLE_LINE_COUNT)
start_line=lines.size()-CONSOLE_LINE_COUNT;
else
start_line=0;
update_overlay=true;
}
bool OgreConsole::frameEnded(const Ogre::FrameEvent &evt){
return true;
}
void OgreConsole::setVisible(bool visible){
this->visible=visible;
}
void OgreConsole::addCommand(const String &command, void (*func)(vector<String>&)){
commands[command]=func;
}
void OgreConsole::removeCommand(const String &command){
commands.erase(commands.find(command));
}
{CODE}
!!console.material
{CODE(wrap="1", colors="c++")} material console/background
{
technique
{
pass
{
scene_blend alpha_blend
diffuse 0 0 1 0.5
}
}
}
{CODE}
And finally the font:
!!console.fontdef
{CODE(wrap="1", colors="c++")} Console
{
type truetype
source console.ttf
size 32
resolution 55
}
{CODE}
You can find the font file [http://fixedsys.moviecorner.de/?p=download&l=1|here].
!!TODO
* Command history
* Cursor (I haven't figured out yet how to draw underlined characters)
* Colored text quake style with ^# prefixes to indicate colors (I haven't figured this out either; the only thing I can do right now is to set the color of the whole textarea and not individual characters in it) - see [http://www.ogre3d.org/wiki/index.php/ColoredTextAreaOverlayElement|this wiki article]
* Support for command callbacks as pointers to member functions of a class (boost?)
!!About
This OgreConsole was originally posted by PixL in this Ogre Forum topic:
[http://www.ogre3d.org/phpBB2/viewtopic.php?p=189903|Ogre graphical console]
!!New version
A newer GUI toolkit called ((Gorilla|Gorilla)) now includes a [http://www.ogre3d.org/forums/viewtopic.php?f=11&t=59984|new version of this console]. It's the same code as shown in this article, but ported to a new and more expansive GUI system.