I am currently enrolled in a course doing OO with C++ at college. I have been assigned an assessment where using Visual Studio and DirectX, create a game of basically any sort of difficulty but I must use all aspects of OO.

I have an idea of what game sort of game I am going to create, here is a basic (really basic, as in - made in paint in 5 seconds) shot of what my game will look like.

[IMG]http://i17.photobucket.com/albums/b79/perthskateboarder/GameIdea.png[/IMG]

As you can probably see from the image, the user controlls the stick man and the aim is to collect the 3 tokens (red circles) and make it to the door without colliding with the monstors which just move horizontally and vertically.

How do you guys think I should set out my classes? I am thinking something like:
*clsParent - using polymorphism and inhertance to have this class draw all the sprites.
*clsWallH - have this class set the sprites and locations of all the horizontal walls.
*clsWallV - have this class set the sprites and locations of all the vertical walls.
*clsChar - have this class set the sprite and location and movement etc. of the character.
*clsToken - have this class the the sprite and locations and removal etc. of the tokens.
*clsMonstorB - have this class set the sprites and location of the blue monstor.
*clsMonstorO - have this class set the sprites and location of the orange monstor.

This is just my approx. 2 months of directx c++ experience talking. So I thought I would ask the proffesionals.

ANY help or advice is much appreciated.

Thanks alot,
Oliver.

For this game, I'd have a cGrid with arrays (or similar) of cTiles, each with a cImage and some flags indicating solidity. That way, your walls are just tiles with different images and a boolean state solid = true.

I'd subclass cTile to cSprite and cItem. I'd subclass cSprite to cEnemy and cPlayer, put automated control code in the cEnemy ( along with a vector of movement direction ), and I'd put manual control code in the player. Using the grid, you can work out whether the player or enemy is able to move into different positions... i.e, say the grid squares are 20x20 units, take the sprites global position, divide the topleft and bottomright points of the player by 20, chop off the fractional part, and the x and y are x and y indices into the grid, then you can see whether the player can or can't move into a given piece of space.

You can use the same principle to check player collisions with enemy and item sprites, by temporarily assigning sprites to cells in the grid.. in some circumstances it can be faster, however, in this circumstance, I'd say it'd be faster to check physical distances between each sprite every cycle.. if you had a bigger space with more sprites though, it might be faster to assign them into the grid. It will certainly be faster to store the walls into the grid; since the walls don't move. Instead of having to check player position against every wall cell, you only have to look at the tile that the player is about to move onto. So, I would make the walls as 'tiles' with solidity, and the enemies as 'sprites'.

