Midterm – Ghostly Pong

 

Concept

My midterm project, which is called Ghostly Pong, is similar to the Ping Pong game but instead of 1 disk the players will play with 2, and the disks will keep periodically disappearing and reappearing, just like a ghost.

After hearing some valuable feedback during the class on my midterm progress, I decided to change the concept of my game and make it unique of a kind – something that you can’t play in real life. That was when I came up with the idea of “Ghostly Pong” – a Pong game with a disappearing disk. Furthermore, another special feature that I incorporated into my game which is not present in traditional Pong game is the idea of playing with 2 disks simultaneously.

Game features

  • Each player will have a paddle and an individual score counter
  • Players must hit the disk with their corresponding paddles, if they miss the disk, their opponents will score a point
  • Players can move the paddle only vertically (along the initially specified y-axis)
  • Paddles will be in rectangular shape, the disc will be in circular shape
  • The control key for the player 1 will be “W” and “S” keys, for the player 2 – “UP” and “DOWN” keys
  • The first player to score 10 points wins

 

Game design

In my code, I created different classes for Disk, Paddle, and Game objects. Each of these classes contains corresponding functions that aim at displaying the object, updating the coordinates, making the object move, and perform other features unique to the object. Every time a new game starts, new instances of these objects would be created.

Also, my game consists of 4 stages: Welcome page, Instructions page, Game, Game Over page. The draw() function will draw will display different screens based on the stage we’re in. Transitions from one stage to another is usually handled by the mouse click.

//display for a welcome page
function welcomePageDisplay(){
    image(bgWelcome, 0, 0, RES_W, RES_H);
    
    //restore default alignment modes
    rectMode(CORNER)
    textAlign(LEFT)
  
    fill(255)
    textFont("Lakki Reddy", 25) 
    let msg1 = "Welcome to the"
    
    //place all the text in the center of the screen
    let tWidth1 = textWidth(msg1)
    text(msg1, RES_W / 2 - (tWidth1 / 2), RES_H / 9)
  
    textFont("Comforter Brush",60);
    let msg2 = "Ghostly Pong Game"
    let tWidth2 = textWidth(msg2)
    text(msg2, RES_W / 2 - (tWidth2 / 2), RES_H / 4)
  
    textFont("Lakki Reddy",15) 
    let msg3 = "Click anywhere to continue to instructions page"
    let tWidth3 = textWidth(msg3)
    text(msg3, RES_W / 2 - (tWidth3 / 2), 10*RES_H / 11)

}

There are two separate functions for updating the screen when the player scores a goal or when the game is over: continueGame() get called when the disk enters one of the goals, hence in this function I create a new instances of only Disk objects. In the function restartGame(), I create a new instance of the whole Game, which in turn automatically creates new instances of Disk and Paddle objects.

The game will always have a background music, which will also change based on the game theme the user chooses. Moreover, the collision of the disk and the paddle, as well as the goals will be accompanied by different corresponding sounds (this can make the game a bit easier by giving some hints about the location of the disk when it disappears from screen).

P.S. For all the sound and images I used, I created a separate text file called “links” with the links to the websites from where I took those elements.

Problems I ran into

In terms of technical side, the most difficult and time-consuming part of this project was writing the code for checking the collision the paddle with the disk and making it bounce off as in the real life. In order to achieve this goal, I decided to rely on the physics’ law of collision: when the disk hits the surface, it will bounce with the same speed as before in the opposite direction(conservation of impulse) and it will also bounce off with the same angle (angle of incidence = angle of reflection). After a few trials and error , I succeeded in implementing this collision check and bouncing correctly.

The below code is where I implemented this logical check.

//Code for managing Disk bouncing when it touches paddles 
function Disk_Paddle_Contact(paddle, disk){
        
        //check whether the disk contacted the left paddle
        if ((disk.disk_xc - disk.disk_w/2 < paddle.px + paddle.pw/2) && (disk.disk_yc + disk.disk_h/2 > paddle.py - paddle.ph/2) && (disk.disk_yc - disk.disk_h/2 < paddle.py + paddle.ph/2)){
            if(disk.disk_speed_x < 0){
              
                //play the collision sound when disk touches the paddle
                collisionSound.play()
              
                //print(paddle.px,paddle.py)
                disk.disk_speed_x = - disk.disk_speed_x
            }
        }      
        //check whether the disk contacted the right paddle      
        else if ((disk.disk_xc + disk.disk_w/2 > paddle.pxr - paddle.pw/2) && (disk.disk_yc + disk.disk_h/2 > paddle.pyr - paddle.ph/2) && (disk.disk_yc - disk.disk_h/2 < paddle.pyr + paddle.ph/2)){
            if(disk.disk_speed_x > 0){
              
              //play the collision sound when disk touches the paddle
                collisionSound.play()
              
              //print(paddle.pxr,paddle.pyr)
              disk.disk_speed_x = - disk.disk_speed_x
        
            }
        }   
}
//Code for managing Disk bouncing when it touches boundaries 
    Disk_bouncing(){
        //this if statement will be executed if the disk touches right-side boundaries of the table
        if(this.disk_xc + this.disk_w/2 > RES_W){
          
          //this indicates, player 2 missed the disk, so play 1 scores one point
          score_player1 += 1
          goalSound.play()
          continueGame() 
        }
                
         //this if statement will be executed if the disk touches left-side boundaries of the table       
        else if(this.disk_xc - this.disk_w/2 < 0){
          
           //this indicates, player 1 missed the disk, so play 2 scores one point
           score_player2 += 1
           goalSound.play()
           continueGame() 
        }
  
       
        //this if statement will be executed if the disk touches upper or lower boundaries of the table
        if(this.disk_yc + this.disk_h/2 > RES_H || this.disk_yc - this.disk_h/2 < 0){
            if(RES_H/2 + 100 < this.disk_yc  || this.disk_yc < RES_H/2 - 100){
                this.disk_speed_y = - this.disk_speed_y
            }   
        }
      
        this.disk_xc = this.disk_xc + this.disk_speed_x
        this.disk_yc = this.disk_yc + this.disk_speed_y 

     }
}

What I’m particularly proud of

I’m proud of the good technical decision to create different classes for each of the Disk and Paddle objects, as this allowed me to easily create new instances of them, independently of each other, later in the game and to write a separate function on a global scope called ” Disk_Paddle_Contact(paddle, disk)” which takes instances of those classes and calculates whether there was a contact. I’m also proud of the good game design and the designs of the Welcome and Game Over pages.

Future improvements

Some people may find it hard to hit the disk as it constantly keeps disappearing. Therefore I decided to add some sound stimuli in terms of the collision sound and goal sound to help the user recognize when he/she scores a goal. One of the suggestions for future improvements would be making the paddles blink when the disk is nearby, thus sending some sort of a “hint” to the player of where the disk is located.

Another possible suggestion would be adding a volume slider for the game_display(), so that the user could control the intensity of a background music, and make it lower if it is too loud.

Leave a Reply