Paper Escaper!

This was my third year game design course's final project. I developed it with a friend. The game has the player trapped inside a book, where pages are constantly falling. Each page has in it a randomly located hole which allows the player to survive to the next page. The goal of the game is to last as long as possible without getting squished by the falling page. The game was developed in C++, using the DirectX graphics framework. This was the first 3D game I had made. The goal was to make the game get harder as time went on, such as making the pages fall faster, but it didn't end up getting implemented.

In second year we had learned to make 2D games in DirectX, and the switch to 3D wasn't so difficult. The main issues were figuring out how to position the camera, as well as understanding how the transformation matrices worked. The hardest part of the implementation of the game was creating the see-through hole that the player uses. Making a randomly located circle on the texture wasn't difficult, the difficult part was making it see through. In this implementation there was no background created. If there were to be one, it would look very strange if the hole was simply a black circle. There were many attempts to get this to work, and after weeks of trying a solution was found. The internet seemed to imply that the easiest method was to use shaders, but I didn't understand shaders very well at the time. Here is a code snippet of how the hole was made see through. The comments explain what is happening.

Your screen resolution is too small to display the program code

        /**
         * Loops through the hole texture and sets the alpha values
         * of a random spot of the sheet to be the color values of the hole
         *
         * Meaning that where the hole is black, the sheet will be transparent
         * and white the sheet is visible
         *
         * Does it for both the front texture and back texture so it is fully see through
         */
        void PaperSheet::createAlphaMask()
        {
            D3DLOCKED_RECT frontRec, backRec, holeRec;
            D3DSURFACE_DESC frontDesc, backDesc, holeDesc;
            IDirect3DSurface9 *frontSurf, *backSurf, *holeSurf;

            frontTexture->GetSurfaceLevel(0, &frontSurf);
            backTexture->GetSurfaceLevel(0,   &backSurf);
            holeTexture->GetSurfaceLevel(0,  &holeSurf);

            frontSurf->GetDesc( &frontDesc);
            backSurf->GetDesc( &backDesc);
            holeSurf->GetDesc( &holeDesc);

            UINT frontWidth = frontDesc.Width;
            UINT frontHeight = frontDesc.Height;

            UINT backWidth = backDesc.Width;
            UINT backHeight = backDesc.Height;

            UINT holeWidth = holeDesc.Width;
            UINT holeHeight = holeDesc.Height;

            // Needs to be the same in order to make sense, if wrong do nothing
            if (frontWidth != backWidth || frontHeight != backHeight)
            {
                return;
            }

            // TODO: Make LockRect only lock relevant pixels
            frontSurf->LockRect( &frontRec, NULL, 0);
            backSurf->LockRect( &backRec, NULL, 0);
            holeSurf->LockRect( &holeRec, NULL, 0);

            BYTE* frontBuffer = (BYTE*)(frontRec.pBits);
            BYTE* backBuffer = (BYTE*)(backRec.pBits);
            BYTE* holeBuffer = (BYTE*)(holeRec.pBits);

            const int margin = 25;  // distance from edge
            UINT startingY = rand() % (frontHeight - holeHeight - margin) + margin;
            UINT startingX = rand() % (frontWidth - holeWidth - margin) + margin;

            // Get the pixel location on the texture of the hole
            holeCenter = holeCenterPixel = D3DXVECTOR2(startingX + holeWidth / 2.0f, startingY + holeHeight / 2.0f);
            
            // Make bottom left the origin of holeCenter
            holeCenter.y = frontHeight - holeCenter.y;

            // Convert it to texture coordinates UV (out of 1)
            holeCenter.x /= frontWidth;
            holeCenter.y /= frontHeight;

            // Convert it to where it would be situated on the quad (using scaleX * 2 and scaleY * 2)
            holeCenter.x *= PaperSheet::sharedScaleX * 2;
            holeCenter.y *= PaperSheet::sharedScaleY * 2;

            // Reflect and translate the pixel location to be related to the world origin
            holeWorldLocation.x = holeCenter.x - PaperSheet::sharedScaleX;
            holeWorldLocation.y = -holeCenter.y + PaperSheet::sharedScaleY;
            
            this->holeHeight = holeHeight;
            this->holeWidth = holeWidth;
            
            for (UINT y = startingY; y < startingY + holeHeight; ++y) {
                for (UINT x = startingX; x < startingX + holeWidth; ++x) {
                    DWORD index = (x * 4) + (y * frontRec.Pitch);
                    DWORD hIndex = ((x - startingX) * 4) + ((y - startingY) * holeRec.Pitch);
                    // since the hole is grayscale, RGB values should all be the same, so 255 = black, 0 = white
                    frontBuffer[index+3] = (BYTE)holeBuffer[hIndex];        // Alpha
                }
            }
            
            // Flip the y so the hole is in the right spot on the other side
            for (UINT y = backHeight - startingY - holeHeight; y < backHeight - startingY; ++y) {
                for (UINT x = startingX; x < startingX + holeWidth; ++x) {
                    DWORD index = (x * 4) + (y * frontRec.Pitch);
                    DWORD hIndex = ((x - startingX) * 4) + ((y - backHeight + startingY + holeHeight) * holeRec.Pitch);
                    // since the hole is grayscale, RGB values should all be the same, so 255 = black, 0 = white
                    backBuffer[index+3] = (BYTE)holeBuffer[hIndex];     // Alpha
                }
            }
            
            // TODO: Check if desc and surfaces need to be released
            holeSurf->UnlockRect();
            backSurf->UnlockRect();
            frontSurf->UnlockRect();
        }
        

I have uploaded the source code for your reference, as well as an executable to play the game. You need to have DirectX installed for the executable to work. If you have Visual Studio you can compile the game yourself. You can download it here.

Your screen resolution is too small to display the screenshot. Here is a link.

Falling Paper