This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  In-Game Command Console
  Submitted by



Here is my submission for code of the day. It's a class for an in-game command console that I've developed for my current DirectX 3D engine project. You can see the console in action ( along with the rest of the engine ) at http://tatooine.fortunecity.com/falcon/183/stoom2.htm ( shameless plug !!! )

To use call Initialize() once and then call Render() every frame. Call OnChar() when WM_CHAR is received and OnKeyUp() when WM_KEYUP is received.

I must apologise for it not being very portable code. You _WILL_ have to make changes to implement it in your own application. Firstly, it makes use of the D3DFrame library via a global variable g_pFramework which needs to be initialised beforehand. Secondly, it makes use of the MFC classes CSize, CString & CstringList. You may wish/need to a) Replace the framework library functions with your own methods, b) replace CString/CStringList with char * c) replace the entire Render() function to better suit your own needs /application. d) you will need a .bmp containing the background image and a .bmp containing the font - Chars in ascii order of size FONT_WIDTH x FONT_HEIGHT.

You can use this code wherever and for whatever you like, I hope someone finds it useful.

Steve.

Currently browsing [console.zip] (5,010 bytes) - [Console.h] - (1,873 bytes)

#if !defined(AFX_CONSOLE_H__B0C8B741_A776_11D3_A33F_8CED61F5D517__INCLUDED_)
#define AFX_CONSOLE_H__B0C8B741_A776_11D3_A33F_8CED61F5D517__INCLUDED_

#if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // Console.h : header file // #define STATIC 1 #define UP 2 #define DOWN 4 #define FONT_HEIGHT 16 #define FONT_WIDTH 16 #define DISPLAY_FONT_HEIGHT 8 #define DISPLAY_FONT_WIDTH 8 #define UMAX 16 #define VMAX 32 #define COMP 0.000001 #define FLASH_SPEED 500 #define MAX_SIZE 64 #define XOFF 10 #define YOFF 5

struct console { char *command; // Command char *help; // Optional help test void (*handler)(char *string); // Function hadler };

/* Console prototypes */ void ConsoleCullbackfaces(char * command = NULL); void ConsoleGamma(char* command = NULL);

///////////////////////////////////////////////////////////////////////////// // CConsole window class CConsole : public CWnd { public: CConsole(); virtual ~CConsole(); void OnChar(char key); void OnKeyUp(int key); void FormatLine(char *text,...); void DisplayLine(char *text); void Restore(); void Render(); void Initialize(); void ProccessCommand(); void ProccessTab(); void Close();

bool m_initialised; D3DTLVERTEX m_v[4]; LPDIRECT3DTEXTURE2 m_font, m_background; char *m_BackgroundName, *m_FontName; int m_nLines; CStringList m_text, m_CommandHistory; CString m_InputBuffer; CRect m_size; float m_yOffset; DWORD m_mode; bool m_bCursor; int m_FlashCounter; POSITION m_HistoryPos; int m_CursorPos; int m_TabCount; CString m_PrevInput; CSize m_FontSz;

// Generated message map functions protected: };

#endif // !defined(AFX_CONSOLE_H__B0C8B741_A776_11D3_A33F_8CED61F5D517__INCLUDED_)

Currently browsing [console.zip] (5,010 bytes) - [Console.cpp] - (14,924 bytes)

// Console.cpp : implementation file
//

#include "Console.h"

#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif

/* List of commands the console supports. */ /* Commands are of the form : <Command text><Help text (optional)><handler function> */ struct console CommandList[] = { {"CullBackFaces","...[0/1]", ConsoleCullBackFaces}, {"Gamma","...........[0..100]", ConsoleGamma}, {NULL,NULL,NULL} };

//----------------------------------------------------------------------------- // Function handler for the gamma command void ConsoleGamma(char *command) { if ( command == NULL ) { /* Just display the current value on the console */ theApp.m_console.FormatLine("gamma=%d",theApp.m_GammaVal); return; }

/* Set the new gamma value */ theApp.m_GammaVal = atol(command); theApp.SetGammaCorrection((float)theApp.m_GammaVal / 100.0f ); }

