refac: moved draw & input code out of game class

This commit is contained in:
Florian Sylvain
2025-05-11 01:01:18 +02:00
parent 874b54cf36
commit 885ed1ec4b
9 changed files with 217 additions and 161 deletions
+1 -1
View File
@@ -17,4 +17,4 @@ file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS src/*.hpp)
add_executable(SFMLplayground ${SOURCES} ${HEADERS}) add_executable(SFMLplayground ${SOURCES} ${HEADERS})
target_compile_features(SFMLplayground PRIVATE cxx_std_17) target_compile_features(SFMLplayground PRIVATE cxx_std_17)
target_link_libraries(SFMLplayground PRIVATE SFML::Graphics) target_link_libraries(SFMLplayground PRIVATE SFML::Graphics)
+6 -98
View File
@@ -1,101 +1,9 @@
# CMake SFML Project Template # SFML Playground
This repository template should allow for a fast and hassle-free kick start of your next SFML project using CMake. Simple CMake project w/ SFML 3 to play around with.
Thanks to [GitHub's nature of templates](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template), you can fork this repository without inheriting its Git history.
The template starts out very basic, but might receive additional features over time: ## TODO
- Basic CMake script to build your project and link SFML on any operating system - [ ] Add a toggle for debug stuff
- Basic [GitHub Actions](https://github.com/features/actions) script for all major platforms - [ ] Add debug text (for ball velocity, position, rest status, etc.)
- [ ] Refactor game class to split input handling and debug stuff into their own classes
## How to Use
1. Install [Git](https://git-scm.com/downloads) and [CMake](https://cmake.org/download/). Use your system's package manager if available.
2. Follow [GitHub's instructions](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template) for how to use their project template feature to create your own project. If you don't want to use GitHub, see the section below.
3. Clone your new GitHub repo and open the repo in your text editor of choice.
4. Open [CMakeLists.txt](CMakeLists.txt). Rename the project and the target name of the executable to whatever name you want. Make sure to change all occurrences.
5. If you want to add or remove any .cpp files, change the source files listed in the `add_executable` call in CMakeLists.txt to match the source files your project requires. If you plan on keeping the default main.cpp file then no changes are required.
6. If your code uses the Audio or Network modules then add `SFML::Audio` or `SFML::Network` to the `target_link_libraries` call alongside the existing `SFML::Graphics` library that is being linked.
7. If you use Linux, install SFML's dependencies using your system package manager. On Ubuntu and other Debian-based distributions you can use the following commands:
```
sudo apt update
sudo apt install \
libxrandr-dev \
libxcursor-dev \
libxi-dev \
libudev-dev \
libfreetype-dev \
libflac-dev \
libvorbis-dev \
libgl1-mesa-dev \
libegl1-mesa-dev \
libfreetype-dev
```
8. Configure and build your project. Most popular IDEs support CMake projects with very little effort on your part.
- [VS Code](https://code.visualstudio.com) via the [CMake extension](https://code.visualstudio.com/docs/cpp/cmake-linux)
- [Visual Studio](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170)
- [CLion](https://www.jetbrains.com/clion/features/cmake-support.html)
- [Qt Creator](https://doc.qt.io/qtcreator/creator-project-cmake.html)
Using CMake from the command line is straightforward as well.
Be sure to run these commands in the root directory of the project you just created.
```
cmake -B build
cmake --build build
```
9. Enjoy!
## Upgrading SFML
SFML is found via CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) module.
FetchContent automatically downloads SFML from GitHub and builds it alongside your own code.
Beyond the convenience of not having to install SFML yourself, this ensures ABI compatibility and simplifies things like specifying static versus shared libraries.
Modifying what version of SFML you want is as easy as changing the `GIT_TAG` argument.
Currently it uses SFML 3 via the `3.0.0` tag.
## But I want to...
Modify CMake options by adding them as configuration parameters (with a `-D` flag) or by modifying the contents of CMakeCache.txt and rebuilding.
### Not use GitHub
You can use this project without a GitHub account by [downloading the contents](https://github.com/SFML/cmake-sfml-project/archive/refs/heads/master.zip) of the repository as a ZIP archive and unpacking it locally.
This approach also avoids using Git entirely if you would prefer to not do that.
### Change Compilers
See the variety of [`CMAKE_<LANG>_COMPILER`](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html) options.
In particular you'll want to modify `CMAKE_CXX_COMPILER` to point to the C++ compiler you wish to use.
### Change Compiler Optimizations
CMake abstracts away specific optimizer flags through the [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) option.
By default this project recommends `Release` builds which enable optimizations.
Other build types include `Debug` builds which enable debug symbols but disable optimizations.
If you're using a multi-configuration generator (as is often the case on Windows), you can modify the [`CMAKE_CONFIGURATION_TYPES`](https://cmake.org/cmake/help/latest/variable/CMAKE_CONFIGURATION_TYPES.html#variable:CMAKE_CONFIGURATION_TYPES) option.
### Change Generators
While CMake will attempt to pick a suitable default generator, some systems offer a number of generators to choose from.
Ubuntu, for example, offers Makefiles and Ninja as two potential options.
For a list of generators, click [here](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
To modify the generator you're using you must reconfigure your project providing a `-G` flag with a value corresponding to the generator you want.
You can't simply modify an entry in the CMakeCache.txt file unlike the above options.
Then you may rebuild your project with this new generator.
## More Reading
Here are some useful resources if you want to learn more about CMake:
- [Official CMake Tutorial](https://cmake.org/cmake/help/latest/guide/tutorial/)
- [How to Use CMake Without the Agonizing Pain - Part 1](https://alexreinking.com/blog/how-to-use-cmake-without-the-agonizing-pain-part-1.html)
- [How to Use CMake Without the Agonizing Pain - Part 2](https://alexreinking.com/blog/how-to-use-cmake-without-the-agonizing-pain-part-2.html)
- [Better CMake YouTube series by Jefferon Amstutz](https://www.youtube.com/playlist?list=PL8i3OhJb4FNV10aIZ8oF0AA46HgA2ed8g)
## License
The source code is dual licensed under Public Domain and MIT -- choose whichever you prefer.
+101
View File
@@ -0,0 +1,101 @@
# CMake SFML Project Template
This repository template should allow for a fast and hassle-free kick start of your next SFML project using CMake.
Thanks to [GitHub's nature of templates](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template), you can fork this repository without inheriting its Git history.
The template starts out very basic, but might receive additional features over time:
- Basic CMake script to build your project and link SFML on any operating system
- Basic [GitHub Actions](https://github.com/features/actions) script for all major platforms
## How to Use
1. Install [Git](https://git-scm.com/downloads) and [CMake](https://cmake.org/download/). Use your system's package manager if available.
2. Follow [GitHub's instructions](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template) for how to use their project template feature to create your own project. If you don't want to use GitHub, see the section below.
3. Clone your new GitHub repo and open the repo in your text editor of choice.
4. Open [CMakeLists.txt](CMakeLists.txt). Rename the project and the target name of the executable to whatever name you want. Make sure to change all occurrences.
5. If you want to add or remove any .cpp files, change the source files listed in the `add_executable` call in CMakeLists.txt to match the source files your project requires. If you plan on keeping the default main.cpp file then no changes are required.
6. If your code uses the Audio or Network modules then add `SFML::Audio` or `SFML::Network` to the `target_link_libraries` call alongside the existing `SFML::Graphics` library that is being linked.
7. If you use Linux, install SFML's dependencies using your system package manager. On Ubuntu and other Debian-based distributions you can use the following commands:
```
sudo apt update
sudo apt install \
libxrandr-dev \
libxcursor-dev \
libxi-dev \
libudev-dev \
libfreetype-dev \
libflac-dev \
libvorbis-dev \
libgl1-mesa-dev \
libegl1-mesa-dev \
libfreetype-dev
```
8. Configure and build your project. Most popular IDEs support CMake projects with very little effort on your part.
- [VS Code](https://code.visualstudio.com) via the [CMake extension](https://code.visualstudio.com/docs/cpp/cmake-linux)
- [Visual Studio](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170)
- [CLion](https://www.jetbrains.com/clion/features/cmake-support.html)
- [Qt Creator](https://doc.qt.io/qtcreator/creator-project-cmake.html)
Using CMake from the command line is straightforward as well.
Be sure to run these commands in the root directory of the project you just created.
```
cmake -B build
cmake --build build
```
9. Enjoy!
## Upgrading SFML
SFML is found via CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) module.
FetchContent automatically downloads SFML from GitHub and builds it alongside your own code.
Beyond the convenience of not having to install SFML yourself, this ensures ABI compatibility and simplifies things like specifying static versus shared libraries.
Modifying what version of SFML you want is as easy as changing the `GIT_TAG` argument.
Currently it uses SFML 3 via the `3.0.0` tag.
## But I want to...
Modify CMake options by adding them as configuration parameters (with a `-D` flag) or by modifying the contents of CMakeCache.txt and rebuilding.
### Not use GitHub
You can use this project without a GitHub account by [downloading the contents](https://github.com/SFML/cmake-sfml-project/archive/refs/heads/master.zip) of the repository as a ZIP archive and unpacking it locally.
This approach also avoids using Git entirely if you would prefer to not do that.
### Change Compilers
See the variety of [`CMAKE_<LANG>_COMPILER`](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html) options.
In particular you'll want to modify `CMAKE_CXX_COMPILER` to point to the C++ compiler you wish to use.
### Change Compiler Optimizations
CMake abstracts away specific optimizer flags through the [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) option.
By default this project recommends `Release` builds which enable optimizations.
Other build types include `Debug` builds which enable debug symbols but disable optimizations.
If you're using a multi-configuration generator (as is often the case on Windows), you can modify the [`CMAKE_CONFIGURATION_TYPES`](https://cmake.org/cmake/help/latest/variable/CMAKE_CONFIGURATION_TYPES.html#variable:CMAKE_CONFIGURATION_TYPES) option.
### Change Generators
While CMake will attempt to pick a suitable default generator, some systems offer a number of generators to choose from.
Ubuntu, for example, offers Makefiles and Ninja as two potential options.
For a list of generators, click [here](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
To modify the generator you're using you must reconfigure your project providing a `-G` flag with a value corresponding to the generator you want.
You can't simply modify an entry in the CMakeCache.txt file unlike the above options.
Then you may rebuild your project with this new generator.
## More Reading
Here are some useful resources if you want to learn more about CMake:
- [Official CMake Tutorial](https://cmake.org/cmake/help/latest/guide/tutorial/)
- [How to Use CMake Without the Agonizing Pain - Part 1](https://alexreinking.com/blog/how-to-use-cmake-without-the-agonizing-pain-part-1.html)
- [How to Use CMake Without the Agonizing Pain - Part 2](https://alexreinking.com/blog/how-to-use-cmake-without-the-agonizing-pain-part-2.html)
- [Better CMake YouTube series by Jefferon Amstutz](https://www.youtube.com/playlist?list=PL8i3OhJb4FNV10aIZ8oF0AA46HgA2ed8g)
## License
The source code is dual licensed under Public Domain and MIT -- choose whichever you prefer.
+41
View File
@@ -0,0 +1,41 @@
#include "DebugDraw.hpp"
#include <SFML/Window/Mouse.hpp>
#include <algorithm>
#include <cmath>
#include "VectorMath.hpp"
float clamp(float value, float minVal, float maxVal) {
return std::max(minVal, std::min(value, maxVal));
}
void DebugDraw::drawLine(sf::RenderWindow& window, const sf::Vector2f& start,
const sf::Vector2f& direction, float length,
const sf::Color& color) {
sf::Vector2f endPoint = start + VectorMath::normalize(direction) * length;
sf::Vertex line[] = {{start, color}, {endPoint, color}};
window.draw(line, 2, sf::PrimitiveType::Lines);
}
void DebugDraw::drawDirectionLine(sf::RenderWindow& window, const Ball* ball) {
sf::Vector2f ballCenter = ball->getPosition();
sf::Vector2i mousePixel = sf::Mouse::getPosition(window);
sf::Vector2f mouseWorld(static_cast<float>(mousePixel.x),
static_cast<float>(mousePixel.y));
sf::Vector2f dir = mouseWorld - ballCenter;
drawLine(window, ballCenter, dir, 100.f, sf::Color::Green);
}
void DebugDraw::drawVelocityLine(sf::RenderWindow& window, const Ball* ball) {
if (ball->isAtRest()) return;
sf::Vector2f ballCenter = ball->getPosition();
sf::Vector2f velocity = ball->getVelocity();
float velLength = VectorMath::length(velocity);
float clampedLength = std::max(0.f, std::min(velLength, 100.f));
drawLine(window, ballCenter, velocity, clampedLength, sf::Color::Blue);
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include "Ball.hpp"
class DebugDraw {
public:
static void drawLine(sf::RenderWindow& window, const sf::Vector2f& start,
const sf::Vector2f& direction, float length,
const sf::Color& color);
static void drawDirectionLine(sf::RenderWindow& window, const Ball* ball);
static void drawVelocityLine(sf::RenderWindow& window, const Ball* ball);
};
+15 -55
View File
@@ -4,9 +4,13 @@
#include <cmath> #include <cmath>
#include "Constants.hpp" #include "Constants.hpp"
#include "DebugDraw.hpp"
#include "VectorMath.hpp" #include "VectorMath.hpp"
Game::Game() { Game::Game()
: m_inputManager(
std::bind(&Game::processKeyPressed, this, std::placeholders::_1),
std::bind(&Game::processMousePressed, this, std::placeholders::_1)) {
m_window.create(sf::VideoMode({Constants::WIDTH, Constants::HEIGHT}), m_window.create(sf::VideoMode({Constants::WIDTH, Constants::HEIGHT}),
"SFML Playground"); "SFML Playground");
m_window.setVerticalSyncEnabled(true); m_window.setVerticalSyncEnabled(true);
@@ -18,7 +22,7 @@ Game::Game() {
sf::Vector2f(400.f, 0.f))); sf::Vector2f(400.f, 0.f)));
} }
void Game::processKeyPressed(const sf::Event::KeyPressed& kP) { void Game::processKeyPressed(const sf::Event::KeyPressed &kP) {
if (kP.code == sf::Keyboard::Key::Equal) { if (kP.code == sf::Keyboard::Key::Equal) {
m_timeScale *= 1.1f; m_timeScale *= 1.1f;
} else if (kP.code == sf::Keyboard::Key::Hyphen) { } else if (kP.code == sf::Keyboard::Key::Hyphen) {
@@ -27,26 +31,13 @@ void Game::processKeyPressed(const sf::Event::KeyPressed& kP) {
} }
} }
void Game::processMousePressed(const sf::Event::MouseButtonPressed& mP) { void Game::processMousePressed(const sf::Event::MouseButtonPressed &mP) {
if (mP.button != sf::Mouse::Button::Left) return; if (mP.button != sf::Mouse::Button::Left) return;
handleMouseClick(sf::Vector2i(mP.position.x, mP.position.y)); handleMouseClick(sf::Vector2i(mP.position.x, mP.position.y));
} }
void Game::processEvents() { void Game::handleMouseClick(const sf::Vector2i &mousePos) {
while (const std::optional event = m_window.pollEvent()) { auto *ball = dynamic_cast<Ball *>(m_objects[0].get());
if (event->is<sf::Event::Closed>()) {
m_window.close();
return;
}
if (auto kP = event->getIf<sf::Event::KeyPressed>())
processKeyPressed(*kP);
if (auto mP = event->getIf<sf::Event::MouseButtonPressed>())
processMousePressed(*mP);
}
}
void Game::handleMouseClick(const sf::Vector2i& mousePos) {
auto* ball = dynamic_cast<Ball*>(m_objects[0].get());
if (!ball) return; if (!ball) return;
sf::Vector2f mouseWorld(static_cast<float>(mousePos.x), sf::Vector2f mouseWorld(static_cast<float>(mousePos.x),
@@ -58,48 +49,17 @@ void Game::handleMouseClick(const sf::Vector2i& mousePos) {
void Game::update() { void Game::update() {
float dt = m_clock.restart().asSeconds() * m_timeScale; float dt = m_clock.restart().asSeconds() * m_timeScale;
for (auto& object : m_objects) object->update(dt); for (auto &object : m_objects) object->update(dt);
}
void Game::drawLine(const sf::Vector2f& start, const sf::Vector2f& direction,
float length, const sf::Color& color) {
float dirLength =
std::sqrt(direction.x * direction.x + direction.y * direction.y);
sf::Vector2f dirNorm =
(dirLength != 0.f) ? direction / dirLength : sf::Vector2f(1.f, 0.f);
sf::Vector2f endPoint = start + dirNorm * length;
sf::Vertex line[] = {{start, color}, {endPoint, color}};
m_window.draw(line, 2, sf::PrimitiveType::Lines);
}
void Game::drawDirectionLine(const Ball* ball) {
sf::Vector2f ballCenter = ball->getPosition();
sf::Vector2i mousePixel = sf::Mouse::getPosition(m_window);
sf::Vector2f mouseWorld(static_cast<float>(mousePixel.x),
static_cast<float>(mousePixel.y));
sf::Vector2f dir = mouseWorld - ballCenter;
drawLine(ballCenter, dir, 100.f, sf::Color::Green);
}
void Game::drawVelocityLine(const Ball* ball) {
if (ball->isAtRest()) return;
sf::Vector2f ballCenter = ball->getPosition();
sf::Vector2f velocity = ball->getVelocity();
float velLength =
std::sqrt(velocity.x * velocity.x + velocity.y * velocity.y);
float clampedLength = std::min(100.f, std::max(0.f, velLength));
drawLine(ballCenter, velocity, clampedLength, sf::Color::Blue);
} }
void Game::render() { void Game::render() {
m_window.clear(sf::Color::Black); m_window.clear(sf::Color::Black);
for (auto& object : m_objects) object->draw(m_window); for (auto &object : m_objects) object->draw(m_window);
auto* ball = dynamic_cast<Ball*>(m_objects[0].get()); auto *ball = dynamic_cast<Ball *>(m_objects[0].get());
if (ball) { if (ball) {
drawDirectionLine(ball); DebugDraw::drawDirectionLine(m_window, ball);
drawVelocityLine(ball); DebugDraw::drawVelocityLine(m_window, ball);
} }
m_window.display(); m_window.display();
@@ -107,7 +67,7 @@ void Game::render() {
void Game::run() { void Game::run() {
while (m_window.isOpen()) { while (m_window.isOpen()) {
processEvents(); m_inputManager.processEvents(m_window);
update(); update();
render(); render();
} }
+2 -7
View File
@@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include "Ball.hpp" #include "Ball.hpp"
#include "InputManager.hpp"
#include "PhysicalObject.hpp" #include "PhysicalObject.hpp"
class Ball; class Ball;
@@ -18,19 +19,13 @@ class Game {
sf::RenderWindow m_window; sf::RenderWindow m_window;
std::vector<std::unique_ptr<PhysicalObject>> m_objects; std::vector<std::unique_ptr<PhysicalObject>> m_objects;
sf::Clock m_clock; sf::Clock m_clock;
InputManager m_inputManager;
void processEvents();
void processKeyPressed(const sf::Event::KeyPressed& keyPressed); void processKeyPressed(const sf::Event::KeyPressed& keyPressed);
void processMousePressed(const sf::Event::MouseButtonPressed& mousePressed); void processMousePressed(const sf::Event::MouseButtonPressed& mousePressed);
void handleMouseClick(const sf::Vector2i& mousePos); void handleMouseClick(const sf::Vector2i& mousePos);
void update(); void update();
void render(); void render();
void drawLine(const sf::Vector2f& start, const sf::Vector2f& direction,
float length, const sf::Color& color);
void drawDirectionLine(const Ball* ball);
void drawVelocityLine(const Ball* ball);
public: public:
Game(); Game();
void run(); void run();
+18
View File
@@ -0,0 +1,18 @@
#include "InputManager.hpp"
#include <optional>
InputManager::InputManager(KeyCallback keyCb, MouseCallback mouseCb)
: m_keyCallback(std::move(keyCb)), m_mouseCallback(std::move(mouseCb)) {}
void InputManager::processEvents(sf::Window& window) {
while (const std::optional event = window.pollEvent()) {
if (event->is<sf::Event::Closed>()) {
window.close();
return;
}
if (auto kP = event->getIf<sf::Event::KeyPressed>()) m_keyCallback(*kP);
if (auto mP = event->getIf<sf::Event::MouseButtonPressed>())
m_mouseCallback(*mP);
}
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <SFML/Window/Event.hpp>
#include <SFML/Window/Window.hpp>
#include <functional>
class InputManager {
public:
using KeyCallback = std::function<void(const sf::Event::KeyPressed&)>;
using MouseCallback =
std::function<void(const sf::Event::MouseButtonPressed&)>;
InputManager(KeyCallback keyCb, MouseCallback mouseCb);
void processEvents(sf::Window& window);
private:
KeyCallback m_keyCallback;
MouseCallback m_mouseCallback;
};