Page 1 of 1

Creating Pong A simple walk-through

#1 Hyper  Icon User is offline

  • Banned

Reputation: 108
  • View blog
  • Posts: 2,129
  • Joined: 15-October 08

Posted 21 March 2009 - 11:54 PM

PREFACE: You require this header file (TextControl) to compile THIS (Pong!) program (game)

Hello, first and foremost, my name is Dani (Danielle, to be proper) but you may call me Hyper.
I'm not very good at writting tutorials or thinking of how to start them and word them and everything else, but I'll try my best! :)

Please note:
I am not very proficient with object oriented programming - I am not very proficient with C++, do not mistake me for a "professional" programmer


First things first when it comes to designing a game (no matter how simple, complex, large or small):
Look at everything we're going to need to do. In Pong, I think it's rather straight-forward and simple:
  • Two Paddles
  • A ball (to hit)
  • Walls (a "playing field" if you will)
  • An opponent! (Computer or Player)
.

We will create three classes to run the game, formally:
  • cBall: A class that "describes" the ball and all its properties (size, color, speed, etc)
  • cPaddle: A class that "describes" the paddle and all its properties (size, color, location, speed, etc)
  • cController: A class that ties all the other classes together, with a little extra functionality (testing if two "objects" (instances of the classes) collided)
.

Step 1. Creating the Ball Class
The Ball class will keep track of the Balls:
X and Y velocity, X and Y coordinates, foreground and background color, and last but not least! The balls perimeter ("playing field", walls)
When you create a game, over-time you forget what a variable was originally intended for, and because of that reason, we have "naming conventions." Such as placing an "i" infront of integers, "b" infront of booleans, etc. The Ball class is prefixed with "c" meaning "class" (or... "Ball Class")
The variables the Ball Class (cBall) shall contain should be meaning full, and useful! Here is what we have so far for the cBall class:
class cBall {

    private:
        int iForeground;  /* Balls foreground color */
        int iBackground;  /* Balls Background color */

        int iBall_X;      /* Balls X coordinate */
        int iBall_Y;      /* Balls Y coordinate */

        int iVelocity_X;  /* Balls X velocity (speed) */
        int iVelocity_Y;  /* Balls Y velocity (speed) */

        SMALL_RECT smWalls; /* The balls playing field */

    public:
        cBall() { }; /* An empty constructor */
};


