mirror of
https://github.com/Floriansylvain/SFMLplayground.git
synced 2026-08-19 11:43:24 +02:00
feat: basic game structure w/ Ball physics & input handling
This commit is contained in:
+7
-4
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.28)
|
||||
project(CMakeSFMLProject LANGUAGES CXX)
|
||||
project(SFMLplayground LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
@@ -12,6 +12,9 @@ FetchContent_Declare(SFML
|
||||
SYSTEM)
|
||||
FetchContent_MakeAvailable(SFML)
|
||||
|
||||
add_executable(main src/main.cpp)
|
||||
target_compile_features(main PRIVATE cxx_std_17)
|
||||
target_link_libraries(main PRIVATE SFML::Graphics)
|
||||
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS src/*.cpp)
|
||||
file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS src/*.hpp)
|
||||
|
||||
add_executable(SFMLplayground ${SOURCES} ${HEADERS})
|
||||
target_compile_features(SFMLplayground PRIVATE cxx_std_17)
|
||||
target_link_libraries(SFMLplayground PRIVATE SFML::Graphics)
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "Ball.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
Ball::Ball(float radius, const sf::Vector2f& pos, const sf::Vector2f& vel)
|
||||
: m_radius(radius), m_velocity(vel) {
|
||||
m_shape.setRadius(radius);
|
||||
m_shape.setOrigin(sf::Vector2f(radius, radius));
|
||||
m_shape.setPosition(pos);
|
||||
m_shape.setFillColor(sf::Color::Red);
|
||||
}
|
||||
|
||||
void Ball::update(float dt) {
|
||||
m_velocity.y += Constants::GRAVITY * dt;
|
||||
m_shape.move(m_velocity * dt);
|
||||
handleWallCollision();
|
||||
}
|
||||
|
||||
void Ball::draw(sf::RenderWindow& window) { window.draw(m_shape); }
|
||||
|
||||
void Ball::applyImpulse(const sf::Vector2f& impulse) { m_velocity += impulse; }
|
||||
|
||||
sf::Vector2f Ball::getPosition() const { return m_shape.getPosition(); }
|
||||
|
||||
void Ball::handleWallCollision() {
|
||||
sf::Vector2f pos = m_shape.getPosition();
|
||||
|
||||
auto handleAxis = [&](int axis, float min, float max, float& velocity,
|
||||
float radius, float restitution) {
|
||||
float value = (axis == 0) ? pos.x : pos.y;
|
||||
|
||||
if (value - radius < min) {
|
||||
value = min + radius;
|
||||
velocity = -velocity * restitution;
|
||||
} else if (value + radius > max) {
|
||||
value = max - radius;
|
||||
velocity = -velocity * restitution;
|
||||
if (axis == 1 && std::abs(velocity) < 10.f) velocity = 0.f;
|
||||
}
|
||||
|
||||
(axis == 0 ? pos.x : pos.y) = value;
|
||||
};
|
||||
|
||||
handleAxis(0, 0.f, Constants::WIDTH, m_velocity.x, m_radius,
|
||||
Constants::RESTITUTION);
|
||||
handleAxis(1, 0.f, Constants::HEIGHT, m_velocity.y, m_radius,
|
||||
Constants::RESTITUTION);
|
||||
|
||||
if (pos.y + m_radius >= Constants::HEIGHT - 1.0f) {
|
||||
m_velocity.x *= Constants::FRICTION;
|
||||
if (std::abs(m_velocity.x) < 5.f) m_velocity.x = 0.f;
|
||||
}
|
||||
|
||||
m_shape.setPosition(pos);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <SFML/Graphics/CircleShape.hpp>
|
||||
|
||||
#include "PhysicalObject.hpp"
|
||||
|
||||
class Ball : public PhysicalObject {
|
||||
private:
|
||||
sf::CircleShape m_shape;
|
||||
sf::Vector2f m_velocity;
|
||||
float m_radius;
|
||||
|
||||
void handleWallCollision();
|
||||
|
||||
public:
|
||||
Ball(float radius, const sf::Vector2f& pos, const sf::Vector2f& vel);
|
||||
|
||||
void update(float dt) override;
|
||||
void draw(sf::RenderWindow& window) override;
|
||||
void applyImpulse(const sf::Vector2f& impulse) override;
|
||||
|
||||
sf::Vector2f getPosition() const;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Constants {
|
||||
constexpr unsigned WIDTH = 1280;
|
||||
constexpr unsigned HEIGHT = 720;
|
||||
constexpr float GRAVITY = 8000.f;
|
||||
constexpr float RESTITUTION = 0.8f;
|
||||
constexpr float FRICTION = 0.9f;
|
||||
constexpr float BALL_RADIUS = 20.f;
|
||||
} // namespace Constants
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "Game.hpp"
|
||||
|
||||
#include <SFML/Window/Event.hpp>
|
||||
|
||||
#include "Ball.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include "VectorMath.hpp"
|
||||
|
||||
Game::Game() {
|
||||
m_window.create(sf::VideoMode({Constants::WIDTH, Constants::HEIGHT}),
|
||||
"SFML Playground");
|
||||
m_window.setVerticalSyncEnabled(true);
|
||||
m_timeScale = 1.0f;
|
||||
m_objects.clear();
|
||||
m_objects.push_back(std::make_unique<Ball>(
|
||||
Constants::BALL_RADIUS,
|
||||
sf::Vector2f(Constants::WIDTH / 2.f, Constants::HEIGHT / 4.f),
|
||||
sf::Vector2f(400.f, 0.f)));
|
||||
}
|
||||
|
||||
void Game::processKeyPressed(const sf::Event::KeyPressed& keyPressed) {
|
||||
if (keyPressed.code == sf::Keyboard::Key::Add ||
|
||||
keyPressed.code == sf::Keyboard::Key::Equal) {
|
||||
m_timeScale *= 1.1f;
|
||||
} else if (keyPressed.code == sf::Keyboard::Key::Subtract ||
|
||||
keyPressed.code == sf::Keyboard::Key::Hyphen) {
|
||||
m_timeScale /= 1.1f;
|
||||
if (m_timeScale < 0.1f) m_timeScale = 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::processMousePressed(
|
||||
const sf::Event::MouseButtonPressed& mousePressed) {
|
||||
if (mousePressed.button == sf::Mouse::Button::Left) {
|
||||
handleMouseClick(
|
||||
sf::Vector2i(mousePressed.position.x, mousePressed.position.y));
|
||||
}
|
||||
}
|
||||
|
||||
void Game::processEvents() {
|
||||
while (const std::optional event = m_window.pollEvent()) {
|
||||
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;
|
||||
sf::Vector2f ballPos = ball->getPosition();
|
||||
sf::Vector2f mouseWorld(static_cast<float>(mousePos.x),
|
||||
static_cast<float>(mousePos.y));
|
||||
sf::Vector2f dir = mouseWorld - ballPos;
|
||||
|
||||
dir = VectorMath::normalize(dir);
|
||||
|
||||
ball->applyImpulse(2000.f * dir);
|
||||
}
|
||||
|
||||
void Game::update() {
|
||||
float dt = m_clock.restart().asSeconds() * m_timeScale;
|
||||
for (auto& object : m_objects) {
|
||||
object->update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::render() {
|
||||
m_window.clear(sf::Color::Black);
|
||||
for (auto& object : m_objects) {
|
||||
object->draw(m_window);
|
||||
}
|
||||
m_window.display();
|
||||
}
|
||||
|
||||
void Game::run() {
|
||||
while (m_window.isOpen()) {
|
||||
processEvents();
|
||||
update();
|
||||
render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <SFML/Graphics/RenderWindow.hpp>
|
||||
#include <SFML/System/Clock.hpp>
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
#include <SFML/Window/Event.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "PhysicalObject.hpp"
|
||||
|
||||
class PhysicalObject;
|
||||
|
||||
class Game {
|
||||
private:
|
||||
float m_timeScale;
|
||||
sf::RenderWindow m_window;
|
||||
std::vector<std::unique_ptr<PhysicalObject>> m_objects;
|
||||
sf::Clock m_clock;
|
||||
|
||||
void processEvents();
|
||||
void processKeyPressed(const sf::Event::KeyPressed& keyPressed);
|
||||
void processMousePressed(const sf::Event::MouseButtonPressed& mousePressed);
|
||||
void handleMouseClick(const sf::Vector2i& mousePos);
|
||||
void update();
|
||||
void render();
|
||||
|
||||
public:
|
||||
Game();
|
||||
void run();
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <SFML/Graphics/RenderWindow.hpp>
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
|
||||
class PhysicalObject {
|
||||
public:
|
||||
virtual ~PhysicalObject() = default;
|
||||
virtual void update(float dt) = 0;
|
||||
virtual void draw(sf::RenderWindow& window) = 0;
|
||||
virtual void applyImpulse(const sf::Vector2f& impulse) = 0;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "VectorMath.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace VectorMath {
|
||||
float length(const sf::Vector2f& vector) {
|
||||
return std::sqrt(vector.x * vector.x + vector.y * vector.y);
|
||||
}
|
||||
|
||||
sf::Vector2f normalize(const sf::Vector2f& vector) {
|
||||
float len = length(vector);
|
||||
if (len > 0.0001f) {
|
||||
return vector / len;
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
} // namespace VectorMath
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
|
||||
namespace VectorMath {
|
||||
float length(const sf::Vector2f& vector);
|
||||
sf::Vector2f normalize(const sf::Vector2f& vector);
|
||||
} // namespace VectorMath
|
||||
+5
-19
@@ -1,21 +1,7 @@
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include "Game.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
auto window = sf::RenderWindow(sf::VideoMode({1920u, 1080u}), "CMake SFML Project");
|
||||
window.setFramerateLimit(144);
|
||||
|
||||
while (window.isOpen())
|
||||
{
|
||||
while (const std::optional event = window.pollEvent())
|
||||
{
|
||||
if (event->is<sf::Event::Closed>())
|
||||
{
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
|
||||
window.clear();
|
||||
window.display();
|
||||
}
|
||||
int main() {
|
||||
Game game;
|
||||
game.run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user