# A better way to do this?

This topic is 3035 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello, I've just finished reading a C++ Programming book, and thought of testing my newly earned skills in making a small game so I decided to make Pacman. But I have a question. I have simulated the maze using a 2-Dimensional array like this:
unsigned char maze[24][21] = {
{'_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_'},
{'_','1','1','1','1','1','1','1','1','1','_','1','1','1','1','1','1','1','1','1','_'},
{'_','4','_','_','_','1','_','_','_','1','_','1','_','_','_','1','_','_','_','4','_'},
{'_','1','_','_','_','1','_','_','_','1','_','1','_','_','_','1','_','_','_','1','_'},
{'_','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','_'},
{'_','1','_','_','_','1','_','1','_','_','_','_','_','1','_','1','_','_','_','1','_'},
{'_','1','_','_','_','1','_','1','1','1','_','1','1','1','_','1','_','_','_','1','_'},
{'_','1','1','1','1','1','_','_','_','2','_','2','_','_','_','1','1','1','1','1','_'},
{'_','_','_','_','_','1','_','2','2','2','2','2','2','2','_','1','_','_','_','_','_'},
{'2','2','2','2','_','1','_','2','_','_','1','_','_','2','_','1','_','2','2','2','2'},
{'_','_','_','_','_','1','_','2','_','2','2','2','_','2','_','1','_','_','_','_','_'},
{'5','5','5','5','5','1','2','2','_','2','2','2','_','2','2','1','5','5','5','5','5'},
{'_','_','_','_','_','1','_','2','_','_','_','_','_','2','_','1','_','_','_','_','_'},
{'2','2','2','2','_','1','_','2','2','2','2','2','2','2','_','1','_','2','2','2','2'},
{'_','_','_','_','_','1','_','2','_','_','_','_','_','2','_','1','_','_','_','_','_'},
{'_','1','1','1','1','1','1','1','1','1','_','1','1','1','1','1','1','1','1','1','_'},
{'_','1','_','_','_','1','_','_','_','1','_','1','_','_','_','1','_','_','_','1','_'},
{'_','4','1','1','_','1','1','1','1','1','2','1','1','1','1','1','_','1','1','4','_'},
{'_','_','_','1','_','1','_','1','_','_','_','_','_','1','_','1','_','1','_','_','_'},
{'_','1','1','1','1','1','_','1','1','1','_','1','1','1','_','1','1','1','1','1','_'},
{'_','1','_','_','_','_','_','_','_','1','_','1','_','_','_','_','_','_','_','1','_'},
{'_','1','_','_','_','_','_','_','_','1','_','1','_','_','_','_','_','_','_','1','_'},
{'_','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','_'},
{'_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_'}};
and here is the definition of each element of the array:
enum MazeID
{
OBSTACLE    = '_',
BEANS       = '1',
NO_BEANS    = '2',
POWERBEAN   = '4',
TUNNEL      = '5',
};
Now i want to make a level designer so i wanted to parse and display the maze dynamically, so if i change anything in the maze i wouldn't have to change any of the code. so i draw it like this:
void Maze::Draw()
{
LOGPEN lPen = {0};
lPen.lopnColor = 0x00C00000;
lPen.lopnWidth.x = 5;
lPen.lopnWidth.x = 5;
HPEN hPen = CreatePenIndirect(&lPen);
HPEN hOldPen = (HPEN)::SelectObject(m_hDC, hPen);

unsigned int startingPointX = 0;
unsigned int startingPointY = 0;

unsigned int blocksize = 21; // width and height of the pacman/ghost sprite

for(int i = 0; i < 24; ++i)
{
for(int j = 0; j < 21; ++j)
{
startingPointX = (j+1) * blocksize; // for each OBSTACLE element it increases the horizontal line by blocksize(21)
startingPointY = (i+1) * blocksize; // for each OBSTACLE element it increases the vertical line by blocksize(21)

// draw obstacles
if(m_Maze[j] == OBSTACLE)
{
// draw horizontal obstacles only if next horizontal block has 0
if(m_Maze[j+1] == OBSTACLE)
{
MoveToEx(m_hDC, startingPointX, startingPointY, 0);
LineTo(m_hDC, startingPointX + blocksize, startingPointY);
}

// draw vertical obstacles only if next vertical block has 0
if(m_Maze[i+1][j] == OBSTACLE)
{
MoveToEx(m_hDC, startingPointX, startingPointY, 0);
LineTo(m_hDC, startingPointX, startingPointY + blocksize);
}

}
}
}

SelectObject(m_hDC, hOldPen);
DeleteObject(hPen);
}
However this works just fine but i feel its ugly. so is there any better way to achieve this? Any comment would be highly appreciated. Please let me know if there anything unclear in the above ugly code. And by the way, what books related to Game Programming do you recommend? Regards