Now we can easily! Create as many game balls as we'd like (although you should have only one in a game like Pong).
We now have raw data for our Ball class, but no methods (functions) to go with that data!
Thus making the data pointless, useless, void, empty, etc... :( But have no fear, for I shall light the path! :)
What kind of functions would we need for a ball? Well, we obviously need a function to set the colors, and set the ball's X/Y values, and! The ball's velocity... and and and...! To update the ball's position and draw the ball itself! I mean come on, what's a game without a ball being drawn?
Here's a more formal list of methods (functions) for the Ball class:
    /* "Generic" methods */
  • ReverseBall(): To flip the balls direction (+1 becomes -1 or -1 becomes +1)
  • UpdateBall(): To update the balls position, AND check if he has hit a wall (thus! Bouncing away from it)
  • DrawBall(): To draw the ball (and erase the old ball) at the new position

    /* Accessor methods */
  • GetXVelocity(): To get the X velocity (for what else? To compare it against stuff, d'uh)
  • GetYVelocity(): To get the Y velocity
  • GetXBall(): To get the balls X position
  • GetYBall(): To get the balls Y position

  • SetForeground(): To set the balls color
  • SetBackground(): To set the balls background color (usually black)
  • SetXBall(): To set the balls X coordinate (used only to start a game, and to reset a ball after a lost life)
  • SetYBall(): To set the balls Y coordinate
  • SetXVelocity(): To set the balls (initial) X velocity (I'd say +1 so it isn't berserk fast) :P
  • SetYVelocity(): To set the balls Y velocity
  • SetLeftWall(): To set a boundary (West side)
  • SetRightWall(): To set a boundary (East side)
  • SetTopWall(): To set a boundary (North side)
  • SetBottomWall(): To set a boundary (South side)

Phew! That was a mouthful for such a "simple" game, huh? :) Pong isn't as simple as it looks
(if you're like really really new to C/C++ or programming or even game programming! in general).

Step 2. Creating the Paddle Class
The Paddle class will keep track of the Paddle's X and Y coordinates, and that's about it.. Because a paddle's pretty boring! Especially compared to a ball... That round shape is SO much better than a boring rectangle. ;)
Here's the raw data for the Paddle class that we'd need:
class cPaddle {

    private:
        int iForeground; /* Paddle's foreground color */
        int iBackground; /* Paddle's Background color */

        int iVelocity_Y; /* Paddle's Y velocity (speed) */
        int iPaddle_Y;   /* Paddle's Y coordinate */
        int iPaddle_X;   /* Paddle's X coordinate */

        int iLives;      /* Players lives left */

        int iPaddleUp;   /* The key to move the Paddle up */
        int iPaddleDown; /* The key to move the Paddle down */

        bool bComputer;  /* Is the player a computer? */

    public:
        cPaddle() { bComputer = false; iLives = 4; }; /* A not so empty constructor */
};


Well, the reason the constructor isn't empty for the Paddle class is pretty simple and straight forward: We don't want to start out with 32,000 lives (or some other random number), and we want it to be a PERSON instead of a COMPUTER by default.
What kind of functions would we want for a paddle? Probably just the typical: Draw, update, and that's it... but I don't know, maybe you (as a reader and programmer who SHOULD have a cheerful heart and excited mind) could create something more fantastic.
Here's a more formal list:
    /* "Generic" methods */
  • ReduceLife(): A player lost a life
  • UpdatePaddle(): To update the Paddles X and Y coordinates (by means of getting user input)
  • DrawPaddle(): To draw! The Paddle (and erase the old paddles position)

    /* Accessor methods */
  • GetXPaddle(): To get the Paddles X coordinate (for comparisons on Ball vs. Paddle hits)
  • GetYPaddle(): To get the Paddles Y coordinate
  • GetLives(): To get the player ("paddle")'s total lives (left); A great way to see if he's still "alive" in the game.

  • SetUpArrow(): To set the players up arrow key (a key they'll press (or joystick button) to move UP!)
  • SetDownARrow(): To set the players down arrow key
  • SetXPaddle(): To set the (initial.. I hope) X coordinate of the Paddle
  • SetYPaddle(): To set the Y coordinate of the Paddle
  • SetForeground(): To set the foreground (front) color of the Paddle
  • SetBackground(): To set the background (behind) color of the Paddle

That wasn't so bad... Well, now you can EASILY!! Create (yay for re-usable code! ;)) multiple paddles in a single game.

Step 3. Tying it all together, the Controller class
Yay! We're almost at the end of this wretchedly horrible tutorial! :) Now the point of the third (and final) class, is to tie all other classes together! Simply because I'm cool like that. :P Now I'm sure you're ABSOLUTELY! Confused at this point, because you already HAVE your Ball class and your Paddle class, so what could possibly be left, hmm? Well... think about this: How do you check if the Paddle's out of range? How do you check if the Ball hit the paddle? How do you check if the Ball surpassed the Paddle's position? How do you... etc.
The answer: The (this) controller class! The master class. The furthest down root in the class tree.
What kind of variables should this class contain? You might wonder. The answer: The cBall and cPaddle classes. ;)
class cController {

    public:
        int iInitialBallX; /* A copy of the Balls original X position */
        int iInitialBallY; /* A copy of the Balls original Y position */

        cBall Ball;       /* The game's ball (you could create multiple of these for fun effects! ;)/>) */
        cPaddle Player;   /* The players paddle */
        cPaddle Computer; /* The computers paddle */
};


To cut this short, here's a list of the methods of the cController class:
  • UpdatePositions(): To display a list of ALL variables in the game (Ball X/Y, Paddle (computer/player) X/Y, etc)
  • CheckHit(): To check if a Ball hits either Players top/bottom (half of their) paddle, or computers, OR if a player Scores on another player
  • AI(): To give the computer a winning edge (very simple, rule-based system logic)
.

Step 4. A closer look at the classes
Here's a more in-depth look at the classes we have from earlier (shown in ascending order):

- class cBall:
class cBall {

    private:
        int iForeground; /* Ball's foreground color */
        int iBackground; /* Ball's Background color */

        int iBall_X; /* Ball's X coordinate */
        int iBall_Y; /* Ball's Y coordinate */

        int iVelocity_X; /* Ball's X velocity (speed) */
        int iVelocity_Y; /* Ball's Y velocity (speed) */

        SMALL_RECT smWalls;

    public:
        cBall() { };

        void ResetBall(const int X, const int Y) {
            PlaceCursor(iBall_X, iBall_Y);
            printf(" ");

            iBall_X = X;
            iBall_Y = Y;
            iVelocity_X = 1;
            iVelocity_Y = 1;
        }

        void UpdateBall() {

            /* Erase the old ball */
            PlaceCursor(iBall_X, iBall_Y);
            printf(" ");

            /* Bounce if the ball hits a wall */
            if (iBall_X > smWalls.Right || iBall_X < smWalls.Top) { iVelocity_X = -iVelocity_X; }
            if (iBall_Y > smWalls.Bottom || iBall_Y < smWalls.Top) { iVelocity_Y = -iVelocity_Y; }

            /* Update the position */
            iBall_X += iVelocity_X;
            iBall_Y += iVelocity_Y;

            return;
        }

        void DrawBall() {

            /* Drew the ball at the new position */
            PlaceCursor(iBall_X, iBall_Y);
            SetColor(iForeground, iBackground);
            printf("");

            return;
        }

        void ReverseBall() { if (rand() % 9) { iVelocity_X = -iVelocity_X; } else { iVelocity_X = (-iVelocity_X + 1); } return; }

        int GetXVelocity() { return iVelocity_X; }
        int GetYVelocity() { return iVelocity_Y; }
        int GetXBall() { return iBall_X; }
        int GetYBall() { return iBall_Y; }

        void SetForeground(const int Color) { iForeground = Color; return; }
        void SetBackground(const int Color) { iBackground = Color; return; }
        void SetXBall(const int X) { iBall_X = X; return; }
        void SetYBall(const int Y) { iBall_Y = Y; return; }
        void SetXVelocity(const int Velocity) { iVelocity_X = Velocity; return; }
        void SetYVelocity(const int Velocity) { iVelocity_Y = Velocity; return; }
        void SetLeftWall(const int Wall) { smWalls.Left = Wall; return; }
        void SetRightWall(const int Wall) { smWalls.Right = Wall; return; }
        void SetTopWall(const int Wall) { smWalls.Top = Wall; return; }
        void SetBottomWall(const int Wall) { smWalls.Bottom = Wall; return; }
};


- class cPaddle:
class cPaddle {

    private:
        int iForeground; /* Paddle's foreground color */
        int iBackground; /* Paddle's Background color */

        int iVelocity_Y; /* Paddle's Y velocity (speed) */
        int iPaddle_Y;   /* Paddle's Y coordinate */
        int iPaddle_X;   /* Paddle's X coordinate */

        int iLives;      /* Players lives left */
        
        int iPaddleUp;   /* The key to move the Paddle up */
        int iPaddleDown; /* The key to move the Paddle down */

        bool bComputer;

    public:
        cPaddle() { bComputer = false; iLives = 4; };

        void UpdatePaddle() {

            if (!bComputer) {
                if (GetAsyncKeyState(iPaddleDown) && iPaddle_Y < BOTTOM_WALL) {
                    /* Erase the old Paddle */
                    PlaceCursor(iPaddle_X, iPaddle_Y);
                    printf(" ");
                    PlaceCursor(iPaddle_X, iPaddle_Y + 1);
                    printf(" ");

                    iPaddle_Y += 1;
                }

                if (GetAsyncKeyState(iPaddleUp) && iPaddle_Y > TOP_WALL) {
                    /* Erase the old Paddle */
                    PlaceCursor(iPaddle_X, iPaddle_Y);
                    printf(" ");
                    PlaceCursor(iPaddle_X, iPaddle_Y + 1);
                    printf(" ");

                    iPaddle_Y -= 1;
                }
            }

            return;
        }

        void DrawPaddle() {

            /* Drew the Paddle at the new position */
            PlaceCursor(iPaddle_X, iPaddle_Y);
            SetColor(iForeground, iBackground);
            printf("");
            PlaceCursor(iPaddle_X, iPaddle_Y + 1);
            SetColor(iForeground, iBackground);
            printf("");

            return;
        }

        void ReduceLife() { iLives--; }
        void SetPaddleType(bool Type) { bComputer = Type; }

        int GetXPaddle() { return iPaddle_X; }
        int GetYPaddle() { return iPaddle_Y; }
        int GetLives() { return iLives; }

        void SetXPaddle(int X) { iPaddle_X = X; }
        void SetYPaddle(int Y) { iPaddle_Y = Y; }
        void SetUpArrow(const int Up) { iPaddleUp = Up; }
        void SetDownArrow(const int Down) { iPaddleDown = Down; }
        void SetForeground(int Color) { iForeground = Color; }
        void SetBackground(int Color) { iBackground = Color; }
};


- class cController:
class cController {

    public:
        int iInitialBallX; /* A copy of the Balls original X position */
        int iInitialBallY; /* A copy of the Balls original Y position */

        cBall Ball;       /* The game's ball */
        cPaddle Player;   /* The players paddle */
        cPaddle Computer; /* The computers paddle */

        void UpdatePositions(const int X, const int Y) {

            PlaceCursor(X, Y);
            SetColor(WHITE, BLACK);
            printf("Ball X: %i  ", Ball.GetXBall());
            PlaceCursor(X, Y + 1);
            printf("Ball Y: %i  ", Ball.GetYBall());
            PlaceCursor(X, Y + 2);
            printf("Ball X Velocity: %i  ", Ball.GetXVelocity());
            PlaceCursor(X, Y + 3);
            printf("Ball Y Velocity: %i  ", Ball.GetYVelocity());

            PlaceCursor(X, Y + 4);
            printf("Player X: %i  ", Player.GetXPaddle());
            PlaceCursor(X, Y + 5);
            printf("Player Y: %i  ", Player.GetYPaddle());
            PlaceCursor(X, Y + 6);
            printf("Player Lives: %i  ", Player.GetLives());

            PlaceCursor(X, Y + 7);
            printf("Computer X: %i  ", Computer.GetXPaddle());
            PlaceCursor(X, Y + 8);
            printf("Computer Y: %i  ", Computer.GetYPaddle());
            PlaceCursor(X, Y + 9);
            printf("Computer Lives: %i  ", Computer.GetLives());

            return;
        }

        void CheckHit() {

            /* Ball hits player's (top half) paddle */
            if (Ball.GetXBall() == Player.GetXPaddle() - 1 && Ball.GetYBall() == Player.GetYPaddle()) { Ball.ReverseBall(); }

            /* Ball hits player's (bottom half) paddle */
            else if (Ball.GetXBall() == Player.GetXPaddle() - 1 && Ball.GetYBall() == Player.GetYPaddle() + 1) { Ball.ReverseBall(); }

            /* Ball hits computer's paddle */
            if (Ball.GetXBall() == Computer.GetXPaddle() + 1 && Ball.GetYBall() == Computer.GetYPaddle()) { Ball.ReverseBall(); }

            /* Ball hits computer's (bottom half) paddle */
            else if (Ball.GetXBall() == Computer.GetXPaddle() + 1 && Ball.GetYBall() == Computer.GetYPaddle()) { Ball.ReverseBall(); }

            /* Computer lost */
            if (Ball.GetXBall() < Computer.GetXPaddle()) {
                Ball.ResetBall(iInitialBallX, iInitialBallY);
                Computer.ReduceLife();
                if (Computer.GetLives() == 0) { printf("COMPUTER OUT!"); cin.get(); exit(0); }
            }

            /* Player lost */
            if (Ball.GetXBall() > Player.GetXPaddle()) {
                Ball.ResetBall(iInitialBallX, iInitialBallY);
                Player.ReduceLife();
                if (Player.GetLives() == 0) { printf("PLAYER OUT!"); cin.get(); exit(0); }
            }

            return;
        }

        void AI(cPaddle& Paddle) {

            /* Move paddle down */
            if (Paddle.GetYPaddle() < Ball.GetYBall() && Paddle.GetYPaddle() < BOTTOM_WALL) {
                PlaceCursor(Paddle.GetXPaddle(), Paddle.GetYPaddle());
                printf(" ");
                Paddle.SetYPaddle(Paddle.GetYPaddle() + 1);
            }

            /* Move paddle up */
            if (Paddle.GetYPaddle() > Ball.GetYBall() && Paddle.GetYPaddle() > TOP_WALL) {
                PlaceCursor(Paddle.GetXPaddle(), Paddle.GetYPaddle() + 1);
                printf(" ");
                Paddle.SetYPaddle(Paddle.GetYPaddle() - 1);
            }

            return;
        }
};


Now that we have all our tools (classes), and we have all our attention, inspiration, motivation, and excitement removed!
I shall show you how to put the final product together!
#define TOP_WALL 4
#define BOTTOM_WALL 20

int main() {

    HANDLE OutHandle = GetStdHandle(STD_OUTPUT_HANDLE);

    /* Resize the console window so it has more room */
    SetConsoleTitle("Pong");
    SMALL_RECT windowSize = {0, 0, 79, 49};
    SetConsoleWindowInfo(OutHandle, TRUE, &windowSize);

    /* Remove the blinking caret */
    RemoveCursor();

    /* Clear the entire! Screen */
    ClearConsole(BLACK, BLACK);

    /* To create the game, we use our "master" class */
    cController Game;

    /* Setup the player's Paddle */
    Game.Player.SetForeground(WHITE);
    Game.Player.SetBackground(BLACK);
    Game.Player.SetXPaddle(70);
    Game.Player.SetYPaddle(15);
    Game.Player.SetUpArrow(VK_UP);
    Game.Player.SetDownArrow(VK_DOWN);
    Game.Player.SetPaddleType(false);

    /* Setup the computer's Paddle */
    Game.Computer.SetForeground(WHITE);
    Game.Computer.SetBackground(BLACK);
    Game.Computer.SetXPaddle(10);
    Game.Computer.SetYPaddle(15);
    Game.Computer.SetPaddleType(true);

    /* Setup the ONE Ball */
    Game.Ball.SetXVelocity(1);
    Game.Ball.SetYVelocity(1);
    Game.Ball.SetXBall(25);
    Game.Ball.SetYBall(5);
    Game.iInitialBallX = 25;
    Game.iInitialBallY = 5;
    Game.Ball.SetForeground(WHITE);
    Game.Ball.SetBackground(BLACK);
    Game.Ball.SetTopWall(5);
    Game.Ball.SetBottomWall(20);
    Game.Ball.SetLeftWall(20);
    Game.Ball.SetRightWall(72);

    /* Draw a playing field */
    SetColor(WHITE, BLACK);
    DrawBox(65, 20, 8, 3);

    /* The game loop */
    while (true) {
        Game.Ball.UpdateBall();
        Game.Ball.DrawBall();

        //Game.AI(Game.Player);
        Game.Player.UpdatePaddle();
        Game.Player.DrawPaddle();

        Game.AI(Game.Computer);
        Game.Computer.UpdatePaddle();
        Game.Computer.DrawPaddle();

        Game.CheckHit();
        Game.UpdatePositions(0, 25);
        Sleep(50); /* This is so you don't suck up all your system resources */
    }

    /* Clear the entire screen again, and set it up for another DOS program to run */
    ClearConsole(BLACK, BLACK);
    PlaceCursor(0, 0);

    return 0;
}


Welcome! Hope that helps somebody. :)
If you find any bugs, or have any comments/suggestions, feel free to PM, e-mail, or otherwise contact me via "The internet."

