First of all, here is my code for Breakout (pset3):
//
// breakout.c
//
// Computer Science 50
// Problem Set 3
//
// standard libraries
#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// Stanford Portable Library
#include <spl/gevents.h>
#include <spl/gobjects.h>
#include <spl/gwindow.h>
// height and width of game's window in pixels
#define HEIGHT 600
#define WIDTH 400
// number of rows of bricks
#define ROWS 5
// number of columns of bricks
#define COLS 10
// radius of ball in pixels
#define RADIUS 10
// lives
#define LIVES 3
// prototypes
void initBricks(GWindow window);
GOval initBall(GWindow window);
GRect initPaddle(GWindow window);
GLabel initScoreboard(GWindow window);
void updateScoreboard(GWindow window, GLabel label, int points);
GObject detectCollision(GWindow window, GOval ball);
int main(void)
{
// seed pseudorandom number generator
srand48(time(NULL));
//generate velocity values
double VelocityX = drand48() + 1;
double VelocityY = 2;
// instantiate game window
GWindow window = newGWindow(WIDTH, HEIGHT);
// instantiate bricks
initBricks(window);
// instantiate ball, centered in middle of window
GOval ball = initBall(window);
// instantiate paddle, centered at bottom of window
GRect paddle = initPaddle(window);
// instantiate scoreboard, centered in middle of window, just above ball
GLabel scoreboard = initScoreboard(window);
sendBackward(scoreboard);
// number of bricks initially
int bricks = COLS * ROWS;
// number of lives initially
int lives = LIVES;
// number of points initially
int points = 0;
// keep playing until game over
restart:
waitForClick();
while (lives > 0 && bricks > 0)
{
// move paddle with mouse cursor
GEvent PaddleMove = getNextEvent(MOUSE_EVENT);
if (PaddleMove != NULL)
{
if (getEventType(PaddleMove) == MOUSE_MOVED)
{
double x = getX(PaddleMove) - 30;
setLocation(paddle, x, 560);
}
}
// sets ball in motion
move(ball, VelocityX, VelocityY);
// makes ball bounce off edges of window
if (getX(ball) + getWidth(ball) >= getWidth(window))
VelocityX = -VelocityX;
else if (getX(ball) <= 0)
VelocityX = -VelocityX;
else if (getY(ball) + getHeight(ball) >= getHeight(window))
{
setLocation(ball, WIDTH/2 - RADIUS*2, HEIGHT/2 - RADIUS*2);
lives--;
goto restart;
}
else if (getY(ball) <= 0)
VelocityY = -VelocityY;
// set behaviour of collisions
GObject object = detectCollision(window, ball);
if (object != NULL)
{
// collisions with paddle
if (strcmp(getType(object), "GRect") == 0)
{
VelocityY = -VelocityY;
// collisions with bricks
if (object != paddle)
{
removeGWindow (window, object);
points++;
updateScoreboard(window, scoreboard, points);
}
}
}
// allow pauses to avoid slowdown
pause(8);
}
// wait for click before exiting
waitForClick();
// game over
closeGWindow(window);
return 0;
}
/**
* Initializes window with a grid of bricks.
*/
void initBricks(GWindow window)
{
for (int n = 0; n < ROWS; n++)
{
int BlockYPosition = 55 + n*15;
int BlockXPosition = 2;
for (int m = 0; m < COLS; m++)
{
GRect block = newGRect (BlockXPosition + m*40, BlockYPosition, 36, 11);
if (n == 0)
setColor (block, "ff4eef");
if (n == 1)
setColor (block, "fff84e");
if (n == 2)
setColor (block, "58f2f6");
if (n == 3)
setColor (block, "ff4e84");
if (n == 4)
setColor (block, "ffbe43");
setFilled (block, true);
add(window, block);
}
}
}
/**
* Instantiates ball in center of window. Returns ball.
*/
GOval initBall(GWindow window)
{
GOval ball = newGOval(WIDTH/2 - RADIUS*2 + 11, HEIGHT/2 - RADIUS*2, RADIUS*2, RADIUS*2);
setColor (ball, "3664e0");
setFilled (ball, true);
add (window, ball);
return ball;
}
/**
* Instantiates paddle in bottom-middle of window.
*/
GRect initPaddle(GWindow window)
{
GRect paddle = newGRect (170, 560, 60, 10);
setColor (paddle, "3639e0");
setFilled (paddle, true);
add (window, paddle);
return paddle;
}
/**
* Instantiates, configures, and returns label for scoreboard.
*/
GLabel initScoreboard(GWindow window)
{
GLabel scoreboard = newGLabel("0");
setFont(scoreboard, "Helvetica-40");
double x = (getWidth(window) - getWidth(scoreboard)) / 2;
double y = (getHeight(window) - getHeight(scoreboard)) / 2;
setLocation(scoreboard, x, y);
setColor(scoreboard, "c1e3da");
add(window, scoreboard);
sendBackward(scoreboard);
return scoreboard;
}
/**
* Updates scoreboard's label, keeping it centered in window.
*/
void updateScoreboard(GWindow window, GLabel label, int points)
{
// update label
char s[12];
sprintf(s, "%i", points);
setLabel(label, s);
// center label in window
double x = (getWidth(window) - getWidth(label)) / 2;
double y = (getHeight(window) - getHeight(label)) / 2;
setLocation(label, x, y);
}
/**
* Detects whether ball has collided with some object in window
* by checking the four corners of its bounding box (which are
* outside the ball's GOval, and so the ball can't collide with
* itself). Returns object if so, else NULL.
*/
GObject detectCollision(GWindow window, GOval ball)
{
// ball's location
double x = getX(ball);
double y = getY(ball);
// for checking for collisions
GObject object;
// check for collision at ball's top-left corner
object = getGObjectAt(window, x, y);
if (object != NULL)
{
return object;
}
// check for collision at ball's top-right corner
object = getGObjectAt(window, x + 2 * RADIUS, y);
if (object != NULL)
{
return object;
}
// check for collision at ball's bottom-left corner
object = getGObjectAt(window, x, y + 2 * RADIUS);
if (object != NULL)
{
return object;
}
// check for collision at ball's bottom-right corner
object = getGObjectAt(window, x + 2 * RADIUS, y + 2 * RADIUS);
if (object != NULL)
{
return object;
}
// no collision
return NULL;
}
My code works just fine (maybe just a little bit more antsy than staff implementation). However, from the very beginning I wanted my game to have a black background. Having no success in finding the way to change the color of the game window itself, I decided to make a background by adding this bit of code just after instantiating game window:
GRoundRect background = newGRoundRect (0, 0, WIDTH, HEIGHT, 0);
setColor (background, "BLACK");
setFilled (background, true);
add (window, background);
It's just a black rectangle of the same size as the window. I used GRoundRect
instead of GRect
just so there would be no problems when it comes to detecting collisions at this part of code:
// collisions with paddle
if (strcmp(getType(object), "GRect") == 0)
{
VelocityY = -VelocityY;
// collisions with bricks
if (object != paddle)
{
removeGWindow (window, object);
points++;
updateScoreboard(window, scoreboard, points);
}
}
When I added this background, strange things started to happen:
I know that the background color is not the most important part of the game, but I spent quite some time trying to figure out what is going on and came to no solution. Nowhere in the code anything interacts with GRoundRect
type, yet it seems to create problems to the game. Any ideas?