diff --git a/breakout/assets/heartFull.png b/breakout/assets/heartFull.png new file mode 100644 index 0000000..c3881ba Binary files /dev/null and b/breakout/assets/heartFull.png differ diff --git a/breakout/assets/heartUsed.png b/breakout/assets/heartUsed.png new file mode 100644 index 0000000..891507f Binary files /dev/null and b/breakout/assets/heartUsed.png differ diff --git a/breakout/breakout.c b/breakout/breakout.c index 3c471d8..4ed3159 100644 --- a/breakout/breakout.c +++ b/breakout/breakout.c @@ -2,8 +2,11 @@ #include #include #include +#include #else #include +#include +#include #endif #include #include @@ -65,16 +68,20 @@ double heronsFormula(coord a, coord b, coord c) { double s = (A + B + C) / 2; return sqrt(s*(s - A)*(s - B)*(s - C)); } + +int loadTextures(GLuint *texture, const char *imageLoc); + char vertexWithinQuad(coord point, coord *quad); void updatePaddle(Uint32 tickrate, paddle *thePaddle, char moveX, int winWidth, int winHeight); -void updateBall(Uint32 tickrate, ball *theBall, paddle *thePaddle, int winWidth, int winHeight, brick *bricks, int brickCount); +void updateBall(Uint32 tickrate, ball *theBall, paddle *thePaddle, int winWidth, int winHeight, brick *bricks, int brickCount, int *playerLives); void updateScore(int *playerScore, brick *bricks, int brickCount); +void drawHearts(GLuint heartFullTex, GLuint heartEmptyTex, int playerLives, int winWidth, int winHeight); void drawPaddle(paddle *thePaddle, int winWidth, int winHeight, colour *c); void drawBall(ball *theBall, colour *c); -void drawBg(int winWidth, int winHeight, colour *c1, colour *c2); void drawBricks(brick *bricks, int brickCount, int winWidth, int winHeight, colour *colours); +void drawBg(int winWidth, int winHeight, colour *c1, colour *c2); void initialiseBricks(brick *bricks, int *brickCount); @@ -88,6 +95,7 @@ int main(void) { * 3: Error creating context * 4: SDL_TTF init fail * 5: Error loading font(s) + * 6: Error loading image asset(s) * */ @@ -162,6 +170,8 @@ int main(void) { glShadeModel(GL_SMOOTH); // GL_FLAT for flat shading instead glEnable(GL_DEPTH_TEST); // Enables depth comparisons glEnable(GL_TEXTURE_2D); // Enables use of textures + glAlphaFunc(GL_GREATER, 0); + glEnable(GL_ALPHA_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); @@ -220,6 +230,8 @@ int main(void) { int playerScore = 0; + int playerLives = 3; + /* * ---------------- * Font stuff @@ -238,6 +250,19 @@ int main(void) { .b = 255 }; + /* + * -------------------------------- + * Loading required textures + * -------------------------------- + */ + + GLuint heartFullTex; + GLuint heartEmptyTex; + if ( + loadTextures(&heartEmptyTex, "assets/heartUsed.png") || + loadTextures(&heartFullTex, "assets/heartFull.png") > 1) { + return 6; + } /* * ---------------- * Let's go! @@ -303,8 +328,11 @@ int main(void) { */ updatePaddle(tickrate, &thePaddle, moveX, winWidth, winHeight); - updateBall(tickrate, &theBall, &thePaddle, winWidth, winHeight, &bricks, brickCount); + updateBall(tickrate, &theBall, &thePaddle, winWidth, winHeight, &bricks, brickCount, &playerLives); updateScore(&playerScore, &bricks, brickCount); + if (playerLives < 1) { + go = 0; + } if (step % (subSteps+1) == 0) { //step = 0; @@ -316,7 +344,7 @@ int main(void) { SDL_GL_MakeCurrent(window1, context1); - sprintf(windowTitle, "Breakout: Score %d", playerScore); + sprintf(windowTitle, "Breakout: Score (%d), lives remaining (%d)", playerScore, playerLives); SDL_SetWindowTitle(window1, windowTitle); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); @@ -335,6 +363,7 @@ int main(void) { bgcol1.b = 0.6078; /*155*/ bgcol2.b = 0.4431; /*113*/ bgcol1.a = 1.0000; /*255*/ bgcol2.a = 1.0000; /*255*/ + drawHearts(heartFullTex, heartEmptyTex, playerLives, winWidth, winHeight); drawPaddle(&thePaddle, winWidth, winHeight, &paddleCol); drawBall(&theBall, &paddleCol); drawBricks(&bricks, brickCount, winWidth, winHeight, &brickColours); @@ -365,6 +394,37 @@ int main(void) { return 0; } +int loadTextures(GLuint *texture, const char *imageLoc) { + char errorMsg[512]; + sprintf(errorMsg, "[FAILED] Opening %s", imageLoc); + SDL_Surface *surface = IMG_Load(imageLoc); + if (surface == NULL) { + fprintf(stderr, errorMsg); + return 1; + } + *texture = 0; + + // Start of code from SDLTutorials.com + + int Mode = GL_RGB; + + glGenTextures(1, texture); + glBindTexture(GL_TEXTURE_2D, *texture); + + if (surface->format->BytesPerPixel == 4) { + Mode = GL_RGBA; + } + glTexImage2D(GL_TEXTURE_2D, 0, Mode, surface->w, surface->h, 0, Mode, GL_UNSIGNED_BYTE, surface->pixels); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // End of code from SDLTutorials.com + // Tim Jones (2011) SDL Surface to OpenGL Texture, Available at: http://www.sdltutorials.com/sdl-tip-sdl-surface-to-opengl-texture (Accessed: 10/01/2018) + + return 0; +} + char vertexWithinQuad(coord point, coord *quad) { double quadArea = heronsFormula(quad[0], quad[1], quad[2]) + heronsFormula(quad[1], quad[2], quad[3]); double testArea = 0; @@ -385,7 +445,7 @@ void updatePaddle(Uint32 tickrate, paddle *thePaddle, char moveX, int winWidth, if (thePaddle->x > winWidth-(thePaddle->width/2)) thePaddle->x = winWidth-(thePaddle->width/2); } -void updateBall(Uint32 tickrate, ball *theBall, paddle *thePaddle, int winWidth, int winHeight, brick *bricks, int brickCount) { +void updateBall(Uint32 tickrate, ball *theBall, paddle *thePaddle, int winWidth, int winHeight, brick *bricks, int brickCount, int *playerLives) { double ballNX = theBall->x + theBall->vX * 0.1 * tickrate; // Calculates the position of the ball's centre on the next step double ballNY = theBall->y + theBall->vY * 0.1 * tickrate; @@ -482,6 +542,7 @@ void updateBall(Uint32 tickrate, ball *theBall, paddle *thePaddle, int winWidth, theBall->y = 0; theBall->vX = theBall->initVX; theBall->vY = theBall->initVY; + *playerLives -= 1; } // Update the ball's position @@ -512,6 +573,34 @@ void updateScore(int *playerScore, brick *bricks, int brickCount) { } } +void drawHearts(GLuint heartFullTex, GLuint heartEmptyTex, int playerLives, int winWidth, int winHeight) { + coord locations[3] = { {.x = -19, .y = -32}, { .x = 0, .y = -32 }, { .x = 19, .y = -32 } }; + + for (int i = 0; i < 3; i++) { + if (i > playerLives-1) { + glBindTexture(GL_TEXTURE_2D, heartEmptyTex); + } else { + glBindTexture(GL_TEXTURE_2D, heartFullTex); + } + GLint matrixmode = 0; + glGetIntegerv(GL_MATRIX_MODE, &matrixmode); + glPushMatrix(); + glTranslated(0.0, (winHeight/2), 0.0); + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex3d(0.0+locations[i].x, 0.0+locations[i].y, 0.0); + glTexCoord2f(0, 1); glVertex3d(0.0+locations[i].x, -15.0 + locations[i].y, 0.0); + glTexCoord2f(1, 1); glVertex3d(15.0+locations[i].x, -15.0 + locations[i].y, 0.0); + glTexCoord2f(1, 0); glVertex3d(15.0+locations[i].x, 0.0 + locations[i].y, 0.0); + glEnd(); + + glPopMatrix(); + glMatrixMode(matrixmode); + } + + glBindTexture(GL_TEXTURE_2D, 0); +} + void drawBg(int winWidth, int winHeight, colour *c1, colour *c2) { GLint matrixmode = 0; glGetIntegerv(GL_MATRIX_MODE, &matrixmode);