#ifdef _WIN32 #include #include #include #include #else #include #include #include #endif #include #include #include #include #include #include "include/structs.h" #include "include/utilityFunctions.h" #include "include/bricks.h" #include "include/paddleAndBall.h" int loadTextures(GLuint *texture, const char *imageLoc); void surfaceToTexture(GLuint *texture, SDL_Surface *surface); void updateScore(int *playerScore, brick *bricks, int *brickCount, int *level, ball *theBall); int main(void) { /* * RETURN CODES * 0: Success * 1: SDL init fail * 2: Error creating Window * 3: Error creating context * 4: SDL_TTF init fail * 5: Error loading font(s) * 6: Error loading image asset(s) * */ /* ---------------- * SDL Init * ---------------- */ if (SDL_Init(SDL_INIT_EVERYTHING)!=0) { perror("[FAILED] Initialising SDL: "); return 1; } if (TTF_Init() < 0) { perror("[FAILED] Initialising SDL_TTF: "); } int go; //Var for loop control int step = 0; //Var for step counter /* ---------------- * Main Window * ---------------- * Create the main output Window for SDL */ // Window vars int winPosX = 100; int winPosY = 100; int winWidth = 480; int winHeight = 720; char windowTitle[128] = "Breakout"; Uint32 timer; SDL_Window *window1 = SDL_CreateWindow( windowTitle, winPosX, winPosY, winWidth, winHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL ); if (window1==NULL) { fprintf(stderr, "[FAILED] Creating Window window1"); return 2; //Error creating Window } /* * ---------------- * Renderer * ---------------- * Creates the main renderer (tied to main window) */ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); // Specifies the version of the OpenGL context SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); // (here, 1.5) -- hello, 2003! SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); // Allows deprecated functions to be used SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); // Set depth buffer to 16 bits, if OpenGL agrees (see below) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // Enable double buffering SDL_GLContext context1 = SDL_GL_CreateContext(window1); // Use SDL_GL_MakeCurrent function to switch contexts if (context1 == NULL) { fprintf(stderr, "[FAILED] Creating render context context1 for window1"); return 3; } glFrontFace(GL_CCW); // Counter-clockwise winding glEnable(GL_NORMALIZE); // Vectors normalized after transforming to keep units consistent for lighting 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); // How we plan on treating aplha glEnable(GL_ALPHA_TEST); // Enables alpha testing (needed for transparent textures) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glMatrixMode(GL_PROJECTION); // Sets our matrix mode (initially) to work with the projection stack glLoadIdentity(); // Projection matrix is now the identity matrix, so we can work in 2D space gluOrtho2D( -1.0*(GLdouble)(winWidth / 2), // Left (GLdouble)(winWidth / 2), // Right -1.0*(GLdouble)(winHeight / 2), // Bottom (GLdouble)(winHeight / 2) // Top ); glViewport(0, 0, winWidth, winHeight); // Set the viewport to fill the window timer = SDL_GetTicks(); /* * ---------------- * Game variables * ---------------- */ char subSteps = 1; // Draw every N+1 frames, thereby calculating movement more precisely int targetFPS = 60; // Limit FPS to this value, to stop unneccessary calculations char moveX=0; // Toggled between -1, 0 and 1 by keypresses later int playerScore = 0; int level = 1; int playerLives = 3; brick bricks[2048]; // Sets up array for the bricks to be stored in, max no. bricks is 2048 int brickCount; colour brickColours[7]; //brickColours[n] corresponds to the colour of a brick with type n+1 brickColours[6] = (colour) { .r = 0.9000 /*255*/, .g = 0.9000 /*64 */, .b = 0.9000 /*167*/, .a = 1 }; brickColours[5] = (colour) { .r = 0.8000 /*255*/, .g = 0.8000 /*64 */, .b = 0.8000 /*167*/, .a = 1 }; brickColours[4] = (colour) { .r = 0.7000 /*255*/, .g = 0.7000 /*64 */, .b = 0.7000 /*167*/, .a = 1 }; brickColours[3] = (colour) { .r = 1.0000 /*255*/, .g = 0.2510 /*64 */, .b = 0.6549 /*167*/, .a = 1 }; brickColours[2] = (colour) { .r = 0.4588 /*117*/, .g = 0.5490 /*140*/, .b = 1.0000 /*255*/, .a = 1 }; brickColours[1] = (colour) { .r = 0.3490 /*89 */, .g = 0.8706 /*222*/, .b = 1.0000 /*255*/, .a = 1 }; brickColours[0] = (colour) { .r = 1.0000 /*255*/, .g = 0.7569 /*193*/, .b = 0.1216 /*31 */, .a = 1 }; initialiseBricks(&bricks, &brickCount, level); // Sets up the bricks for level one colour paddleCol; paddleCol.r = 1.0; paddleCol.g = 1.0; paddleCol.b = 1.0; paddleCol.a = 1.0; // RGBA of paddle and ball colour bgcol1; colour bgcol2; bgcol1.r = 0.7216; /*184*/ bgcol2.r = 0.0824; /*21 */ // RGBA values for the gradient, bgcol1 refers to the bottom colour and bgcol2 to the top bgcol1.g = 0.2471; /*63 */ bgcol2.g = 0.0901; /*23 */ bgcol1.b = 0.6078; /*155*/ bgcol2.b = 0.4431; /*113*/ bgcol1.a = 1.0000; /*255*/ bgcol2.a = 1.0000; /*255*/ ball theBall = { .x = 0, .y = 0, .vX = 1.5f, // Change to desired X / Y speeds .vY = -5.0f, .initVX = theBall.vX, .initVY = theBall.vY, .radius = 4, .isColliding = 0, .stickToPaddle = 1 }; // Initialising the ball paddle thePaddle = { .width = 50, .height = 10, .x = (winWidth/2)-(thePaddle.width/2) }; // Initialising the paddle char txtL[4] = "000"; // Placeholder text char txtR[4] = "000"; /* * ---------------- * Font stuff * ---------------- */ TTF_Font* font_main = TTF_OpenFont("assets/font_main.ttf", 56); if (font_main == NULL) { printf("[FAILED] Unable to load font\n"); return 5; } SDL_Color textColour = { .a = 255, .r = 255, .g = 255, .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! * ---------------- */ go=1; while(go) { /* ---------------- * Timing control * ---------------- * */ Uint32 tickrate=SDL_GetTicks()-timer; timer=SDL_GetTicks(); int fps=1000/(tickrate+1); /* * ---------------- * Event handling * ---------------- */ SDL_Event incomingEvent; while (SDL_PollEvent(&incomingEvent)) { switch (incomingEvent.type) { case SDL_QUIT: // User requested program quit - e.g. window close go=0; break; case SDL_KEYDOWN: switch(incomingEvent.key.keysym.sym) { case SDLK_ESCAPE: go = 0; break; case SDLK_LEFT: moveX=-1; break; case SDLK_RIGHT: moveX=1; break; case SDLK_SPACE: if (playerLives>0) theBall.stickToPaddle = 0; else { playerLives = 3; level = 1; theBall.stickToPaddle = 1; initialiseBricks(&bricks, &brickCount, level); } break; } break; case SDL_KEYUP: switch(incomingEvent.key.keysym.sym) { case SDLK_LEFT: moveX=0; break; case SDLK_RIGHT: moveX=0; break; } break; } } /* * ---------------- * Update elements * ---------------- */ if (playerLives > 0) { sprintf(txtL, "%03d", playerScore); sprintf(txtR, "%03d", level); updatePaddle(tickrate, &thePaddle, moveX, winWidth, winHeight); updateBall(tickrate, &theBall, &thePaddle, winWidth, winHeight, &bricks, brickCount, &playerLives); updateScore(&playerScore, &bricks, &brickCount, &level, &theBall); } if (level > 3) { printf("Congratulations! You won!\n"); go = 0; } if (step % (subSteps+1) == 0) { //step = 0; /* * ---------------- * Draw to buffer * ---------------- */ SDL_GL_MakeCurrent(window1, context1); sprintf(windowTitle, "Breakout: Score (%d), lives remaining (%d)", playerScore, playerLives); SDL_SetWindowTitle(window1, windowTitle); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (playerLives==0) { drawText("Game Over", font_main, textColour, (coord){ .x=0, .y=-128}); } drawText(txtL, font_main, textColour, (coord){ .x=-128, .y=(winHeight/2)-32 }); drawText(txtR, font_main, textColour, (coord){ .x=128, .y=(winHeight/2)-32 }); drawHearts(heartFullTex, heartEmptyTex, playerLives, winWidth, winHeight); drawPaddle(&thePaddle, winWidth, winHeight, &paddleCol); drawBall(&theBall, &paddleCol); drawBricks(&bricks, brickCount, winWidth, winHeight, &brickColours); drawBg(winWidth, winHeight, &bgcol1, &bgcol2); glFlush(); /* * -------------------------------- * Output to Window * -------------------------------- */ SDL_GL_SwapWindow(window1); } if (SDL_GetTicks() - timer < 1000 / (targetFPS * (subSteps+1))) SDL_Delay((1000 / (targetFPS * (subSteps+1))) - (SDL_GetTicks() - timer)); step++; } /* * -------------------------------- * Clean up after ourselves * -------------------------------- */ SDL_GL_DeleteContext(context1); SDL_DestroyWindow(window1); SDL_Quit(); printf("Your score was: %d, with %d lives remaining\n", playerScore, playerLives); 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; } void surfaceToTexture(GLuint *texture, SDL_Surface *surface) { *texture = 0; 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); } void updateScore(int *playerScore, brick *bricks, int *brickCount, int *level, ball *theBall) { *playerScore = 0; char allBricksDestroyed = 1; for (int i = 0; i < *brickCount; i++) { if (bricks[i].destroyed == 1) { switch (bricks[i].brickType) { case 1: *playerScore += 1; break; case 2: *playerScore += 3; break; case 3: *playerScore += 5; break; case 4: *playerScore += 7; break; } } else { if (bricks[i].brickType<5) allBricksDestroyed = 0; // Excludes 'power-up bricks' from having to be destroyed before moving on to the next level } } if (allBricksDestroyed==1) { *level += 1; initialiseBricks(bricks, brickCount, *level); theBall->stickToPaddle = 1; } }