mirror of
https://github.com/Floriansylvain/SFMLplayground.git
synced 2026-08-19 11:43:24 +02:00
151 lines
4.6 KiB
C++
151 lines
4.6 KiB
C++
#include "Game.hpp"
|
|
|
|
#include <SFML/Window/Event.hpp>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
|
|
#include "BallFactory.hpp"
|
|
#include "Constants.hpp"
|
|
#include "DebugDraw.hpp"
|
|
#include "VectorMath.hpp"
|
|
|
|
Game::Game()
|
|
: m_inputManager(
|
|
std::bind(&Game::processKeyPressed, this, std::placeholders::_1),
|
|
std::bind(&Game::processMousePressed, this, std::placeholders::_1)),
|
|
m_debugLines(sf::PrimitiveType::Lines),
|
|
m_drawCallCount(0),
|
|
m_debugOverlay("assets/consolas.ttf") {
|
|
m_window.create(sf::VideoMode({Constants::WIDTH, Constants::HEIGHT}),
|
|
"SFML Playground");
|
|
m_window.setVerticalSyncEnabled(true);
|
|
m_objects.clear();
|
|
|
|
auto balls = BallFactory::generateBalls();
|
|
for (auto &ball : balls) {
|
|
m_objects.push_back(std::move(ball));
|
|
}
|
|
}
|
|
|
|
void Game::processKeyPressed(const sf::Event::KeyPressed &kP) {
|
|
if (kP.code == sf::Keyboard::Key::Equal) {
|
|
m_timeScale += 0.25f;
|
|
if (m_timeScale > 10.0f) m_timeScale = 10.0f;
|
|
} else if (kP.code == sf::Keyboard::Key::Hyphen) {
|
|
m_timeScale -= 0.25f;
|
|
if (m_timeScale < 0.25f) m_timeScale = 0.25f;
|
|
}
|
|
|
|
if (kP.code == sf::Keyboard::Key::F3) {
|
|
m_toggleDebug = !m_toggleDebug;
|
|
}
|
|
}
|
|
|
|
void Game::processMousePressed(const sf::Event::MouseButtonPressed &mP) {
|
|
if (mP.button != sf::Mouse::Button::Left) return;
|
|
handleMouseClick(sf::Vector2i(mP.position.x, mP.position.y));
|
|
}
|
|
|
|
void Game::handleMouseClick(const sf::Vector2i &mousePos) {
|
|
sf::Vector2f mouseWorld(static_cast<float>(mousePos.x),
|
|
static_cast<float>(mousePos.y));
|
|
for (auto &object : m_objects) {
|
|
auto *ball = dynamic_cast<Ball *>(object.get());
|
|
if (!ball) continue;
|
|
|
|
sf::Vector2f dir = mouseWorld - ball->getPosition();
|
|
ball->applyImpulse(Constants::IMPULSE * VectorMath::normalize(dir));
|
|
}
|
|
}
|
|
|
|
Game::Grid Game::buildSpatialGrid() {
|
|
const float cellSize = 2 * Constants::BALL_RADIUS;
|
|
const float safeCellSize = std::max(cellSize, 0.001f);
|
|
Grid grid;
|
|
for (auto &object : m_objects) {
|
|
if (Ball *ball = dynamic_cast<Ball *>(object.get())) {
|
|
const sf::Vector2f &pos = ball->getPosition();
|
|
if (std::isfinite(pos.x) && std::isfinite(pos.y)) {
|
|
int cellX = static_cast<int>(std::floor(pos.x / safeCellSize));
|
|
int cellY = static_cast<int>(std::floor(pos.y / safeCellSize));
|
|
grid[{cellX, cellY}].push_back(ball);
|
|
}
|
|
}
|
|
}
|
|
return grid;
|
|
}
|
|
|
|
void Game::resolveSpatialCollisions(const Grid &grid) {
|
|
static const Cell forwardNeighbors[] = {
|
|
{0, 0}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}};
|
|
for (const auto &[cell, cellBalls] : grid) {
|
|
for (const auto &offset : forwardNeighbors) {
|
|
Cell neighborCell = {cell.first + offset.first,
|
|
cell.second + offset.second};
|
|
auto neighborIt = grid.find(neighborCell);
|
|
if (neighborIt == grid.end()) continue;
|
|
if (neighborCell == cell) {
|
|
for (size_t i = 0; i < cellBalls.size(); ++i) {
|
|
for (size_t j = i + 1; j < cellBalls.size(); ++j) {
|
|
cellBalls[i]->resolveCollision(*cellBalls[j]);
|
|
}
|
|
}
|
|
} else {
|
|
for (Ball *ballA : cellBalls) {
|
|
for (Ball *ballB : neighborIt->second) {
|
|
ballA->resolveCollision(*ballB);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Game::update() {
|
|
float dt = m_clock.restart().asSeconds() * m_timeScale;
|
|
if (dt > 0.1f) dt = 0.1f;
|
|
|
|
for (auto &object : m_objects) {
|
|
object->update(dt);
|
|
}
|
|
|
|
auto grid = buildSpatialGrid();
|
|
resolveSpatialCollisions(grid);
|
|
}
|
|
|
|
void Game::render() {
|
|
m_window.clear(sf::Color::Black);
|
|
|
|
m_debugLines.clear();
|
|
m_drawCallCount = 0;
|
|
|
|
for (const auto &object : m_objects) {
|
|
object->draw(m_window);
|
|
++m_drawCallCount;
|
|
|
|
if (!m_toggleDebug) continue;
|
|
if (auto *ball = dynamic_cast<Ball *>(object.get())) {
|
|
DebugDraw::addDirectionLine(m_debugLines, ball, m_window);
|
|
DebugDraw::addVelocityLine(m_debugLines, ball);
|
|
}
|
|
}
|
|
|
|
if (m_toggleDebug) {
|
|
DebugDraw::drawBatchedLines(m_window, m_debugLines);
|
|
m_debugOverlay.update(m_drawCallCount + 2, m_timeScale, m_window);
|
|
m_debugOverlay.draw(m_window);
|
|
}
|
|
|
|
m_window.display();
|
|
}
|
|
|
|
void Game::run() {
|
|
while (m_window.isOpen()) {
|
|
m_inputManager.processEvents(m_window);
|
|
update();
|
|
render();
|
|
}
|
|
}
|