From: Nils Forssén Date: Thu, 9 Nov 2023 12:18:47 +0000 (+0100) Subject: Changed to cc files X-Git-Url: https://gitweb.forssennils.se/?a=commitdiff_plain;h=93252bb29328c0fd82869dd11556f29d15d3169e;p=TDDC76_proj.git Changed to cc files --- diff --git a/src/Ball.cc b/src/Ball.cc new file mode 100644 index 0000000..edcfbcf --- /dev/null +++ b/src/Ball.cc @@ -0,0 +1,101 @@ +#include "Ball.h" + +#include +#include + +using namespace std; + +/* + * One way to create variables, functions and classes + * which are only visible in one file is by using an + * anonymous namespace (that is, a namespace which + * has no name). + * + * This will ensure that no other file will get + * access to these variables. + */ +namespace +{ + /* + * an engine which generates random numbers + */ + random_device rd { }; + + /* + * wrappers which defines various ranges + * we want to generate numbers in. + */ + + // used to randomly choose what sprite to use + uniform_int_distribution index { 0, 1 }; + + // randomly choose a radius + uniform_real_distribution radius { 10, 50 }; + + // randomly choose a velocity direction + uniform_real_distribution angle { 0, 2*3.14 }; + + // randomly choose a speed for our ball + uniform_real_distribution length { 0.5, 8.0 }; +} + +/* + * This constructor uses some basic trigonometry + * to create a velocity vector. + */ +//Ball :: Ball (Spritesheet & sheet, +// float x, +// float y) +// : sprite { sheet.get_sprite (0, index (rd)) }, +// velocity { } +//{ +// float direction { angle (rd) }; +// float speed { length (rd) }; +// +// velocity = sf::Vector2f { speed * cos (direction), +// speed * sin (direction) }; +// +// /* position the ball, and make sure that we place +// * the center of the ball where the mouse clicked */ +// auto sprite_size { sheet.sprite_size () }; +// sprite.setPosition (x, y); +// sprite.setOrigin (sprite_size.x / 2, sprite_size.y / 2); +//} + +Ball :: Ball (float x, + float y) + : + sprite{radius(rd)}, velocity { } +{ + float direction { angle (rd) }; + float speed { length (rd) }; + + velocity = sf::Vector2f { speed * cos (direction), + speed * sin (direction) }; + + /* position the ball, and make sure that we place + * the center of the ball where the mouse clicked */ + + // Create a sprite + float sprite_size { sprite.getRadius () }; + sprite.setPosition (x, y); + sprite.setOrigin (sprite_size / 2, sprite_size / 2); +} + +void Ball :: update () +{ + // move the sprite according to the velocity + sprite.move (velocity); +} + +void Ball :: render (sf::RenderTarget & target) +{ + // draw the sprite + target.draw (sprite); +} + +sf::FloatRect Ball :: bounds () const +{ + // get the hitbox of the sprite + return sprite.getGlobalBounds (); +} diff --git a/src/Ball.cpp b/src/Ball.cpp deleted file mode 100644 index edcfbcf..0000000 --- a/src/Ball.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "Ball.h" - -#include -#include - -using namespace std; - -/* - * One way to create variables, functions and classes - * which are only visible in one file is by using an - * anonymous namespace (that is, a namespace which - * has no name). - * - * This will ensure that no other file will get - * access to these variables. - */ -namespace -{ - /* - * an engine which generates random numbers - */ - random_device rd { }; - - /* - * wrappers which defines various ranges - * we want to generate numbers in. - */ - - // used to randomly choose what sprite to use - uniform_int_distribution index { 0, 1 }; - - // randomly choose a radius - uniform_real_distribution radius { 10, 50 }; - - // randomly choose a velocity direction - uniform_real_distribution angle { 0, 2*3.14 }; - - // randomly choose a speed for our ball - uniform_real_distribution length { 0.5, 8.0 }; -} - -/* - * This constructor uses some basic trigonometry - * to create a velocity vector. - */ -//Ball :: Ball (Spritesheet & sheet, -// float x, -// float y) -// : sprite { sheet.get_sprite (0, index (rd)) }, -// velocity { } -//{ -// float direction { angle (rd) }; -// float speed { length (rd) }; -// -// velocity = sf::Vector2f { speed * cos (direction), -// speed * sin (direction) }; -// -// /* position the ball, and make sure that we place -// * the center of the ball where the mouse clicked */ -// auto sprite_size { sheet.sprite_size () }; -// sprite.setPosition (x, y); -// sprite.setOrigin (sprite_size.x / 2, sprite_size.y / 2); -//} - -Ball :: Ball (float x, - float y) - : - sprite{radius(rd)}, velocity { } -{ - float direction { angle (rd) }; - float speed { length (rd) }; - - velocity = sf::Vector2f { speed * cos (direction), - speed * sin (direction) }; - - /* position the ball, and make sure that we place - * the center of the ball where the mouse clicked */ - - // Create a sprite - float sprite_size { sprite.getRadius () }; - sprite.setPosition (x, y); - sprite.setOrigin (sprite_size / 2, sprite_size / 2); -} - -void Ball :: update () -{ - // move the sprite according to the velocity - sprite.move (velocity); -} - -void Ball :: render (sf::RenderTarget & target) -{ - // draw the sprite - target.draw (sprite); -} - -sf::FloatRect Ball :: bounds () const -{ - // get the hitbox of the sprite - return sprite.getGlobalBounds (); -} diff --git a/src/Game.cc b/src/Game.cc new file mode 100644 index 0000000..f40f3d9 --- /dev/null +++ b/src/Game.cc @@ -0,0 +1,128 @@ +#include "constants.h" +#include "Game.h" +#include "Menu_State.h" +#include "Game_State.h" + +using namespace sf; + +/* + * Initialize the window and the first state is Menu_State. + * + * The constructor of RenderWindow takes a 'VideoMode' + * which defines the width, height and bits per pixel + * (default 32), a title which should be visible in + * the titlebar of the windows, as well as a style flag. + * The style flag is a bit mask (i.e. a value where + * each bit represents a property) of properties we + * want our window to have. If we do not care, + * we can omitt this argument or use sf::Style::Default. + */ +Game :: Game (std::string const & title, + unsigned width, + unsigned height) + : window { VideoMode { width, height }, + title, Style::Titlebar | Style::Close }, + current_state{ MENU_STATE }, + running { true } +{ + // Insert all sates you want in your game in the states map + states.insert(std::pair>({MENU_STATE, + std::make_unique()})); + + states.insert(std::pair>({GAME_STATE, + std::make_unique()})); +} + + +void Game :: start () +{ + // The clock is used to measure of long each iteration took + // this is used to keep the framerate as steady as possible. + Clock clock { }; + while ( running ) + { + // Handle user events e.g. mouse click or key pressed + handle_events(); + + // Let the current state do its update + states.at(current_state) -> update(); + + /* + * clear fills the entire window with one color + * (by default black) thus overwriting everything + * that was drawn before. + * + * If we do not perform this step we allow for weird + * graphical artifacts to show up. + */ + window.clear (); + + // let the current state render itself onto the window + states.at(current_state) -> render(window); + + /* + * No drawn pixels will be shown in the window + * until this function is called. + * + * All drawing operations are performed on a + * hidden buffer in memory, so if we want them + * to actually show up on the screen we have + * make sure that the window switches to drawing + * that buffer instead of its current one. + * (This technique is called 'double buffering') + */ + window.display (); + + /* + * When all logic and rendering has been performed + * we are now ready to update the current_state + */ + current_state = states.at(current_state) -> get_next_state(); + + /* + * Wait if we still haven't reached the target + * time for a frame. + */ + delay (clock); + } +} + + +void Game :: handle_events () +{ + /* + * event is an object which contains all + * relevant information for an event that + * occured in the window (i.e. key-pressed, + * mouse clicks etc.). + * + * The function 'pollEvent' takes the next + * event in the event queue and places it + * in the 'event' variable so that we can + * read what that event was. + * + * While there are events in event queue + * 'pollEvent' will return true. + */ + Event event; + while ( window.pollEvent (event) ) + { + // Check if the window has been closed. + // This event fires whenever the user + // presses the X icon, or if the operating + // system kills it. + if ( event.type == Event::Closed ) + running = false; + + // send the event to 'state' + states.at(current_state) -> handle_event (event); + } +} + +void Game :: delay (sf::Clock & clock) const +{ + sleep (milliseconds (1000.0 / fps) - clock.getElapsedTime ()); + clock.restart (); +} diff --git a/src/Game.cpp b/src/Game.cpp deleted file mode 100644 index f40f3d9..0000000 --- a/src/Game.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "constants.h" -#include "Game.h" -#include "Menu_State.h" -#include "Game_State.h" - -using namespace sf; - -/* - * Initialize the window and the first state is Menu_State. - * - * The constructor of RenderWindow takes a 'VideoMode' - * which defines the width, height and bits per pixel - * (default 32), a title which should be visible in - * the titlebar of the windows, as well as a style flag. - * The style flag is a bit mask (i.e. a value where - * each bit represents a property) of properties we - * want our window to have. If we do not care, - * we can omitt this argument or use sf::Style::Default. - */ -Game :: Game (std::string const & title, - unsigned width, - unsigned height) - : window { VideoMode { width, height }, - title, Style::Titlebar | Style::Close }, - current_state{ MENU_STATE }, - running { true } -{ - // Insert all sates you want in your game in the states map - states.insert(std::pair>({MENU_STATE, - std::make_unique()})); - - states.insert(std::pair>({GAME_STATE, - std::make_unique()})); -} - - -void Game :: start () -{ - // The clock is used to measure of long each iteration took - // this is used to keep the framerate as steady as possible. - Clock clock { }; - while ( running ) - { - // Handle user events e.g. mouse click or key pressed - handle_events(); - - // Let the current state do its update - states.at(current_state) -> update(); - - /* - * clear fills the entire window with one color - * (by default black) thus overwriting everything - * that was drawn before. - * - * If we do not perform this step we allow for weird - * graphical artifacts to show up. - */ - window.clear (); - - // let the current state render itself onto the window - states.at(current_state) -> render(window); - - /* - * No drawn pixels will be shown in the window - * until this function is called. - * - * All drawing operations are performed on a - * hidden buffer in memory, so if we want them - * to actually show up on the screen we have - * make sure that the window switches to drawing - * that buffer instead of its current one. - * (This technique is called 'double buffering') - */ - window.display (); - - /* - * When all logic and rendering has been performed - * we are now ready to update the current_state - */ - current_state = states.at(current_state) -> get_next_state(); - - /* - * Wait if we still haven't reached the target - * time for a frame. - */ - delay (clock); - } -} - - -void Game :: handle_events () -{ - /* - * event is an object which contains all - * relevant information for an event that - * occured in the window (i.e. key-pressed, - * mouse clicks etc.). - * - * The function 'pollEvent' takes the next - * event in the event queue and places it - * in the 'event' variable so that we can - * read what that event was. - * - * While there are events in event queue - * 'pollEvent' will return true. - */ - Event event; - while ( window.pollEvent (event) ) - { - // Check if the window has been closed. - // This event fires whenever the user - // presses the X icon, or if the operating - // system kills it. - if ( event.type == Event::Closed ) - running = false; - - // send the event to 'state' - states.at(current_state) -> handle_event (event); - } -} - -void Game :: delay (sf::Clock & clock) const -{ - sleep (milliseconds (1000.0 / fps) - clock.getElapsedTime ()); - clock.restart (); -} diff --git a/src/Game_State.cc b/src/Game_State.cc new file mode 100644 index 0000000..090e528 --- /dev/null +++ b/src/Game_State.cc @@ -0,0 +1,156 @@ +#include "Game_State.h" +#include "constants.h" + +#include + +using namespace sf; + +/* + * Check for mouse button presses. + * + * If a mouse button press is detected, when that + * mouse button is release we add a new ball to + * the screen at the mouse's location. + * + * This demonstrates how we can integrate the mouse + * into our project. + */ +void Game_State :: handle_event (Event event) +{ + if ( event.type == Event::MouseButtonReleased ) + { + if ( event.mouseButton.button == Mouse::Button::Left ) + { + balls.emplace_back ( + // get the coordinates of the mouse right now + event.mouseButton.x, + event.mouseButton.y); + } + } + else if ( event.type == Event::KeyPressed ) + { + if ( event.key.code == Keyboard::Key::Return ) + end_game = true; + } +} + +void Game_State :: update () +{ + // Iterate over all balls and let + // them update themselves. + for ( auto & ball : balls ) + ball.update (); +} + +void Game_State :: render (RenderTarget & target) +{ + // Let each ball render itself onto the + // supplied RenderTarget + for ( auto & ball : balls ) + ball.render (target); +} + +/* + * If return was pressed, we jump back to MENU_STATE, + * otherwise we stay in this state GAME_STATE + */ +int Game_State :: get_next_state() +{ + if (end_game) + { + end_game = false; + return MENU_STATE; + } + return GAME_STATE; +} + +/* + * If we want to remove all balls which are outside + * of the screen we need to iterate all balls and + * do two things for each ball: + * 1. Check if it is outside of the screen + * 2. If it is, make sure that we remove it from + * the vector. + * + * The first part can be solved in several ways. + * We can for instance implement our own logic + * for checking if a sprite is on the screen + * or not, or we can use some built-in functions + * in SFML. + * + * In this example we use the latter. + * + * We begin by getting the global bounds of each ball. + * + * A bounds is the smallest possible rectangle which + * covers the entire ball (these are basically the + * "hitboxes" of our balls). + * + * There are two kinds of bounds for each ball, a + * global bound and a local bound, the difference + * is where the (0, 0) coordinate is placed. + * + * In a global bound, (0, 0) referes to top-left + * corner of the window, while in a local bounds + * it referes to the origin of the ball (in this + * example, the center of the ball (see Ball.cpp + * for details)). + * + * Using the global bounds we can use the built-in + * 'intersects' function to check if the bounds + * intersects the screen. If it does, it is still + * visible, if not it is safe to remove. + * + * The second part is a bit trickier. + * + * A problem arises when we iterate over the + * balls and then remove one of them; the current + * index has been changed so we have now potentially + * skipped over an element. + * + * Example of how this might happen: + * + * Say that we are iterating over this list, with + * a standard index based for-loop (here ^ denotes + * the element we are currently looking at): + * + * 1 5 7 2 4 3 + * ^ + * Now say that we remove the 7, then we have: + * + * 1 5 2 4 3 + * ^ + * Note that ^ now points to the next element, but the + * same location as before, which might at first glance + * seem like what we exactly want. But we must not + * forget that the final step in a for loop is to step + * the index, so the next iteration will look like this: + * + * 1 5 2 4 3 + * ^ + * Which shows that we have skipped an element. + * + * This means that we have to be a bit more cleaver + * when removing elements from the list we are + * iterating. + * + * But we are in luck, we can use an iterator based + * for-loop, since erase (the function which removes + * elements from the vector) removes elements pointed + * to by iterators, and returns an iterator to the next + * element. + */ +void Game_State :: cleanup () +{ + for ( auto it { std::begin (balls) }; it != std::end (balls); ) + { + // get the global bounds of our current ball + auto bounds { it -> bounds () }; + // get a rectangle representing the screen + FloatRect screen { 0, 0, screen_width, screen_height }; + if ( !screen.intersects (bounds) ) + it = balls.erase (it); + else + ++it; + } +} diff --git a/src/Game_State.cpp b/src/Game_State.cpp deleted file mode 100644 index 090e528..0000000 --- a/src/Game_State.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "Game_State.h" -#include "constants.h" - -#include - -using namespace sf; - -/* - * Check for mouse button presses. - * - * If a mouse button press is detected, when that - * mouse button is release we add a new ball to - * the screen at the mouse's location. - * - * This demonstrates how we can integrate the mouse - * into our project. - */ -void Game_State :: handle_event (Event event) -{ - if ( event.type == Event::MouseButtonReleased ) - { - if ( event.mouseButton.button == Mouse::Button::Left ) - { - balls.emplace_back ( - // get the coordinates of the mouse right now - event.mouseButton.x, - event.mouseButton.y); - } - } - else if ( event.type == Event::KeyPressed ) - { - if ( event.key.code == Keyboard::Key::Return ) - end_game = true; - } -} - -void Game_State :: update () -{ - // Iterate over all balls and let - // them update themselves. - for ( auto & ball : balls ) - ball.update (); -} - -void Game_State :: render (RenderTarget & target) -{ - // Let each ball render itself onto the - // supplied RenderTarget - for ( auto & ball : balls ) - ball.render (target); -} - -/* - * If return was pressed, we jump back to MENU_STATE, - * otherwise we stay in this state GAME_STATE - */ -int Game_State :: get_next_state() -{ - if (end_game) - { - end_game = false; - return MENU_STATE; - } - return GAME_STATE; -} - -/* - * If we want to remove all balls which are outside - * of the screen we need to iterate all balls and - * do two things for each ball: - * 1. Check if it is outside of the screen - * 2. If it is, make sure that we remove it from - * the vector. - * - * The first part can be solved in several ways. - * We can for instance implement our own logic - * for checking if a sprite is on the screen - * or not, or we can use some built-in functions - * in SFML. - * - * In this example we use the latter. - * - * We begin by getting the global bounds of each ball. - * - * A bounds is the smallest possible rectangle which - * covers the entire ball (these are basically the - * "hitboxes" of our balls). - * - * There are two kinds of bounds for each ball, a - * global bound and a local bound, the difference - * is where the (0, 0) coordinate is placed. - * - * In a global bound, (0, 0) referes to top-left - * corner of the window, while in a local bounds - * it referes to the origin of the ball (in this - * example, the center of the ball (see Ball.cpp - * for details)). - * - * Using the global bounds we can use the built-in - * 'intersects' function to check if the bounds - * intersects the screen. If it does, it is still - * visible, if not it is safe to remove. - * - * The second part is a bit trickier. - * - * A problem arises when we iterate over the - * balls and then remove one of them; the current - * index has been changed so we have now potentially - * skipped over an element. - * - * Example of how this might happen: - * - * Say that we are iterating over this list, with - * a standard index based for-loop (here ^ denotes - * the element we are currently looking at): - * - * 1 5 7 2 4 3 - * ^ - * Now say that we remove the 7, then we have: - * - * 1 5 2 4 3 - * ^ - * Note that ^ now points to the next element, but the - * same location as before, which might at first glance - * seem like what we exactly want. But we must not - * forget that the final step in a for loop is to step - * the index, so the next iteration will look like this: - * - * 1 5 2 4 3 - * ^ - * Which shows that we have skipped an element. - * - * This means that we have to be a bit more cleaver - * when removing elements from the list we are - * iterating. - * - * But we are in luck, we can use an iterator based - * for-loop, since erase (the function which removes - * elements from the vector) removes elements pointed - * to by iterators, and returns an iterator to the next - * element. - */ -void Game_State :: cleanup () -{ - for ( auto it { std::begin (balls) }; it != std::end (balls); ) - { - // get the global bounds of our current ball - auto bounds { it -> bounds () }; - // get a rectangle representing the screen - FloatRect screen { 0, 0, screen_width, screen_height }; - if ( !screen.intersects (bounds) ) - it = balls.erase (it); - else - ++it; - } -} diff --git a/src/Menu_State.cc b/src/Menu_State.cc new file mode 100644 index 0000000..fefb66f --- /dev/null +++ b/src/Menu_State.cc @@ -0,0 +1,73 @@ +#include "Menu_State.h" + +#include +#include + +using namespace sf; + +/* + * Create the welcome message using the font given in + * 'resources/fonts/font.ttf'. + * + * The first argument of the sf::Text constructor is what string + * it should draw, the second argument is what font should be used + * to draw the text and the final argument specifies the font-size + * (in pixels) of the text. + */ +Menu_State :: Menu_State () + :play { false } +{ + std::string file = "resources/fonts/font.ttf"; + if ( !font.loadFromFile (file) ) + throw std::invalid_argument ("Unable to load font"); + text = sf::Text{ "Press to start", font, 16 }; +} + +/* + * check for the 'enter' key. + * + * If it has been pressed we want to change to a new state. + */ +void Menu_State :: handle_event (Event event) +{ + if ( event.type == Event::KeyPressed ) + { + if ( event.key.code == Keyboard::Key::Return ) + play = true; + } +} + + +void Menu_State :: update () +{ + +} + +void Menu_State :: render (RenderTarget & target) +{ + auto bounds { text.getGlobalBounds () }; + auto size { target.getSize () }; + + text.setPosition ((size.x - bounds.width) / 2, + (size.y - bounds.height) / 2); + + target.draw (text); +} + +/* + * If return has been pressed we change to the GAME_STATE, otherwise + * we stay in MENU_STATE + */ +int Menu_State :: get_next_state() +{ + if (play) + { + play = false; + return GAME_STATE; + } + else + { + return MENU_STATE; + } +} + diff --git a/src/Menu_State.cpp b/src/Menu_State.cpp deleted file mode 100644 index fefb66f..0000000 --- a/src/Menu_State.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "Menu_State.h" - -#include -#include - -using namespace sf; - -/* - * Create the welcome message using the font given in - * 'resources/fonts/font.ttf'. - * - * The first argument of the sf::Text constructor is what string - * it should draw, the second argument is what font should be used - * to draw the text and the final argument specifies the font-size - * (in pixels) of the text. - */ -Menu_State :: Menu_State () - :play { false } -{ - std::string file = "resources/fonts/font.ttf"; - if ( !font.loadFromFile (file) ) - throw std::invalid_argument ("Unable to load font"); - text = sf::Text{ "Press to start", font, 16 }; -} - -/* - * check for the 'enter' key. - * - * If it has been pressed we want to change to a new state. - */ -void Menu_State :: handle_event (Event event) -{ - if ( event.type == Event::KeyPressed ) - { - if ( event.key.code == Keyboard::Key::Return ) - play = true; - } -} - - -void Menu_State :: update () -{ - -} - -void Menu_State :: render (RenderTarget & target) -{ - auto bounds { text.getGlobalBounds () }; - auto size { target.getSize () }; - - text.setPosition ((size.x - bounds.width) / 2, - (size.y - bounds.height) / 2); - - target.draw (text); -} - -/* - * If return has been pressed we change to the GAME_STATE, otherwise - * we stay in MENU_STATE - */ -int Menu_State :: get_next_state() -{ - if (play) - { - play = false; - return GAME_STATE; - } - else - { - return MENU_STATE; - } -} -