// Included with assignment #include <iostream> #include <iomanip> #include <vector> #include <cstring> #include <tuple> // Included by 21015407 GARETH CHAPPELL // For rand() function #include <stdlib.h> // For time value #include <time.h> // Included with assignment // Accessing other files #include <context.hpp> #include <app.hpp> using namespace std; // NOTE: // I *still* can't get the batch files to work, and I don't know why. // Init values used to determine the width/ // Modified the window dimension values from what was assigned by the assignment, so they're easier to work with in setting up UI and math stuff. const int width = 800; const int height = 600; // GAME SCREENS // 1. Title Screen - The player starts on the title screen. When they press A or D, the title screen closes, and the gameplay begins. // 2. Gameplay - The player controls a paddle with the A/D keys. The player must keep hitting a moving ball with the paddle // to prevent it from hitting the bottom of the screen, which ends the gameplay and displays the game over screen. A timer is displayed on the // top left of the screen, indicating the player's score. // 3. Game Over - When the player fails at the gameplay, the game over screen is displayed, showing their time score. Player can then press A or D // to play again. class MyApp: public uwe::App { private: // Init int value for generating random numbers int random_x; // Init values for top left of ball int ball_x_topleft; int ball_y_topleft; // Init value for top left of paddle int paddle_x_topleft; // Init int values for ball X/Y co-ordinates int ball_x; int ball_y; // Init int values for ball X/Y gradient values // These values are incremented/decremented every minute, increasing the difficulty over time. int ball_gradient_x; int ball_gradient_y; // Init int value for paddle X co-ordinates // Paddle Y is always 650 int paddle_x; // Init boolean flags for game screens bool title_active = true; bool gameplay_active = false; bool gameover_active = false; // Init int value for timer, in seconds and minutes, and a counter to add 1 second per 60 frames. int counter; int time_seconds; int time_minutes; public: MyApp(int width, int height, std::string title); ~MyApp(); void begin() override; void update() override; void draw() override; void key_pressed(uwe::Scancode scancode, bool repeat) override; void mouse_pressed(int x, int y, uwe::Button button) override; void mouse_released(int x, int y, uwe::Button button) override; void mouse_moved(int x, int y) override; }; MyApp::MyApp(int width, int height, std::string title) { init(width, height, title); } MyApp::~MyApp() { } void MyApp::begin() { // Init the random number seed with the time value. srand (time(NULL)); // Init the font used for UI text. UI_font = create_font("./assets/fonts/FreeSans.ttf", 15, uwe::Colour::white()); // Adjust ball X/Y values ball_x = 400; ball_y = 600; // Randomise the X gradient for the start. random_x = rand() % 1 - 2; switch (random_x) { case 1: ball_gradient_x = -1; break; case 2: ball_gradient_x = 1; break; } // Set the Y gradient for the ball. ball_gradient_y = -1; } // This functions occur every frame (60 times per second) void MyApp::update() { if (gameplay_active == true) { // Update paddle topleft paddle_x_topleft = paddle_x - 25; // Update ball topleft ball_x_topleft = ball_x - 10; ball_y_topleft = ball_y - 10; // Update ball X/Y ball_x = ball_x + ball_gradient_x; ball_y = ball_y + ball_gradient_y; // If counter value is 60 // (i.e Every second) if (counter == 60) { // Increment seconds timer, reset counter timer_seconds++; counter = 0; // Adjust the gradients (in other words, the speed of the ball) // Increment ball gradient X if positive if (ball_gradient_x > 0) { ball_gradient_x++; } // Decrement ball gradient X if positive if (ball_gradient_x < 0) { ball_gradient_x--; } // Increment ball gradient Y if positive if (ball_gradient_y > 0) { ball_gradient_y++; } // Decrement ball gradient Y if positive if (ball_gradient_y < 0) { ball_gradient_y--; } } // If timer seconds value is 60 if (timer_seconds == 60) { // Increment minute timer, reset seconds timer_minutes++; timer_seconds = 0; } // If timer minutes is greater than 99, which would cause a minor visual error. if (timer_minutes > 99) { timer_seconds = 0; timer_minutes = 0; } // If ball is touching either left wall and right wall, and not touching the top or bottom wall. if ((ball_x == 120 || ball_x == 680) && (ball_y != 680 && ball_y != 120)) { // Multiply the X gradient by -1 to 'invert' the X gradient // (i.e If the X gradient is 1, it is now -1. If the X gradient is -1, it is now 1.) ball_gradient_x = ball_gradient_x * -1; } // If ball is not touching either the left or right wall, and touching the top wall. if ((ball_x != 120 && ball_x != 680) && ball_y == 120) { // Multiply the Y gradient by -1 to invert the Y gradient. // (i.e If the Y gradient is 1, it is now -1. If the Y gradient is -1, it is now 1. ball_gradient_y = ball_gradient_y * -1; // 50% chance to invert the X gradient. random_x = rand() % 1 - 2; switch (random_x) { case 1: ball_gradient_x = ball_gradient_x * -1; break; case 2: break; } } // If the ball is touching the left or right wall, and is touching the top wall (i.e a perfect corner shot) if ((ball_x == 120 || ball_x == 680) && ball_y == 120) { // Invert both gradients ball_gradient_x = ball_gradient_x * -1; ball_gradient_y = ball_gradient_y * -1; } // If the ball is touching the bottom wall (game over) if (ball_y == 680) { gameplay_active = false; gameover_active = true; } // If ball is touching the top of the paddle, and within the X co-ordinate of the paddle if (ball_y == 630 && (ball_x > paddle_x - 50) && (ball_x < paddle_x + 50)) { // Invert the Y gradient ball_gradient_y = ball_gradient_y * -1; // 50% chance to invert the X gradient, introducing a degree of randomness to the game random_x = rand() % 1 - 2; switch (random_x) { case 1: ball_gradient_x = ball_gradient_x * -1; break; case 2: break; } } } } // When a key is pressed, this function is called. void MyApp::key_pressed(uwe::Scancode scancode, bool repeat) { switch (scancode) { // If 'A' key is pressed case uwe::Scancode::A: { // If title active if (title_active == true) { gameplay_active = true; title_active = false; } // If gameplay active and the paddle isn't touching the left wall if (gameplay_active == true && paddle_x != 125) { paddle_x--; } // If gameover active if (gameover_active == true) { gameplay_active = true; gameover_active = false; } } // If 'D' key is pressed case uwe::Scancode::D: { // If title active if (title_active == true) { gameplay_active = true; title_active = false; } // If gameplay active and the paddle isn't touching the right wall if (gameplay_active == true && paddle_x != 675) { paddle_x++; } // If gameover active if (gameover_active == true) { gameplay_active = true; gameover_active = false; } } default: { // nothing see here } } } // When the mouse button is pressed, this function is called. void MyApp::mouse_pressed(int x, int y, uwe::Button button) { // Unused. } // When the mouse button is released, this function is called. void MyApp::mouse_released(int x, int y, uwe::Button button) { // Unused. } // When the mouse is moved, this function is called. void MyApp::mouse_moved(int x, int y) { // Unused. } // This function is called per frame, to draw the contents of the window. void MyApp::draw() { // Cover the screen in black // This happens with every frame to avoid visual cluttering. clear(uwe::Colour::black()); // Set the draw colour as white set_draw_color(uwe::Colour(255,255,255,255)); // Title Screen if (title_active == true) { // Draw text draw_font(UI_Font, "Bouncy Ball Game!", 100, 100); draw_font(UI_Font, "Instructions:", 100, 125); draw_font(UI_Font, "> Keep the ball away from the bottom of the screen for as long as possible. Hit it with your paddle!", 100, 150); draw_font(UI_Font, "> Use the A and D keys to move the paddle left and right.", 100, 175); draw_font(UI_Font, "> Good luck!", 100, 200); } // Gameplay if (gameplay_active == true) { // Timer draw_font(UI_Font, timer_minutes, 100, 50); draw_font(UI_Font, ":", 130, 50); draw_font(UI_Font, timer_seconds, 100, 50); // Boundary draw_line(100, 100, 700, 100); // Top left to top right draw_line(700, 100, 700, 700); // Top right to bottom right draw_line(700, 700, 100, 700); // Bottom right to bottom left draw_line(100, 700, 100, 100); // Bottom left to top left // Ball draw_rect(ball_x_topleft, ball_y_topleft, 20, 20); // Paddle draw_rect(paddle_x_topleft, 650, 50, 10); } if (gameover_active == true) { // Draw text draw_font(UI_Font, "Game over!", 100, 100); draw_font(UI_Font, "Score:", 100, 125); draw_font(UI_Font, timer_minutes, 100, 150); draw_font(UI_Font, "minutes!", 145, 150); draw_font(UI_Font, "Press A or D to restart!", 100, 175); } } int main(int argc, char *argv[]) { uwe::App* app = new MyApp{width, height, "21015407 Assignment RESIT - BOUNCY BALL"}; app->run(); return 0; }