//----------------------------------------------------------------------------- void ConsoleCullBackFaces(char * command) { if ( command != NULL ) { switch ( command[0] ) { case '0': theApp.m_dwFlags &= ~CULL_BACK_FACES; theApp.SetOptions(); return;

case '1': theApp.m_dwFlags |= CULL_BACK_FACES; theApp.SetOptions(); return; } }

/* Toggle */ theApp.m_dwFlags ^= CULL_BACK_FACES; theApp.SetOptions(); }

///////////////////////////////////////////////////////////////////////////// // CConsole CConsole::CConsole() { m_initialised = false; m_FontName = "font4.bmp"; m_BackgroundName = "console2.bmp"; m_mode = DOWN; m_HistoryPos = NULL; m_TabCount = 0; }

CConsole::~CConsole() { m_initialised = false; D3DTextr_DestroyTexture(m_FontName); D3DTextr_DestroyTexture(m_BackgroundName); }

//----------------------------------------------------------------------- // Initialize the console. Call before use or after the render device has changed //----------------------------------------------------------------------- void CConsole::Initialize() { DDSURFACEDESC2 ddsd; HDC hdc;

if ( !g_pFramework || !g_pFramework->GetD3DDevice() || !g_pFramework->GetDirect3D()) return; // Just in case... m_FontSz.cy = DISPLAY_FONT_HEIGHT; m_FontSz.cx = DISPLAY_FONT_WIDTH;

/* Calculate the size of the cosoole ( when fully visible ) */ ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT; g_pFramework->GetBackBuffer()->GetSurfaceDesc(&ddsd); m_size.SetRect(0,0,ddsd.dwWidth,ddsd.dwHeight / 2);

/* The vertices of the quad that will be uses for the background */ m_v[0] = D3DTLVERTEX(D3DVECTOR(0,0,1),1,D3DRGB(1,1,1),D3DRGB(1,1,1),0,0); m_v[1] = D3DTLVERTEX(D3DVECTOR(0,0,0),1,D3DRGB(1,1,1),D3DRGB(1,1,1),0,1); m_v[2] = D3DTLVERTEX(D3DVECTOR((float)ddsd.dwWidth,0,0),1,D3DRGB(1,1,1),D3DRGB(1,1,1),1,1); m_v[3] = D3DTLVERTEX(D3DVECTOR((float)ddsd.dwWidth,0,1),1,D3DRGB(1,1,1),D3DRGB(1,1,1),1,0); m_v[0].color = m_v[1].color= m_v[2].color = m_v[3].color = D3DRGBA(1,1,1,0.8);

/* Load the font "texture" to an off-screen surface*/ D3DTextr_CreateTexture( m_FontName , 0, 0 ); D3DTextr_Restore(m_FontName,g_pFramework->GetD3DDevice()); m_font = D3DTextr_GetTexture(m_FontName); DDSetColorKey(D3DTextr_GetSurface(m_FontName),RGB(0,0,0));

/* Load the background image to an off-screen surface */ D3DTextr_CreateTexture( m_BackgroundName , 0, 0 ); D3DTextr_Restore( m_BackgroundName,g_pFramework->GetD3DDevice()); m_background = D3DTextr_GetTexture( m_BackgroundName);

/* Number of visible lines */ m_nLines = (int)(((float)((ddsd.dwHeight) / 2.0) - YOFF - YOFF ) / m_FontSz.cy ) - 1; /* Create a blank empty text buffer */ for ( int i = m_text.GetCount(); i < m_nLines; i++ ) m_text.AddHead("");

/* Initialize some other member variables */ m_bCursor = true; m_FlashCounter = 0; m_yOffset = 0; m_mode = DOWN; m_CursorPos = 0;

m_initialised = true; }

//----------------------------------------------------------------------- // Render the console. Call this every frame. //----------------------------------------------------------------------- void CConsole::Render() { POSITION pos; int top; CString str; char *p; int x, y, c, len; D3DTLVERTEX v[4]; ULONG lOldZFunc, loldColor, loldAlpha, loldSrc, loldDest; float uoffset, voffset, usize = (1 / ( 256.0 / FONT_WIDTH)), vsize = (1 / ( 256.0 / FONT_HEIGHT)); bool done = false; static DWORD LastTime = timeGetTime(); DWORD ThisTime = timeGetTime(); DWORD elapsed = ThisTime - LastTime; HDC hdc = NULL; CDC dc; CFont *old;

