Nazaj na multithreadanje - fonti

This commit is contained in:
2026-03-12 16:18:51 +01:00
parent 834f0b29c3
commit 74159a6fda
20 changed files with 254 additions and 43 deletions

25
src/game/agame/player.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include <game/agame/player.hpp>
namespace Game::AGame {
void Player::start() {
LOG("Created the Player");
if (mTex && mTex->getId() == "Arial") {
LOG("Player texture is a font");
// Treat as Font and build it
std::shared_ptr<Renderer::Font> font = std::dynamic_pointer_cast<Renderer::Font>(mTex);
if (font) {
font->build({255, 255, 255, 255}, "Hello, World!");
} else {
ERROR("Failed to cast texture to font");
}
}
}
void Player::update() {
if (!mIsActive) return;
//LOG("Updated Player");
mTransform.x += 0.5f; // Move right at a constant speed for testing
mTransform.rotation += 1.f; // Rotate clockwise for testing
}
}

31
src/game/gamemanager.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include <game/gamemanager.hpp>
#include <algorithm>
namespace Game {
void GameManager::run(std::stop_token stopToken) {
using namespace std::chrono_literals;
LOG("GameManager thread started");
while (!stopToken.stop_requested()) {
const int updatesPerSecond = std::max(1, mTargetUpdatesPerSecond);
const auto frameDuration = std::chrono::duration<double>(1.0 / static_cast<double>(updatesPerSecond));
const auto frameStart = std::chrono::steady_clock::now();
try {
State::GameState::getInstance().withEntitiesLocked([](auto& entities) {
for (auto& entity : entities) {
entity->update();
}
});
} catch (const std::exception& e) {
ERROR("Exception in GameManager thread: " << e.what());
}
const auto elapsed = std::chrono::steady_clock::now() - frameStart;
const auto remaining = frameDuration - elapsed;
if (remaining > 0s) {
std::this_thread::sleep_for(remaining);
}
}
}
}

View File

@@ -1,14 +0,0 @@
#include <game/player.hpp>
namespace Game::AGame {
void Player::start() {
LOG("Created the Player");
}
void Player::update() {
if (!mIsActive) return;
//LOG("Updated Player");
mTransform.x += 0.5f; // Move right at a constant speed for testing
mTransform.rotation += 1.f; // Rotate clockwise for testing
}
}

View File

