Project 3: Hits from the Pong

Due by: Friday, December 1, 2023 at 11:59 p.m.

Strike the Pong!

Pong is perhaps the oldest video game. The object is simple: Try to prevent a bouncing ball from getting past your paddle while your opponent does the same. Your goal is write a MonoGame-based C# program that is a fully 3D version of the game pong with keyboard controls.

Here is a screenshot of my sample implementation:


Screenshot of pong implementation

Specification

I've broken down the specification into a number of areas:

Basic Rendering

Below, I describe the basic objects that must be displayed in your game of pong. You are not required to use the dimensions that I give. These dimensions are provided to give you some rough outline you can compare against.

Your game of pong must use two paddles, both of which are rectangular prisms. My paddles have a length and width of 2 units and a thickness of 0.2 units. Remember that you can use a unit cube spanning the space from (-1,-1,-1) to (1,1,1) as the basis for any rectangular prism. Before rendering, all you have to do is set the appropriate world transform that stretches and moves the unit cube to make it a box of any dimension and location. The textured cube example from Assignment 3 should provide you with an excellent background for making paddles. You might consider using the cube model provided by Assignment 4, but it doesn't have texture coordinates.

Your game must use a bounding box for the playable area, which must be rendered in some way left up to your discretion. The simplest way is as a line list or a wireframe. My bounding box has a goal area with a length of 20 units and a width of 20 units. The depth of the entire field (from goal to goal) is 40 units.

Your game must render a ball. A ball is much more complex than a rectangular prism. Again, you can use the sphere model from Assignment 4 to make a sphere. Here are both the sphere and the cube models:

Unfortunately, this sphere has a radius of 3.0 units, so I scale it to make a sphere with a radius of 1.0.

You must use a minimum of directional lighting. I use a single directional light for the ball and paddles and then turn off lighting for the skybox.

Game Play

Game play is straightforward. The goal for each player is to deflect the ball when it nears his or her goal area. If any part (not just the center) of the ball gets past a player's paddle, a point is awarded to the other player. The first player to 5 points wins.

The ball should have a constant speed. My ball speed is 10 units/second. That speed is the magnitude of the overall velocity vector (which of course can point in any direction). When the ball hits a wall, the appropriate component of the velocity should be negated. For example, hitting the top of the bounding box will negate the y velocity component.

My ball starts off going along the z axis toward the player paddle. That way, the ball will just bounce back and forth forever unless you make a move. If you hit the ball directly in the center of the paddle, its z component will be inverted. However, if you hit closer to the edge of the paddle, that glancing blow should deflect the ball appropriately. My hack for this works as follows.

  1. Compute the difference between the x location of the ball and the x location of the paddle.
  2. Do the same for the y locations.
  3. Normalize the ball's velocity.
  4. Add the x difference and the y difference to the velocity.
  5. Normalize the velocity again.
  6. Finally, multiply the vector by the constant ball speed so that the overall speed doesn't change.

