Experiments In Game Programming
Main
 Home
XNA - C#
 Coming Soon!
 Level Editing - GTK
DirectX 9 - C++
 Downloads
 Disclaimer
 Introduction
Part 1 - DirectX
 1 - Breakout
 2 - Create DX
 3 - 2d Images
 4 - 3d Models
 5 - Cameras & Lights
 6 - Animation Timing
 7 - Keyboard/Mouse
 8 - Sound
Part 2 - Breakout
 1 - Art and Sounds
 2 - The Menu
 3 - Starting Breakout
 4 - The Level
 5 - The Paddle
 6 - The Ball
 7 - Finishing Touches

Untitled Document

Chapter 6

 

Creating the Ball

 

 

In this chapter we will create the the last class to add to breakout, the BallController.

Topics Covered in this Chapter:
  • Creating The Ball Controller
  • Detecting Collisions
  • Adding Sound Effects

The BallController class is the last class we need to create. The BallController is responsible for controlling the ball movement, handling collisions, playing sound effects, and rendering the ball. The BallController requires access to a Level object and a PaddleController object to do collisions with the ball. It also requires a SoundManager and access to the device. Take a look at the BallController from the chapter 6 code like the previous classes it starts with some defines. The SCREEN_MAX_X, SCREEN_MIN_X, SCREEN_MAX_Y, and SCREEN_MIN_Y define the ball space in the view. The mins and maxes are the edges of play, we will set the ball to bonce off these edges.
const float SCREEN_MAX_X = 22.0f;
const float SCREEN_MIN_X = -22.0f;
const float SCREEN_MAX_Y = 15.0f;
const float SCREEN_MIN_Y = -16.0f;

The BallController init requires the SoundManager, the Level, the PaddleController, and the device. Init loads the ball model and the sound effect files, as well as sets ball movement defualt values.
bool BallController::init(SoundManager* sounds, Level* level, PaddleController* paddle, LPDIRECT3DDEVICE9 device){
     //pointer to the sound manager
     mySounds = sounds;
     //pointer to the paddle
     myPaddle = paddle;
     //pointer to the level
     myLevel = level;
     ballX = 0.0f;
     ballY = 0.0f;
     ballXMovement = 0.0f;
     ballYMovement = 0.0f;
     //get the size of the paddle
     sizeOfPaddle = myPaddle->getWidth();

     //load the ball model
     ball = new Model();
     ball->loadModel(device, "ball.x");
     ball->setScale(D3DXVECTOR3(0.5f,0.5f,0.5f));

     //Load the sound effects
     paddleSound = mySounds->loadFile("hitpaddle.wav");
     brickSound = mySounds->loadFile("hitbrick.wav");

     return true;
}

The render function sets the position, and renders the ball model.
void BallController::render(LPDIRECT3DDEVICE9 device){
     ball->setPosition(D3DXVECTOR3(ballX,ballY,0.0f));
     ball->render(device);

}

The next function is restart, restart resets the ball position and starts the ball moving.
void BallController::restart(){
     ballX = 0.0f;
     ballY = 0.0f;
     ballXMovement = 0.25f;
     ballYMovement = 0.25f;
}

For each frame to update, updateFrames function calls the checkCollide function to handle collisions, and then adds the ballXMovement and ballYMovement to the ball position.
void BallController::updateFrames(int framesToUpdate){
    
     for (int i=0; i < framesToUpdate; i++){
         //check for collide in this frame before we move the ball
         checkCollide();
         //Move the ball
         ballX+= ballXMovement;
         ballY+= ballYMovement;
     }
}