if ( !m_initialised ) return;

/* 'Animation' stuff */

m_FlashCounter += elapsed; if ( m_FlashCounter > FLASH_SPEED ) { m_FlashCounter = 0; m_bCursor = m_bCursor ? false : true; }

if ( m_yOffset == 0 ) elapsed = 1;

LastTime = ThisTime; switch ( m_mode ) { case DOWN: // The console is scrolling down m_yOffset += (float)elapsed / 2.0; if ( m_yOffset > m_size.bottom ) { m_yOffset = m_size.bottom; m_mode = STATIC; }

/* fade in the console as is scrolls down */ alpha = m_yOffset / m_size.bottom; if ( alpha < 0.8f ) m_v[0].color = m_v[1].color= m_v[2].color = m_v[3].color = D3DRGBA(1,1,1,alpha);

break;

case UP: // The console is scrolling up m_yOffset -= elapsed; if ( m_yOffset < 0 ) { m_yOffset = 0; m_mode = DOWN; }

/* fade out the console as is scrolls up */ alpha = m_yOffset / m_size.bottom; if ( alpha >= 0 ) m_v[0].color = m_v[1].color= m_v[2].color = m_v[3].color = D3DRGBA(1,1,1,alpha); break;

case STATIC: // The console is fully visible break; }

/* Setup some render states */ LPDIRECT3DDEVICE3 pDevice = g_pFramework->GetD3DDevice();

pDevice->GetRenderState( D3DRENDERSTATE_ZFUNC, &lOldZFunc ); pDevice->SetRenderState( D3DRENDERSTATE_ZFUNC, D3DCMP_ALWAYS );

pDevice->GetTextureStageState( 1, D3DTSS_COLOROP,&loldColor ); pDevice->SetTextureStageState( 1, D3DTSS_COLOROP,D3DTOP_DISABLE );

pDevice->GetTextureStageState( 1, D3DTSS_ALPHAOP,&loldAlpha ); pDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,D3DTOP_DISABLE );

pDevice->GetRenderState( D3DRENDERSTATE_SRCBLEND,&loldSrc); pDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND,D3DBLEND_SRCALPHA);

pDevice->GetRenderState( D3DRENDERSTATE_DESTBLEND,&loldDest ); pDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND,D3DBLEND_INVSRCALPHA );

pDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 ); pDevice->SetTexture(0, m_background);

/* Draw the background (Transparent quad) */ m_v[0].sy = m_yOffset - m_size.bottom; m_v[1].sy = m_yOffset; m_v[2].sy = m_yOffset; m_v[3].sy = m_yOffset - m_size.bottom;

pDevice->DrawPrimitive(D3DPT_TRIANGLEFAN,D3DFVF_TLVERTEX ,(LPVOID)m_v,4,NULL);

/* Draw the text. This should be done using a vertex buffer & DrawIndexedPrimitiveVB()!!! */ pDevice->SetTexture(0, m_font); top = m_text.GetCount() - m_nLines; pos = m_text.FindIndex(top); x = XOFF; y = (m_yOffset - m_size.bottom) + YOFF;