You're free to use a different approach, but it will ultimately be similar. You want to deflect the ball by different amounts depending on how different its x and y locations are from the paddle's. The key is to make sure that you do some redirection computations on a normalized velocity (so that if you change the overall speed later, your calculations are not dependent on a particular speed) and also making sure that the exit velocity of the ball has the same outgoing magnitude (hitting a paddle should not make the ball's speed faster or slower).

When a point is scored, I send the ball toward the goal of the person who made the point, along the z axis. You can start each point differently, if you like. You could even start the ball at a random trajectory. However, I recommend starting with something easy for debugging first.

AI

The second paddle must be controlled by an AI. If you wish, you may have multiple AI modes, but you must support the following, at a minimum.

Each time the ball hits a paddle (and once at the beginning of each point), the AI computes the location where the ball will finally hit his wall. Then, it begins to move there. It does so very slowly, more slowly than the speed your paddle can move at, but it is always correct. The only thing that can put the AI out of position is hitting an extremely glancing blow from the human paddle, radically changing the trajectory of the ball. The AI will immediately see how the ball is bouncing, but it may not have time to react.

The difficulty of programming this AI is that the ball may bounce many (an unlimited number, in fact) times before it hits the AI's wall. From the velocity vector of the ball, you must compute its next intersection with a wall face, and its next, and its next, and so on, until you find one that intersects with the AI's goal area. Then, you'll know where the AI's target is. Each frame, the AI moves its maximum speed toward the target, unless it has already reached it.

To compute an intersection between a ball and a side of a bounding box, you may want to refer to some of the geometric techniques discussed in Week 4. A ball will intersect the plane that contains a bounding box wall if the dot product of the ball's velocity vector and the plane's normal is negative. Using the explicit form of a 3D line, you can compute the t parameter value when the ball will hit the plane. Then, you can look at the other two coordinates of that intersection and see if they fall within the bounds of the rectangle that forms the wall. If that intersection is with the AI wall, you're done. Otherwise, you have to continue the process recursively (using the location where the ball hits the wall and the new vector) to find another intersection. In principle, the process could go on indefinitely if the ball were bouncing between the top and the bottom (or the left and the right) walls. However, that situation should never arise in the game since the ball will always have a non-zero z velocity.

The maximum speeds of a human paddle and an AI paddle are different, but they apply to the total speed a paddle can have. What does that mean? A paddle does not have separate maximum x and y speeds. For example, the human paddle is controlled by the arrow keys. If the up arrow key is pressed, the human paddle should head in the positive y direction at its maximum speed. However, if up and left are pressed, it should head in positive y at √2/2 times the maximum speed and in negative x at √2/2 times the maximum speed.

Camera Control

You should have camera controls that allow the user to view the game at different angles. In my game, I used WASD to rotate the camera (which initially started at (50, 0, 0)) around the y and z axes. You are welcome to try your own.

Alternatively, you may make the camera automatically follow the ball. You need a creative and smooth solution for doing so. Otherwise, the game becomes confusing, and the player can become dizzy.

Special Effects

You are required to use a few special effects. The first is displaying the score. Earlier in the semester, I did an example of rendering text onto the screen. Use the SpriteFont class inside of a SpriteBatch drawing session.

Drawing the score should be the last thing you do in the rendering process. You can even turn alpha blending on (because you aren't worried about render order at that point) to make the letters semitransparent.

Another important thing you need to do is indicate where the ball is. The human brain uses a lot of cues such as shadowing to interpret 2D data in a 3D way. The (now ancient) Curve Ball game showed z position by highlighting the square that intersects the bounding box at its current location.

Finally, you are required to texture map the two paddles. A skybox would be nice but is not required. Likewise, texturing the ball is not required.

Extras

Two-person groups must implement at least 5 extra features but can implement up to 5 additional features beyond that for up to 25 points of extra credit for this project. A three-person group must implement at least 10 extra features and can also implement up to 5 additional features for the same extra credit.

The extras will generally enhance one of the main areas of the project such as Basic Rendering, Game Play, AI, Camera Control, or Special Effects. Pick features that you think will make the game more fun! The following is a list of suggestions, but you can create your own (though you may want to check with me to see if they are worth full credit).

  • Basic Rendering
    • Partially transparent walls for the bounding box
    • Point lights (perhaps even a point light inside the ball)
  • Game Play
    • Two player human mode (that is reasonable to play)
    • Power ups (that change ball speed, paddle size, paddle speed, etc.)
    • Sound effects (MonoGame tutorial for audio here. Also, there's an XNA tutorial you can adapt to MonoGame here.)
    • Background music (You can use the same MonoGame tutorial above or adapt another XNA tutorial to MonoGame from here.)
    • Xbox gamepad support
    • Curving ball support (this will affect intersections as well)
  • Camera Control
    • Automatic camera movement (must make game play better, not worse!)
    • Variably transparent human paddle (becomes more transparent when directly in the line of sight of the viewer to the ball)
  • Special Effects
    • "Fake" shadows (a dark sprite on the ground underneath the ball)
    • Real shadows (using techniques from Chapter 9, almost certainly counts as 2 features because of the difficulty involved)
    • Attractive skybox
    • Smoke or fog effects
    • Bump mapping
    • Indication of where the ball will land as a hint to the user

Hints

You may be rendering and updating a number of models. This is a perfect situation for object-oriented programming. I created a Shape base class that my Box (for paddles), Ball, and SkyBox classes extend. It has methods for rendering itself and for updating itself which are overridden by the child classes. That way, all I had to do to render was set up the lighting and camera and then loop through my array of Shape references, telling them to render themselves.

You will probably have to make your own paddle textures. You can choose to reuse skybox textures from Assignment 3 or Assignment 4, but you can also make your own. If you do any other interesting texture-based techniques (like a textured bounding box, sprite shadows, or bump mapping), you'll also need to make textures for those. Lab computers have Adobe Photoshop, but you can also use the free online tool Pixlr to manipulate images if you're working at a computer that doesn't have Photoshop installed IrfanView is another free tool with limited image manipulation tools. Alternatively, you can try GIMP, a powerful, free, open-source (but slightly unintuitive) image editing program.

My sample implementation can be downloaded here. It is far from perfect, but it is playable. You should not try to recreate it exactly. I'd love to see some creativity and a fun and unexpected gameplay experience from your projects.

Turn In

Your solution and project should both be called Project3. Zip up your entire project and solution and upload the zip file into Blackboard. Also, include a text file called extras.txt that explains the extra features you added. All work must be submitted before Friday, December 1, 2023 at 11:59 p.m. unless you are going to use a grace day.

Only the team leader should turn in the final program. You should clean your solution before submitting it. I should be able to open your solution and run it without any compilation problems.

All work must be done within assigned teams. You may discuss general concepts with your classmates, but it is never acceptable for you to look at another team's code. Please refer to the course policies if you have any questions about academic integrity. If you have trouble with the assignment, I am always available for assistance.


Grading

Your grade will be determined by the following categories:

Category Weight
Basic rendering 20%
Game play 20%
AI 10%
Camera control 5%
Special effects 5%
Extras 25%
Fun factor 5%
Visual polish 5%
Style and comments 5%

Under no circumstances should any member of one group look at the code written by another group. Tools will be used to detect code similarity automatically.

Code that does not compile will automatically score zero points.