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.