##### Share on other sites
To be honest that's about as good as the code is ever going to look. Data loading code isn't really something that can be made to look elegant, it's generally just kinda laborious.

##### Share on other sites

You could load that data as strings (omitting the extra cluttery commas and single quotes) and have every other character be a space/blank to try to get a reasonable aspect to the grid appearance (for readability)

You could use char values that mnemonicly match the actual map tiles:

O B N P T (maybe the obstacle could stay - and the NO_BEAN as a blank

You would then read those strings into your working array converting them to whatever was most efficient ( ordinals rather than chars... but for this it probably doesnt make a difference )

"----------------------------------------------"
"--B B B B B B P B B B B --
"-- ---------- -------------- ---------- --"
etc....

##### Share on other sites
Here's some ideas.

class Maze {	enum TileType	{		SPACE,		OBSTACLE,		BEANS,		POWERBEAN,		TUNNEL	};		const int BLOCKSIZE = 21; // Should this be in a seperate graphics class?		int m_width, m_height;	std::vector<TileType> m_map; // Use a std::vector to hold the map		Maze(int width, int height) : m_width(width), m_height(height), m_map(width * height) {} // The map vector starts empty (all tiles set to SPACE)		TileType TileAt(int x, int y, TileType outOfBounds = SPACE) const	{		return x >= 0 && y >= 0 && x < m_width && y < m_height ? m_map[x + y * m_width] : outOfBounds; // If the co-ord is in range, look in the map, otherwise return a default value	}			// ...	void Draw(HDC hDC) const // Pass in the DC instead of making it part of Maze state, and be const-correct	{		HPEN hPen = CreatePen(PS_SOLID, 5, RGB(0, 0, 0xC0)); // Don't use indirect function, and use proper macros and enums		HPEN hOldPen = reinterpret_cast<HPEN>(SelectObject(hDC, hPen)); // Use C++ casts, and no need to explicitly specify scope (unless you really do have a symbol clash)				HBRUSH hObstacleBrush = CreateSolidBrush(RGB(0, 0, 0x30)); // Brush for filling obstacles		for(int y = 0; y < m_height; ++y) // Enumerate over whatever dimensions are needed, don't hardcode them in			for(int x = 0; x < m_width; ++x)			{				const RECT rcTile = {x * BLOCKSIZE, y * BLOCKSIZE, (x + 1) * BLOCKSIZE, (y + 1) * BLOCKSIZE}; // For easy GDI operations								switch(TileAt(x, y)) // Use an accessor method to get tiles at locations				{					case SPACE:						break;										case OBSTACLE:						FillRect(hDC, &rcTile, hObstacleBrush);						MaybeDrawObstacleWall(hDC, x - 1, y,     rcTile.left,  rcTile.top,    rcTile.left,  rcTile.bottom);						MaybeDrawObstacleWall(hDC, x + 1, y,     rcTile.right, rcTile.top,    rcTile.right, rcTile.bottom);						MaybeDrawObstacleWall(hDC, x,     y - 1, rcTile.left,  rcTile.top,    rcTile.right, rcTile.top   );						MaybeDrawObstacleWall(hDC, x,     y + 1, rcTile.left,  rcTile.bottom, rcTile.right, rcTile.bottom);						break;										// Other tile type cases here...				}			}				DeleteObject(hObstacleBrush);		SelectObject(hDC, hOldPen);		DeleteObject(hPen);	}		void MaybeDrawObstacleWall(HDC hDC, int nx, int ny, int xStart, int yStart, int xEnd, int yEnd) const	{		if(TileAt(nx, ny) != OBSTACLE)		{			MoveToEx(hDC, xStart, yStart, NULL);			LineTo(hDC, xEnd, yEnd);		}	}}

##### Share on other sites
Thank you so much mattd, Winegums and wodinoneeye.