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.  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.  This was described in the last assignment.  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.  The purpose is to allow aiming of the ball

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.

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.

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, end of game) as well as background music during the game.  See the previous writeup for detailed instructions on using sound.

You will be required to create or find your own custom artwork for the game.  Please put some thought and creativity into it, and try to fit the visuals with the sound to make a compelling user experience.  Animation would be nice, though not required.  Effort will be a part of your final grade (though you should finish all the code and functionality first).

The biggest addition is that your playing field must be filled with a 10x10 grid of 7 types of blocks.  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.  When the ball hits a block, it is reflected off, and if that block has been hit enough times, the block is destroyed.  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.

  

Some of the blocks are destroyed with one hit (the colored blocks).  Some are destroyed with multiple hits (the grey blocks), and others are completely indestructible (the brown blocks in the third image).  These indestructible blocks increase the challenge dramatically, as the player is forced to bounce the ball into the narrow hole at the top of the field (needless to say, they do not need to be destroyed to complete the level).

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 charecters, where each charecter 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).  The details of what each block looks like and how it acts are mostly up to you.  The exceptions are that a hard block should take a large number of hits (the number is up to you), and an invincible block needs to be unbreakable. We will use our own map files to test your programs, and so they will need to support this minimum specification.  You can also swap these with other students in the class and try each other's levels in your own game.

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, as in the left screenshot, an example level file might be:

A
AB
ABC
ABCD
ABCDA
ABCDAB
ABCDABC
ABCDABCD
ABCDABCDA
HHHHHHHHHA

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 sfcFront As DirectDrawSurface7, sfcBack As DirectDrawSurface7
Dim
dsfc As DDSURFACEDESC2, dsfcFront As DDSURFACEDESC2, dsfcBack As DDSURFACEDESC2
If bFullScreen Then

    '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
    dsfc.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
    dsfc.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX    
    dsfc.lBackBufferCount = 1 
    Set sfcFront = objDD.CreateSurface(dsfc)

    '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
    dsfcFront.lFlags = DDSD_CAPS 
    dsfcFront.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE
    Set sfcFront = objDD.CreateSurface(g_sdPrimary)

    '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 sfcFront.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 instructor's IsectRect function (discussed in the lecture notes) 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 apply damage to the block, destroy it if necessary, 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 charecter by charecter.  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 charecter 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

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