V22.0480 - ASSIGNMENT 3

Racquet-Pong (Version 2.0)


In the last assignment, we created Raquet-Pong, a simple game where a paddle was used to bounce a ball against the walls of an empty playing field. A common type of game (or at least it used to be common...) has the player bouncing a ball with a paddle to destroy a set of blocks.  In this assignment, we will extend the project to include additional features.  These include:

Requirements

Most of the requirements from the previous assignment still apply to this one.  In case it was not made clear in the previous assignment, you are required to compute the reflection off the paddle based on the collision point.

In this assignment, because the challenge will come from the block obstacles, you are not required to have the ball speed up after every hit.  You are also not required to keep track of the number of hits.

You must make the game work in fullscreen mode, at least at a 640x480 resolution.  You may implement support for additional resolutions, if you would like.  Also, it may be very useful for debugging to support both a fullscreen mode and a windowed mode, though this is not required.  The windowed mode will allow you to use the Visual Basic debugger at the same time.  Sample code to help you accomplish this is given in this page, below.

You should also refer to the Visual Basic Direct Draw Tutorial 3, located in

              samples\Multimedia\VBsamples\DirectDraw\Tutorials\Tut3

You may use that code as well (but if so, you must format according to the course's coding standards and put code in your own module or class (i.e. not in the form and without reference to media.bas):

In fullscreen mode, you will use page flipping, rather than blitting, to copy the back buffer to the front.  This is more efficient, especially at high resolutions, as the graphics card only needs to swap two pointers, rather than copy potentially millions of bytes.  Check the lecture notes (Class Notes 6) and Direct Draw Tutorial 3 as well (paying special attention to use of the .Flip method).

While sound was optional for the last assignment, it is now required.  You need to have sound effects for each event (at least collision with wall, collision with paddle, collision with block, end of game).  See the previous writeup for detailed instructions on using sound.  You may also (recommended) use the Sounds Manager (CSound and CSoundManager) code in the sounds.zip file on the course home page (some slight modification may be necessary to fit it in your own project).

The biggest addition to this assignment is that your playing field must be filled with a 10x10 grid of 5 types of blocksWhen the ball hits a block, a sound is played, the block is destroyed and the ball reflects in a new direction.  When all the blocks are destroyed, the player moves to the next level.  There are some sample screenshots below from the game Arkanoid that should give you the idea (ignore the grey and brown blocks for now (see Extra Credit)).

Each individual level will be specified in a level file format (with the extension .lvl).   You need to create at least 3 different level files for your game, with the ability to add more.  Please put some time and creativity into these, as effort will be part of your grade.  (As with the artwork, finish all of the code first before putting too much time in here).  The lines will specify the blocks in the field, assuming an 10 x 10 grid.  Each one of 10 lines will have 10 characters, where each character represents another type of block.  You will leave a space when there is no block at a particular location.

You must support at least the following block types, with a grid size of 10x10 (if you want, you may support additional grid sizes). We will use our own map files to test your programs, and so they will need to support this minimum specification.

A,B,C,D,E - various colored blocks

To create a stair-like pattern, as in the first screenshot above, an example level file might be:

A
AB
ABC
ABCD
ABCDA
ABCDAB
ABCDABC
ABCDABCD
ABCDABCDA

Note, we've left out the last row- (see extra credit).

You must load levels from the current directory.   Your program must select one of the files on startup.  On completion of a level, you must proceed through the rest of of the levels in the directory.  See the writeup for homework 3 for examples of accessing files from a directory.  If the balls drops off the bottom, you should restart the level.  If you would like to put in a more complete scoring mechanism, such a a certain number of lives or chances for the player, after which point the level will reset, you are encouraged (though not required) to do so.

Implementation Hints

Code for implementing fullscreen mode and page flipping have been provided by the instructor in the lecture notes.   To support both fullscreen and windowed mode, you will set the cooperative level differently, and create the buffers differently, as shown below.

Dim sfcPrimary As DirectDrawSurface7, sfcBack As DirectDrawSurface7
Dim dsfc As DDSURFACEDESC2, dsfcPrimary As DDSURFACEDESC2, dsfcBack As DDSURFACEDESC2
If bFullScreen = True Then 'you set this boolean to false when debugging

    'Switch into fullscreen mode and set parameters (640x480x32bpp)
    objDD.SetCooperativeLevel frmMain.hWnd, DDSCL_FULLSCREEN Or DDSCL_EXCLUSIVE
    objDD.SetDisplayMode 640, 480, 32, 0, DDSDM_DEFAULT

    'Create a complex primary surface that has 1 attached back buffer, and supports flipping
    dsfcPrimary.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
    dsfcPrimary.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE OrDDSCAPS_FLIP Or DDSCAPS_COMPLEX 
    dsfcPrimary.lBackBufferCount = 1
Set sfcPrimary = objDD.CreateSurface(dsfcPrimary)

    'Set a reference to the attached back buffer
    dsfc.ddsCaps.lCaps = DDSCAPS_BACKBUFFER
Set sfcBack = sfcPrimary.GetAttachedSurface(dsfc.ddsCaps)

Else
 
    'Indicate normal (windowed) mode only
    objDD.SetCooperativeLevel frmMain.hWnd, DDSCL_NORMAL

    'Get a handle to the primary buffer
    dsfcPrimary.lFlags = DDSD_CAPS
    dsfcPrimary.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE
    Set sfcPrimary = objDD.CreateSurface(dsfcPrimary)

    'Create a back buffer with the specified size
    dsfcBack.lFlags = DDSD_CAPS Or DDSD_WIDTH Or DDSD_HEIGHT
    dsfcBack.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN
    dsfcBack.lWidth = 640
    dsfcBack.lHeight = 480
    Set sfcBack = objDD.CreateSurface(dsfcBack)

    'Set up a clipper object for the front buffer
    Set objClipper = g_objDirectDraw.CreateClipper(0)
    objClipper.SetHWnd frmMain.hWnd
    sfcPrimary.SetClipper objClipper

 
End If

At the end of the frame, when you are finished drawing to the back buffer, you need to transfer the back buffer to the front buffer.  In windowed mode, you blt from back to front as before.  In fullscreen mode, you simply call sfcPrimary.Flip.  Other than that all code can be identical.
A reasonable technique for implementing the blocks is to store a 2-dimensional array that represents the grid of blocks on the board, along with an array of sprite objects for each type of block (not for each individual block).  A number indicates which type of block is present for each grid cell (or zero for destroyed/non-existent).  Cycle through the array and for each block that has not been destroyed, render the sprite for that block type, at the correct position.

You will also have to extend your collision detection to test each block.  A simple method would be to use the RectanglesIntersect function discussed in the lecture notes (Class Notes 6) to see if the bounding box of the ball overlaps with the bounding box of all blocks that have not been destroyed.  As soon as you find an overlap, you have a hit.  You then destroy the block and then reflect the ball off the block.

You may iterate through all the .lvl files as in Homework 2.  To load a given level file, one solution is to open the file as a text stream (using the File System Objects) and read in the file character by character.  Each time you see the end of a line, you start another row.  The following code snippet will loop through the file given in sLevelFile, and will load the array iBlocks with the ASCII value of each character in the file.  You may want your code to act in a slightly different manner.

Dim objFso As New FileSystemObject
Dim objStream As TextStream
Dim iBlocks(10, 10) As Integer
Dim sBlock As String
Dim iRow As Integer, iCol As Integer

'Open the sLevelFile for reading, do not create if non-existant, open in ASCII mode
Set objStream = objFso.OpenTextFile(sLevelFile, ForReading, False, TristateFalse)

'Start off at row 0
iRow = 0

'Loop until we hit the end of the file
While Not objStream.AtEndOfStream
     'For each new row, reset the column to 0
     iCol = 0

     'Loop until we hit the end of the line (which ends the row)
     While Not objStream.AtEndOfLine
          'Read in the next charecter.  Copy ASCII value into iBlocks
          sBlock = objStream.Read(1)
          iBlocks(iRow, iCol) = Asc(Left(sBlock, 1))

          'Move to the next column
          iCol = iCol + 1
     Wend

     'Skip the carriage return and newline charecters, and go to next row
     objStream.SkipLine
     iRow = iRow + 1
Wend

Extra Credit

Add ball-aiming off paddle and additional block types (must do both for 10 Points total):

The first part of the extra credit was described in the last assignment: you are to vary the reflection of the ball depending on what part of the paddle it hits.  For example, if the ball hits the middle of the paddle, it will bounce straight up.  If it hits the left edge, it will bounce back sharply to the left, and so on for the right edge. If it hits anywhere else on the paddle the angle will likewise be determined by the description in the last assignment.  The purpose is to allow aiming of the ball.

The second part involves adding two additional block types to be used in your assignment, the Hard block type and the Invincible block type.  Recall that a colored block above will be destroyed upon one hit.  The Hard block, however, takes multiple hits to be destroyed.  When the ball hits a Hard block (grey color in the screenshots above), the ball reflects off of it- after the Hard block has been hit enough times, the block is destroyed. The Invincible block type is completely indestructible (for example, the brown blocks in the third screenshot above).  These indestructible blocks increase the challenge dramatically; for example, in the third screenshot the player is forced to bounce the ball into the narrow hole at the top of the field.  Needless to say, Invincible blocks do not need to be destroyed to complete the level.

A Hard block should take a large number of hits (for now 3 hits), and an Invincible block needs to be unbreakable.

So if you do the extra credit you will have the following block types:

A,B,C,D,E - various colored blocks

H - hard block (should take a large number of hits)

I - invincible block (unbreakable)

To create a stair-like pattern with the Hard blocks, as in the first screenshot above, an example level file might be:

A
AB
ABC
ABCD
ABCDA
ABCDAB
ABCDABC
ABCDABCD
ABCDABCDA
HHHHHHHHHA

Note: You might want to modify your collision detection scheme so when you hit a block you apply damage to the block, and destroy it if necessary.

We would like to encourage those that finish early to go above and beyond the basic requirements, and here are some options for additional credit.  If someone has an interesting idea for a feature that they would REALLY like to work on, contact the TA at cdecoro@cat.nyu.edu regarding extra credit for that idea.

Possible ideas include:

Submision Instructions