Between the player and certain items ( i'd make the door an item ), you want to monitor 'objectives' i.e., your objective is to collect all tokens and reach the door, i suppose in that order. I'd make a few modifications to the cItem class, so that each cItem has a unique ID, and the player has a list of collected IDs. Each cItem can also have a list of required IDs, thus, the tokens would have no required IDs, and the door would have required IDs for the tokens. When the player touches an item, check the collected IDs of the player against the required IDs of the item, if they match, allow the player to collect the item.

In your 'game loop' you'll have something like:

grid.[B]draw[/B]
foreach enemy
  enemylocationtemp = enemy.location + enemy.direction
  if grid.[B]getcell([/B]enemylocationtemp[B])[/B].issolid
    enemy.direction *= -1
  else
    enemy.location = enemylocationtemp
  endif
  if enemy.location [B]distancebetween[/B] player.location is approx ( player.radius + enemy.radius )
    fail
  endif
  enemy.[B]draw[/B]
end foreach
foreach item
   if item.location [B]distancebetween[/B] player.location is approx ( player.radius + item.radius )
    if player [B]hasallitemsrequiredby[/B] item
      player.[B]additem([/B] item [B])[/B]
      item.[B]removefrommap[/B]
    endif
  endif
  item.[B]draw[/B]
end foreach
player.[B]draw[/B]
if player.[B]hasallitems[/B]
  win
endif

That's quite oversimplified, the player needs movement code, and personally, I would put the enemy movement code into the enemy class, and give each enemy a pointer to the grid and the player. Perhaps the same with the 'item' class. Alternatively, I'd give the enemy a pointer to the grid only, and the player a pointer to the enemies, and items, and make the player authoritative in each cycle [i.e, have the player check distances to items and enemies , and if neccessary, invoke 'fail' or 'win'] that way, it's easier to imagine multiple players. It's nice to give all sprites a 'run' and 'draw' method, and let them all do their own thing every cycle; this can be done easily, but you need to 'connect everything up' beforehand.

That's how I'd do it.

I've never workd with grids before and we haven't been taught them. Is there any tutorials on them that I could follow? That way looks alot easier if I knew how to do it, but I'm not sure i'll be able to. If that's really simplified, I have no chance.

Any links to gird or a tutorial that creates something somewhat like this?

Thanks alot for such quick reply,
Oliver.

This is quite comprehensive, but it doesn't appear specific to DX or any graphics API: http://www.gamedev.net/reference/articles/article728.asp

Some questions; does the game need to have 3D? You say DirectX, which implies 3D, but it wouldn't have to be 3D to be DirectX.. If not 3D, does DirectX include DirectDraw? From experience a long time ago, it's easier to make tile-based games ( with 2D graphics ) in DirectDraw than DirectX 3D.

Tile based engines are often used in environments where memory ( both in offscreen buffers and on disk ) is limited; traditionally, they have been used for 2D games, but the same principles can be used in 3D; since potential-or-impossible ( broadphase ) collision pruning is very easy when using tiles, and they generally favour games which are restricted to two dimensional movement, which this appears to be.

The tutorial link above uses multiple 2D arrays in the grid. I wouldn't recommend this. Use either a single 2D array of structures or pointers to structures; which store (if 3D) a reference/pointer/index to the model that should be drawn, or if 2D, a reference/pointer/index to the graphical tile to be drawn. Include the solidity flag in that structure. Don't use an obj array; since you only have about 6 'objects' ( enemies, players, tokens, and door ) although, perhaps, put the items ( tokens and door ) into an 'obj' array like that tut suggests; since their positions are only updated during initialization and when they are collected; but do not put the enemies or player into the grid's obj array; since their positions are updated frequently, and cleaning those objects out of their current cell(s) and putting them into new cell(s) every cycle would take longer than checking distances between the three of them every cycle. I'd actually NOT use a 2D array, I'd use a dynamically allocated 1D array with a row/column offset, so each tile is stored sequentially and an index is defined as ( y * gridWidth ) + x.. because then the map can easily be resized based on variables rather than hardcode; but, a 2D array is probably easier to work with.

The 'grid' itself is really simple; the math involved is simple to, and is explained in that article. Drawing the grid is a case of iterating through each cell in the grid, and drawing whatever 3D object / 2D image that the grid cell references at the 2D coordinates which are unique to that grid cell, psuedocode:

/*
gridWidth and gridHeight are the dimensions of the grid ( cell coords into the array ), and cellWidth and cellHeight are the width and height of each cell of the array ( in drawing coords ), thus width of the entire 'map' in drawing coords is gridWidth * cellWidth; likewise for height.
*/
for( x = 0; x < gridWidth; x ++ )
{
  for( y = 0; y < gridHeight; y++ )
  {
    Bitmap * bmp = gridcells[x][y].bitmap;
    drawBitmap( bmp, x * cellWidth, y * cellHeight, cellWidth, cellHeight );
  }
}

I can't remember any DirectX or DirectDraw and don't use MSWindows, but I'm assuming there's some function to draw bitmaps to screen/buffers ( there is in DirectDraw ), and that it's defined like:

drawBitmap( Bitmap * bitmap_to_draw, int left, int top, int width, int height );

In DirectX, it's harder, but still possible, because you have to draw little 3D objects ( pairs of triangles ) which are textured with the two halves of the bitmap that you want to draw.. At least, that's how I learnt to do it, which is why I said it's easier to 2D games in DirectDraw. If you have to actually make this game have 3D models in it; then you have no choice but to use DirectX; but in that case, you don't have to mess about with pairs of textured triangles, you have to mess about with huge arrays of textured triangles instead ^_-. The tile based stuff can still apply; just make sure each tile model is the scaled width and height of one tile, and place the origins of those models at the center of the grid cell that they are placed in.

Checking collisions between a sprite (player/enemy) and a tile (in the grid) is a case of examining the grid cell(s) that are closest to the sprite, using a pair of divisions to convert a position in 'drawing coords' to a position in 'cell coords'. Do that every time the sprite attempts to move (by calculating a temporary that shows the position (2D drawing coords) the sprite is about to occupy ( cell coords ), and checking the cells it would be in), if the sprite would move over a solid tile; do not update the sprites position to the temporary, if the sprite is ok ( non of those cells are solid ), set the sprites current position to the temporary position. Draw the sprites, in the same way as for the tiles, but the positions (drawing coords) aren't unique or based on cell column/row ids, they are probably stored in the structures/classes that represent the sprites.

To be honest; when using grids and cells; you're avoiding OO completely... you'd have a single struct for each cell, with a set of variables; no subclassing in the grid; because it would be unnecessary to do so.. You could though in theory, in that case, you could subclass tile to solid tile and walkable tile; put a virtual function bool canwalk() in the base class.. etc. You could also subclass to 'wall' and have a wall image drawn via a virtual void draw( ) method. Personally, I think OO would suck here. If you DON'T OO, you could load the whole array in a snap, directly from a binary file... College assignments suck.

Better scope for OO is in the parts with the sprites; where you can subclass into players and enemies, and in the items where you subclass into door or token...

Oh, no OO. It specifically says on our assignment sheet that we must. They way he has taught us I think he would like us to do it the way I was talking about as I haven't even heard about grids until just now.

Thank you very very much for your help Matt, but I think i'll have to do it the way he wants us to.

The problem I am encountering at the moment is that the Parent class has a draw function which draws all the sprites. The sprites are set within each individual class. I also have a set location funcion for each individual class which gets given to the draw function. So, how could I have the sprites be drawn in completely different locations, yet by using just the one draw function?

I was thinking something along the lines of:
When I'm setting the location, send through a variable which indicates which sprite is being drawn and then have IFs in the draw function for each sprite?

Is that logical or is that stupid?

Thanks,
Oblique.

I would think that each sprite/tile should 'have' a location, and 'have' or better yet 'know' an image/model to draw, that way, you can have single instances of each image, and one instance for every individual sprite/tile in game (where some may share a common image). In the sprite/tile's draw function, you could use the values in that location object to determine where to draw the sprite/tile. You don't necessarily need more than one instance of each distinct image/model; but you do need many instances of each sprite/tile and each location... The draw location should certainly be attached to the sprite/tile if not using a grid or other space simplification mechanism..

If you're having the parent class do the 'drawing' rather than calling 'sprite.draw()' for each sprite/tile, you'd need to pass or return it the location of each sprite/tile, yes, and possibly the model/image to draw if that is held in sprite/tile instances. That's not very OO though... Each instance is treated identically, regardless of its compound type..

Ifs? You shouldn't need many if's in the draw routines. 'Cept maybe for error checking.. If you mean ifs to check for collisions, you could do that while drawing, it means you don't have to use two loops over identical indices... it's not particularly good OO practice though, ideally you should have a draw and a run routine.. each one doing a specific, clearly defined thing.. run_and_draw() ?

>> a set location funcion for each individual class which gets given to the draw function
do you really mean given? like, as a function pointer? your draw function is doing way more than drawing... do you mean get_location is returned to the draw function? that would make more sense.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.