diff --git a/assignment/assignment.cpp b/assignment/assignment.cpp new file mode 100644 index 0000000000000000000000000000000000000000..70526fde16acc0d4409d6fe76282a25cce1df14e --- /dev/null +++ b/assignment/assignment.cpp @@ -0,0 +1,415 @@ +// 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; +}