The final function in the BallController class is checkCollide. CheckCollide checks all of the possible collision for the ball, and adjusts the balls movement based on the collisions. It also plays the appropriate collision sound effects. The collisions are checked against the position the ball would be on the next frame. The first collisions checked for are against the edges of the play field, when the left, right, or top of the screen is collided with the direction of the ball is changed. When the ball goes off the bottom, it is reset. Next the getTileAtCoords is called with the new X position and the current Y position, and then current X position and the new Y position to determine the direction of the collision. If the ball collides with the new Y position, then the ball hit the top or the bottom of a brick, if it was the new X position that collided then it was a side that was collided with. We use this information to reverse the X or Y direction of the balls movement. The last collision checked is with the paddle. The getPaddleXY is called to get the paddle's position. If the ball collides with the paddle, it plays the paddle collide and changes the ball direction to up.
void BallController::checkCollide(){
     float newBallX = ballX + ballXMovement;
     float newBallY = ballY + ballYMovement;
     //check collide with bounds of the screen
     if (newBallX > SCREEN_MAX_X || newBallX < SCREEN_MIN_X){
         ballXMovement = ballXMovement*(-1);
     }
     //rebound if it hits the top of the screen
     if (newBallY > SCREEN_MAX_Y){
         ballYMovement = ballYMovement*(-1);
     }
     //restart if it goes off the bottom of the screen
     if (newBallY < SCREEN_MIN_Y){
         restart();
     }
    
     //used to get the index of the tile
     int* tempX = new int;
     int* tempY = new int;
     //checks if there is a tile in the balls path
     if (myLevel->getTileAtCoords(newBallX, ballY, tempX, tempY) != 0){
         //reverse the ball movement
         ballXMovement = ballXMovement*(-1);
         //destroys the brick collided with
         myLevel->destroyBrick(*tempX, *tempY);
         //plays the collide sound
         mySounds->playSound(brickSound);
     } else if (myLevel->getTileAtCoords(ballX, newBallY, tempX, tempY) != 0){
         //reverse the ball movement
         ballYMovement = ballYMovement*(-1);
         //destroys the brick collided with
         myLevel->destroyBrick(*tempX, *tempY);
         //plays the collide sound
         mySounds->playSound(brickSound);
     }
    
     //used to get the paddleX and Y
     float* paddleX = new float;
     float* paddleY = new float;

     //get the position of the paddle
     myPaddle->getPaddleXY(paddleX, paddleY);
     if (newBallY < *paddleY){
         if (newBallX > (*paddleX - (sizeOfPaddle / 2.0f)) && newBallX <          (*paddleX + (sizeOfPaddle / 2.0f)) ){
             //reverse the ball movement
             ballYMovement = ballYMovement*(-1);
             //play the paddle sound
             mySounds->playSound(paddleSound);
         }
     }

     //delete the temp variables
     delete paddleX;
     delete paddleY;
     delete tempX;
     delete tempY;

}

That concludes the BallController, adding it to the Breakout class, requires additions to init, updateFrames and render.

Changes to Breakout Init:
bool Breakout::init(InputManager* input, SoundManager* sounds, LPDIRECT3DDEVICE9 device){
     .....
     //create the ball controller
     ball = new BallController();
     ball->init(sounds,level, paddle, device);
     ball->restart();


     .....

     return true;
}

Changes to Breakout updateFrames:
void Breakout::updateFrames(int numberOfFrames){
     //update the paddle
     paddle->update();
     //update the ball controller
     ball->updateFrames(numberOfFrames);

     if (myInput->keyPress(DIK_ESCAPE)){
         message = QUIT;
     }

}

Changes to Breakout Render:
void Breakout::render(LPDIRECT3DDEVICE9 device){
     //render the background
     background->render(device);
     //render the level
     level->render(device);
     //render the ball
     ball->render(device);
     //render the paddle
     paddle->render(device);
}

Now that the BallController has been added, compiling and running the project should provide you with a fully playable breakout game.



Summing Up - Chapter 6

The BallController class is the last class in our breakout game, it handles the ball movement, but also the game logic. The collisions are simple in this version, and the ball always bounces at 90 degree angles. Adding changes to the x and y velocity based on how the ball colides with the paddle would make the game a little more interesting.
 ©2008 David Whittaker