Have a happy day (and year)!

Is This A Good Question/Topic? 1
  • +

Replies To: Creating Pong

#2 Skykast  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 24-March 09

Posted 24 March 2009 - 04:44 PM

How do you make a header?
Was This Post Helpful? 0
  • +
  • -

#3 Masterchief117.arbiter  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 159
  • Joined: 27-March 09

Posted 02 April 2009 - 09:00 AM

hey when i compile i get a error include files nested too deep
Was This Post Helpful? 0
  • +
  • -

#4 Guest_Matt*


Reputation:

Posted 09 April 2010 - 04:33 AM

I JUST realised that I don't even know how to code C++ so I am completely screwed. I was looking forward to making pong. :cry2:
Was This Post Helpful? -1

#5 cplusplus2392  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 14
  • Joined: 30-March 10

Posted 22 April 2010 - 07:32 PM

ok i am very new to c++ and i see that you make all these classes and and then on your last code piece you say your going o show us how to put it all together. ok i am LOST. lol where do i put the classes? same file? as header files? source files? should all of it be in a project? sorry if i come across as an idiot but once again i am lost
Was This Post Helpful? 0
  • +
  • -

#6 Fib  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 161
  • View blog
  • Posts: 554
  • Joined: 12-March 09

Posted 07 May 2010 - 11:16 AM

You should be able to put all the classes, and the main in the same source file.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1