while ( !done ) { if ( pos == NULL ) { str = m_InputBuffer; done = true; } else str = m_text.GetNext(pos);

len = str.GetLength();

p = str.GetBuffer(0);

for ( c = 0, x = XOFF; c < len; c++, x += DISPLAY_FONT_WIDTH,p++ ) { uoffset = (*p % UMAX ) * usize; voffset = (*p / UMAX ) * vsize;

v[0] = D3DTLVERTEX(D3DVECTOR(x,y,1),1,D3DRGB(1,1,1),D3DRGB(1,1,1),uoffset + COMP,voffset + COMP); v[1] = D3DTLVERTEX(D3DVECTOR(x,y + DISPLAY_FONT_HEIGHT ,0),1,D3DRGB(1,1,1),D3DRGB(1,1,1),uoffset + COMP,voffset + vsize - COMP - COMP); v[2] = D3DTLVERTEX(D3DVECTOR(x + DISPLAY_FONT_WIDTH,y + DISPLAY_FONT_HEIGHT,0),1,D3DRGB(1,1,1),D3DRGB(1,1,1),uoffset + usize - COMP - COMP ,voffset + vsize - COMP - COMP); v[3] = D3DTLVERTEX(D3DVECTOR(x + DISPLAY_FONT_WIDTH,y,1),1,D3DRGB(1,1,1),D3DRGB(1,1,1),uoffset + usize - COMP - COMP,voffset + COMP);

pDevice->DrawPrimitive(D3DPT_TRIANGLEFAN,D3DFVF_TLVERTEX ,(LPVOID)v,4,NULL); }

y += m_FontSz.cy; }

/* Draw the cursor */ if ( m_bCursor ) { y -= m_FontSz.cy; x = (m_FontSz.cx * m_CursorPos) + XOFF;

uoffset = ('I' % UMAX ) * usize; voffset = ('I' / UMAX ) * vsize;

v[0] = D3DTLVERTEX(D3DVECTOR(x,y,1),1,D3DRGB(1,1,1),D3DRGB(1,1,1),uoffset + COMP,voffset + COMP); v[1] = D3DTLVERTEX(D3DVECTOR(x,y + DISPLAY_FONT_HEIGHT ,0),1,D3DRGB(1,1,1),D3DRGB(1,1,1),uoffset + COMP,voffset + vsize - COMP - COMP); v[2] = D3DTLVERTEX(D3DVECTOR(x + DISPLAY_FONT_WIDTH,y + DISPLAY_FONT_HEIGHT,0),1,D3DRGB(1,1,1),D3DRGB(1,1,1),uoffset + usize - COMP - COMP ,voffset + vsize - COMP - COMP); v[3] = D3DTLVERTEX(D3DVECTOR(x + DISPLAY_FONT_WIDTH,y,1),1,D3DRGB(1,1,1),D3DRGB(1,1,1),uoffset + usize - COMP - COMP,voffset + COMP);

pDevice->DrawPrimitive(D3DPT_TRIANGLEFAN,D3DFVF_TLVERTEX ,(LPVOID)v,4,NULL); }

/* restore the device render states */ pDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND,loldSrc); pDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND,loldDest); pDevice->SetTextureStageState( 1, D3DTSS_COLOROP,loldColor); pDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,loldAlpha); pDevice->SetRenderState( D3DRENDERSTATE_ZFUNC,lOldZFunc);

}

//----------------------------------------------------------------------- // Call this when the device nees to be restored void CConsole::Restore() { D3DTextr_Restore(m_FontName,g_pFramework->GetD3DDevice()); m_font = D3DTextr_GetTexture(m_FontName); DDSetColorKey(D3DTextr_GetSurface(m_FontName),RGB(0,0,0));

D3DTextr_Restore( m_BackgroundName,g_pFramework->GetD3DDevice()); m_background = D3DTextr_GetTexture( m_BackgroundName); }

//----------------------------------------------------------------------- // Function to add a line to the console. Use in the same way as printf // eg "console.FormatLine("Loaded texture %s",texturename)"; void CConsole::FormatLine(char *format, ...) { va_list arg; char buffer[256];

va_start(arg, format); vsprintf(buffer, format, arg); va_end(arg);

DisplayLine(buffer);

}

//----------------------------------------------------------------------- // Function to display a line of text void CConsole::DisplayLine(char *text) { m_text.AddTail(text);

if ( m_text.GetCount() > MAX_SIZE ) m_text.RemoveHead(); }

//----------------------------------------------------------------------- // Function to handle a key press void CConsole::OnChar(char character) { if ( character != VK_TAB) m_TabCount = 0;