@@ -3,9 +3,10 @@
#include <state/gamestate.hpp>
#include <object/entity.hpp>
#include <object/transform.hpp>
#include <game/player.hpp>
#include <game/agame/player.hpp>
#include <renderer/renderer.hpp>
#include <renderer/texture.hpp>
#include <renderer/font.hpp>
using namespace Game;
@@ -13,7 +14,9 @@ int main() {
Window::Window window = Window::Window();
window.init(1280, 720, "Game Window");
Object::Transform t1{100.f, 100.f, 0.f, 1.f, 1.f};
State::GameState::getInstance().addEntity(std::make_unique<AGame::Player>("Player", std::make_shared<Game::Renderer::Texture>("../resources/missing_texture.png", window.getRenderer()->getSDLRenderer()), Object::DEFAULT_TRANSFORM));
State::GameState::getInstance().addEntity(std::make_unique<AGame::Player>("Player2", std::make_shared<Game::Renderer::Font>("../resources/roboto.ttf", window.getRenderer()->getSDLRenderer(), 128, "Arial"), t1));
window.run();

View File

@@ -3,10 +3,8 @@
#include <renderer/texture.hpp>
namespace Game::Object {
Entity::~Entity() {
LOG("Destroyed Entity: " << mName);
}
Entity::~Entity() = default;
Entity::Entity(const Entity& other) : mName(other.mName), mTex(other.mTex), mTransform(other.mTransform), mIsActive(other.mIsActive) {
LOG("Copied Entity: " << mName);
}

55
src/renderer/font.cpp Normal file
View File

@@ -0,0 +1,55 @@
#include <renderer/font.hpp>
namespace Game::Renderer {
Font::Font(const std::string& path, SDL_Renderer* renderer, int ptSize, std::string id)
: Texture(id), mFont(nullptr), mRenderer(renderer) {
if (!mRenderer) {
ERROR("You must specify a renderer!");
return;
}
mFont = TTF_OpenFont(path.c_str(), ptSize);
if (!mFont) {
ERROR("Error opening font: " << SDL_GetError());
return;
}
}
Font::Font(const Font& other) {
this->mTex = other.mTex;
this->mFont = other.mFont;
}
Font& Font::operator=(const Font& other) {
this->mTex = other.mTex;
this->mFont = other.mFont;
return *this;
}
Font::~Font() {
if (mFont)
TTF_CloseFont(mFont);
}
void Font::build(SDL_Color color, std::string text) {
if (!mFont) { return; }
SDL_Surface* surf = TTF_RenderText_Blended(mFont, text.c_str(), text.size(), color);
if (!surf) {
ERROR("TTF_RenderText_Blended Error: " << SDL_GetError() << " (This object may be unusuable)");
return;
}
// Convert to texture
mTex = SDL_CreateTextureFromSurface(mRenderer, surf);
SDL_DestroySurface(surf);
if (!mTex) {
ERROR("SDL_CreateTextureFromSurface Error: " << SDL_GetError() << " (This object may be unusuable)");
return;
}
}
SDL_Texture* Font::getSDLTexture() { return mTex; }
std::string Font::getId() { return mId; }
}

View File

@@ -39,12 +39,14 @@ namespace Game::Renderer {
void Renderer::renderFrame() {
mClear();
// Get gamestate and render the objects here; GameState::getState().objects or something, idk
auto entities = Game::State::GameState::getInstance().getEntitiesRef();
//LOG("Entity count: " << entities->size());
for (auto& entity : *entities) {
entity->render(this);
try {
Game::State::GameState::getInstance().withEntitiesLocked([this](auto& entities) {
for (auto& entity : entities) {
entity->render(this);
}
});
} catch (const std::exception& e) {
ERROR("Exception while rendering frame: " << e.what());
}
mPresent();

View File

@@ -1,4 +1,8 @@
#include <renderer/texture.hpp>
#include <utility>
Game::Renderer::Texture::Texture(std::string id)
: mTex(nullptr), mId(std::move(id)) {}
Game::Renderer::Texture::Texture(const std::string& path, SDL_Renderer* renderer, std::string id)
: mTex(nullptr), mId(id) {

View File

@@ -2,11 +2,8 @@
#include <iostream>
namespace Game::State {
std::vector<std::unique_ptr<Object::Entity>>* GameState::getEntitiesRef() {
return &mEntities;
}
Object::Entity* GameState::getAtIndex(size_t at) {
std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
try {
return mEntities.at(at).get();
} catch (const std::out_of_range& e) {
@@ -16,8 +13,11 @@ namespace Game::State {
}
void GameState::addEntity(std::unique_ptr<Object::Entity> entity) {
std::lock_guard<std::mutex> lock(mMutex); // Lock the mutex for thread safety
mEntities.push_back(std::move(entity));
GameState::getInstance().getAtIndex(mEntities.size() - 1)->start(); // Call start() on the newly added entity
LOG("Added entity '" << GameState::getInstance().getAtIndex(mEntities.size() - 1)->getName() << "' to GameState");
Object::Entity* addedEntity = mEntities.back().get();
addedEntity->start(); // Initialize the entity immediately after insertion.
LOG("Added entity '" << addedEntity->getName() << "' to GameState");
}
}

View File

@@ -1,11 +1,18 @@
#include <window/window.hpp>
namespace Game::Window {
Window::Window() : mWindow(nullptr), mRenderer(), mRunning(false) { }
Window::Window() : mWindow(nullptr), mRenderer(), mGameManager(), mRunning(false) { }
Window::~Window() {
mRenderer.destroy();
// Try to kill the game slave thread
if (mGameThread.joinable()) {
mGameThread.request_stop();
mGameThread.join();
LOG("Game thread stopped successfully");
}
if (mWindow) {
SDL_DestroyWindow(mWindow);
mWindow = nullptr;
@@ -20,6 +27,12 @@ namespace Game::Window {
return false;
}
if (!TTF_Init()) {
ERROR("Failed to initialize SDL_ttf: " << SDL_GetError());
SDL_Quit();
return false;
}
mWindow = SDL_CreateWindow(title.c_str(), width, height, SDL_WINDOW_RESIZABLE);
if (!mWindow) {
ERROR("Failed to create window: " << SDL_GetError());
@@ -36,6 +49,8 @@ namespace Game::Window {
return false;
}
mGameThread = std::jthread(std::bind_front(&Game::GameManager::run, &mGameManager));
mRunning = true;
return true;
@@ -52,13 +67,14 @@ namespace Game::Window {
// Handle other events (e.g., keyboard, mouse) here
}
/*
auto entities = State::GameState::getInstance().getEntitiesRef();
for (auto& entity : *entities) {
entity->update();
}
}*/
mRenderer.renderFrame();
SDL_Delay(16); // ~60 FPS target, maybe make dynamic based on avg. frame time - TODO
SDL_Delay(1000 / mTargetFPS); // Delay to cap the frame rate to the target FPS
}
}
}