See what's going on with flipcode!




 

Creating A Quake-Like Console
Question submitted by (29 November 1999)




Return to The Archives
 
  I'm a medium experienced programmer and I would like to implement a Quake-like Console in my games. A console, just like Quake I, II and III, linux, etc have, is a very powerful an cool thing, it provides a lot of user-input, it's great for debugging and gives your application a professional look. Until now I've tried some things but none of them seem to work ( except a huge "switch" or "case" statement, witch I don't want to implement ). I'm not interested in the graphical implementation ( the font engine, animation, stuff like that ), I want to know how the console works, how it interacts with the game functions and variables, the procedure's calling, and passing their arguments, the command history and the automatic recognition of commands when the user types only the first letters and presses a key, like TAB ( all the people who played Quake know what I'm talking about ).  
 

 
  I agree, a switch/case would be a nasty way to go (even though case statements don't allow strcmps :)

Common ways of doing this are by simply creating a list of structures that might look like this:


struct	console
{
	char	*command;
	bool	(*handler)(char *string);
};
 


In the above structure, we've got a string which represents the string command, and a function pointer to the routine that is responsible for handling the command.

We create a list that might look like this:


stuct	console	commandList[] =
{
	{"showFrameRate",           showFrameRate},
	{"showStats",               showStats},
	{"enableFog",               enableFog},
	{"killerLeemursOnTheLoose", killerLeemursOnTheLoose},
	{NULL, NULL} // list termination
};
 


Once a command comes in, simply scan the list and find a matching command:


struct	console	*ptr = commandList;
while(ptr->command && ptr->handler) // searches until the end is found
{
	if (!strcmp(ptr->command, userInputCommand))
	{
		ptr->handler(userInputCommand);
	}

ptr++; }


Here, we loop through the list, find the command, and call the handler with the command line that the user entered as the parameter. Doing it this way means that ALL of the handlers have the same function prototype.

Once we're in the handler, we are responsible for interrogating the options. This way, the options can be in any format (and the format can change from command to command.) But to keep things simple, we'll implement a handy routine that will parse our command string for us. Here's an example handler:


void	showFrameRate(char *command)
{
	char	*option = getOption(command, 1);
	frameRateDisplayFlag = *option == '1';
}
 


Given this handler, if the user types 'showFrameRate 1' then they will enable the frame rate display.

The getOption routine would simply parse the command string for space-delimeted pieces of the string and return the one requested.

If you want to have handy command recollection, simply store the previous commands in a buffer and scroll through them with the proper keypress. If they hit TAB, you might find out how many characters they typed in so far, and scan the list of commands for a match on that many characters, then you can type the rest in for them. You might want to get tricky with this, and keep track of the last index into the command list that was found for the last TAB keypress. This way, the next time they press TAB, you can continue searching where you left off. This would allow them to scan all of the commands that begin with a series of letters (like all the commands that begin with 'show').

The rest of the code involved in making the text entry system work cleanly is always a hassle to write. But the logic is never anything too difficult for a moderate level programmer.



Response provided by Paul Nettle
 
 

This article was originally an entry in flipCode's Fountain of Knowledge, an open Question and Answer column that no longer exists.


 

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