switch ( character ) { case '`': case '~': if ( m_mode == STATIC ) { m_InputBuffer.Empty(); m_CursorPos = 0; m_mode = UP; } break;

case VK_ESCAPE: if ( m_InputBuffer.IsEmpty() ) m_mode = UP; else { m_InputBuffer.Empty(); m_CursorPos = 0; }

break;

case VK_BACK: if ( !m_InputBuffer.IsEmpty()) { m_InputBuffer.Delete(m_CursorPos - 1); m_CursorPos--; } break;

case VK_RETURN: ProccessCommand();

break;

case VK_TAB: ProccessTab(); break;

default: if ( m_CursorPos < m_InputBuffer.GetLength() ) { m_InputBuffer.Insert(m_CursorPos,character); } else m_InputBuffer += character;

m_CursorPos++;

} }

//----------------------------------------------------------------------- // Function to handle a key press void CConsole::OnKeyUp(int key) { POSITION pos = NULL;

if ( key != VK_TAB ) m_TabCount = 0;

switch ( key ) { case VK_UP: if ( m_HistoryPos == NULL ) m_HistoryPos = m_CommandHistory.GetHeadPosition(); else m_CommandHistory.GetNext(m_HistoryPos);

if ( m_HistoryPos ) m_InputBuffer = m_CommandHistory.GetAt(m_HistoryPos); else m_HistoryPos = m_CommandHistory.GetTailPosition();

m_CursorPos = m_InputBuffer.GetLength(); break;

case VK_DOWN: if ( m_HistoryPos ) { m_CommandHistory.GetPrev(m_HistoryPos);

if ( m_HistoryPos == NULL ) m_HistoryPos = m_CommandHistory.GetHeadPosition();

m_InputBuffer = m_CommandHistory.GetAt(m_HistoryPos); }

m_CursorPos = m_InputBuffer.GetLength();

break;

case VK_LEFT: if ( m_CursorPos > 0 ) m_CursorPos--; break;

case VK_RIGHT: if ( m_CursorPos < m_InputBuffer.GetLength() ) m_CursorPos++; break;

case VK_DELETE: if ( m_CursorPos < m_InputBuffer.GetLength() ) { m_InputBuffer.Delete(m_CursorPos,1); } break;

case VK_HOME: m_CursorPos = 0; break;

case VK_END: m_CursorPos = m_InputBuffer.GetLength(); break;

}

}

//----------------------------------------------------------------------- // The main part of the console!! This examines the keyed input and // calls the appropriate function handler void CConsole::ProccessCommand() { struct console *pCommand = CommandList; char CommandLine[128], *p; int n = m_InputBuffer.Find(' ');

if ( m_InputBuffer.IsEmpty() ) { DisplayLine(""); return ; }

m_InputBuffer.TrimRight();

/* Update history */ m_CommandHistory.AddHead(m_InputBuffer); if ( m_CommandHistory.GetCount() > MAX_SIZE ) m_CommandHistory.RemoveHead();

m_HistoryPos = NULL;

strcpy(CommandLine,m_InputBuffer); m_InputBuffer.Empty(); m_CursorPos = 0;

while ( pCommand->command && pCommand->handler ) { if ( strnicmp(CommandLine,pCommand->command,n) == 0) { /* Display the command */ DisplayLine(CommandLine);

/* Get the option */ p = strtok(CommandLine," "); p = strtok(NULL," ");

/* Handle the command */ pCommand->handler(p); return; } pCommand++; }

/* Not found a match in the command list */ FormatLine("Unknown command :\"%s\".",CommandLine); DisplayLine( "Type HELP for list ");

}

//----------------------------------------------------------------------- // Enable the TAB key to complete the current command void CConsole::ProccessTab() { struct console *pCommand = CommandList; int match = 0;

if ( m_TabCount == 0 ) m_PrevInput = m_InputBuffer;

while ( pCommand->command && pCommand->handler ) { if ( strnicmp(m_PrevInput,pCommand->command,m_PrevInput.GetLength()) == 0) { m_InputBuffer = pCommand->command; m_CursorPos = m_InputBuffer.GetLength();

if ( m_TabCount <= match ) { m_TabCount = match + 1; return; } match++; }

pCommand++; } }

//----------------------------------------------------------------------- // Close the console void CConsole::Close() { m_yOffset = 0; m_mode = DOWN; }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.