+++ /dev/null
-#include "Ball.h"
-
-#include <random>
-#include <iostream>
-
-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<unsigned> index { 0, 1 };
-
- // randomly choose a radius
- uniform_real_distribution<float> radius { 10, 50 };
-
- // randomly choose a velocity direction
- uniform_real_distribution<float> angle { 0, 2*3.14 };
-
- // randomly choose a speed for our ball
- uniform_real_distribution<float> 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 ();
-}
+++ /dev/null
-#pragma once
-
-#include <SFML/Graphics.hpp>
-
-/*
- * Class representing a moving ball on the screen.
- *
- * This class serves as a demonstration of a moving
- * object.
- */
-class Ball
-{
-
-public:
-
- /*
- * Create a ball and place it at the supplied coordinates.
- */
- Ball( float x, float y);
- /*
- * This function contains all logic that needs
- * to be run for the ball each iteration in the
- * game loop.
- */
- void update ();
-
- /*
- * draw the ball to 'target'
- */
- void render (sf::RenderTarget & target);
-
- /*
- * Get a rectangle which covers the ball
- * completly (a hitbox)
- */
- sf::FloatRect bounds () const;
-
-private:
-
- // graphical representation of the ball
- sf::CircleShape sprite;
-
-
- // the velocity (speed + direction) in
- // which this ball is moving
- sf::Vector2f velocity;
-
-};
-#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<int,
- std::unique_ptr<State>>({MENU_STATE,
- std::make_unique<Menu_State>()}));
-
- states.insert(std::pair<int,
- std::unique_ptr<State>>({GAME_STATE,
- std::make_unique<Game_State>()}));
-}
-
-
-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 ();
-}
-#ifndef GAME_H
-#define GAME_H
-
-#include <SFML/Graphics.hpp>
-
-#include <string>
-#include <memory>
-
-#include "State.h"
-
-class Game {
-
-public:
-
- /*
- * Constructor of Game.
- *
- * Will perform these tasks:
- * - initialize the current_state with an initial state (welcome
- * screen),
- * - Fill the states map with the States needed during the game
- * - spawn a window with the specified titlebar, width and height
- * (in pixels)
- */
- Game (std::string const & title, unsigned width, unsigned height);
-
- /*
- * Run the main loop of the state machine, and therefore by extension
- * the entire game.
- *
- * Each iteration of the main loop go through these steps:
- * - Handle all window events from the event queue (these include
- * key presses/releases, mouse movement, resizing of the window
- * and more).
- *
- * What this means is that we check if the user has requested
- * to close the window and therefore the game, and for any other
- * events we pass them on to the state.
- * - Update the current state.
- *
- * This means running any logic that the state might have, for
- * example; move objects, check for collision, manage resources
- * etc. This step will probably perform the vast majority of the
- * logic in the project (depending on the project of course).
- *
- * - Draw the currently active state to the window.
- *
- * This step will simply pass the window to the state and will
- * thus allow the state to draw itself onto the windows. This
- * is beneficial since we now have completely decoupled the
- * state from the Game class, thus allowing all behaviour of a
- * state to be implemented in the corresponding class without
- * having to touch any other code.
- */
- void start ();
-
-private:
- /*
- * SFML representation of window which we can draw on.
- */
- sf::RenderWindow window;
-
-
- /*
- * Container for all states in the game.
- * Current_state keeps track on the current state
- */
- std::map<int, std::unique_ptr<State>> states;
- int current_state;
-
-
- /*
- * If true, then the game should keep on run, otherwise the next
- * iteration of the game loop will not run.
- */
- bool running;
-
-
- void handle_events();
-
- /*
- * This function puts the program to sleep for a certain
- * period of time in order to keep a steady framerate.
- * The inargument is how many milliseconds the current iteration
- * took to complete.
- *
- * Say that the game is supposed to run at 60 frames per second (fps).
- * This means that each frame should take exactly 1000/60 = 16.666...
- * milliseconds to run. If it took longer, then there is not much
- * we can do about it, but if it took a shorter amount of time,
- * then request that our program sleep for the remaining time.
- */
- void delay (sf::Clock & clock) const;
-};
-
-#endif // GAME_H
+++ /dev/null
-#include "Game_State.h"
-#include "constants.h"
-
-#include <iostream>
-
-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;
- }
-}
+++ /dev/null
-#include "State.h"
-#include "Ball.h"
-
-#include <vector>
-
-/*
- * This class represents the "game".
- *
- * It is a simple program where a ball appears wherever
- * the user has clicked with the mouse. This ball
- * is assigned a random velocity and will begin moving
- * according to that velocity.
- *
- * This class serves as an example of a state, but also
- * of how the various functions of the State class can
- * be used to get moving objects in the game.
- *
- * It also demonstrates how to remove objects from
- * the game in a safe way, as to avoid memory leaks.
- */
-class Game_State : public State
-{
-public:
-
- Game_State () = default;
-
- void handle_event (sf::Event event) override;
- void update () override;
- void render (sf::RenderTarget & target) override;
- virtual int get_next_state() override;
-
-
-private:
- bool end_game{false};
-
- /*
- * Remove all balls which are no longer visible on the
- * screen.
- */
- void cleanup ();
-
-
- /*
- * A collection which contains all balls that are currently
- * visible on the screen (see Ball.h).
- */
- std::vector<Ball> balls { };
-
-};
\ No newline at end of file
+++ /dev/null
-#include "Menu_State.h"
-
-#include <string>
-#include <stdexcept>
-
-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 <Enter> 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;
- }
-}
-
+++ /dev/null
-#pragma once
-
-#include <SFML/Graphics.hpp>
-#include "State.h"
-
-class Menu_State: public State
-{
-public:
- Menu_State ();
-
- virtual void handle_event (sf::Event event) override;
- virtual void update () override;
- virtual void render (sf::RenderTarget & taget) override;
- virtual int get_next_state() override;
-
-
-private:
-
- /*
- * sf::Text is a drawable object representing a string.
- *
- * sf::Text can be drawn to any RenderTarget as long as
- * a font and a string is supplied.
- *
- * It works like a sf::Sprite but represents text instead
- * of an image.
- */
- sf::Text text;
- sf::Font font{};
-
-
- /*
- * Flag to determine wheter or not the 'enter' key has been
- * pressed (see Menu_State.cpp for details).
- */
- bool play;
-
-
-
-};
-#ifndef STATE_H
-#define STATE_H
-#include <SFML/Graphics.hpp>
-#include <string>
-
-int const MENU_STATE{0};
-int const GAME_STATE{1};
-
-
-class State
-{
-public:
-
- // this is a base class so a virtual destructor is needed
- virtual ~State () = default;
-
- /*
- * This function is called for each event which occurs
- * in the window. It is the responsibility of the state
- * to filter out any events it is interested in, since
- * no filtering is performed before calling this function.
- *
- * What this means is that if a state only wants to know
- * if a certain event has happened, the state needs to
- * check for that event inside this function.
- */
- virtual void handle_event (sf::Event event) = 0;
-
- /*
- * The 'update' function is called every iteration of the
- * game loop, no more and no less.
- */
- virtual void update () = 0;
-
- /*
- * Render the state onto the screen through a 'RenderTarget'.
- *
- * A RenderTarget is an abstract class which defines an
- * interface for rendering to a canvas. Two types
- * of RenderTargets exist in SFML:
- * - RenderWindow; draw directly to a window.
- * - RenderTexture; draw onto a texture which can be extracted
- * later on using the 'getTexture' member function.
- */
- virtual void render(sf::RenderTarget & target) = 0;
-
- /*
- * Return the new state if it should change,
- * otherwise return the current state
- */
- virtual int get_next_state() = 0;
-
-};
-
-#endif //STATE_H
+++ /dev/null
-#pragma once
-
-/*
- * In this file we define some global constants.
- *
- * Please note that the problem with global variables
- * is that anyone can change them whenever which makes
- * it extremely hard to reason about your code. But for
- * constants, this is not a problem since we cannot
- * change them, and therefore they stay the same during
- * the entire execution of the program.
- */
-
-/*
- * define the size of the window.
- */
-int const screen_width { 640 };
-int const screen_height { 480 };
-
-/*
- * define how many fps we want our program to run in.
- */
-double const fps { 60.0 };