This commit is contained in:
2026-05-13 07:58:59 +02:00
parent d9769bdbbb
commit 892d8f22eb
18 changed files with 242 additions and 77 deletions

5
TODO.txt Normal file
View File

@@ -0,0 +1,5 @@
Smeti random premikanje - ok
vnos imena kot text box
neko sledenje igralcu (nasprotnikov)
zavezniki naj nekaj delajo
fullscreen

View File

@@ -27,7 +27,7 @@ GAME_ENTITY(Background)
int mPendingLevelStage = 0; int mPendingLevelStage = 0;
// Periodic friendly spawn settings // Periodic friendly spawn settings
float mFriendlySpawnAvgInterval = 6.f; // average seconds between spawns float mFriendlySpawnAvgInterval = 6.f; // average seconds between spawns
int mMaxAutoFriendlies = 7; // hard cap for total active friendlies int mMaxAutoFriendlies = 2; // hard cap for total active friendlies
std::shared_ptr<Game::Renderer::Texture> mSeaTex; std::shared_ptr<Game::Renderer::Texture> mSeaTex;
std::shared_ptr<Game::Renderer::Texture> mEnemyTex; std::shared_ptr<Game::Renderer::Texture> mEnemyTex;
std::shared_ptr<Game::Renderer::Texture> mTrashTex; std::shared_ptr<Game::Renderer::Texture> mTrashTex;

View File

@@ -16,5 +16,8 @@ namespace Game::AGame {
float mMoveSpeedY = 0.f; float mMoveSpeedY = 0.f;
float mDirectionChangeTimer = 0.f; float mDirectionChangeTimer = 0.f;
float mShoreSpawnCooldown = 0.f; float mShoreSpawnCooldown = 0.f;
bool mFollowingPlayer = false;
static constexpr float FOLLOW_DISTANCE = 300.f;
static constexpr float FOLLOW_SPEED = 35.f;
END_GAME_ENTITY() END_GAME_ENTITY()
} }

View File

@@ -14,5 +14,11 @@ namespace Game::AGame {
float mMoveSpeedX = 0.f; float mMoveSpeedX = 0.f;
float mMoveSpeedY = 0.f; float mMoveSpeedY = 0.f;
float mDirectionChangeTimer = 0.f; float mDirectionChangeTimer = 0.f;
bool mOnSea = false;
static constexpr float CLEANUP_RADIUS = 50.f;
static constexpr int CLEANUP_SCORE_BONUS = 5;
static constexpr float LAND_SPEED_MIN = 20.f;
static constexpr float LAND_SPEED_MAX = 50.f;
static constexpr float SEA_SPEED = 14.f;
END_GAME_ENTITY() END_GAME_ENTITY()
} }

View File

@@ -17,6 +17,7 @@ namespace Game {
using clock = std::chrono::steady_clock; using clock = std::chrono::steady_clock;
enum class GameStateEnum { enum class GameStateEnum {
MENU,
RUNNING, RUNNING,
PAUSED, PAUSED,
STOPPED STOPPED

View File

@@ -11,6 +11,8 @@ namespace Game::Renderer {
float camY; float camY;
int screenW; int screenW;
int screenH; int screenH;
float scaleX; // Scale from logical (1280) to actual screen width
float scaleY; // Scale from logical (720) to actual screen height
} RendererConfig; } RendererConfig;
class Renderer { class Renderer {

View File

@@ -57,7 +57,10 @@ namespace Game::AGame {
mSeaTex->setTiled(true); mSeaTex->setTiled(true);
} }
mTiledScale = 0.5f; mTiledScale = 0.5f;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &mW, &mH);
// Use logical world dimensions (1280×720) not actual screen size
mW = 1280;
mH = 720;
// Land boundary: left 1/3 of map in centered coordinates // Land boundary: left 1/3 of map in centered coordinates
// For 1280px window: -640 (left) + 426.67 (1/3) = -213.33 // For 1280px window: -640 (left) + 426.67 (1/3) = -213.33
@@ -128,9 +131,11 @@ namespace Game::AGame {
const float viewBottom = mH / 2.f; const float viewBottom = mH / 2.f;
Object::Transform tS; Object::Transform tS;
tS.x = 0.f;
tS.y = 0.f;
tS.rotation = 0.f; tS.rotation = 0.f;
tS.scaleX = 6.f; tS.scaleX = 4.0f;
tS.scaleY = 6.f; tS.scaleY = 4.0f;
const float halfFriendlyW = mFriendlyTex->getWidth() * tS.adjustedScaleX() / 2.f; const float halfFriendlyW = mFriendlyTex->getWidth() * tS.adjustedScaleX() / 2.f;
const float halfFriendlyH = mFriendlyTex->getHeight() * tS.adjustedScaleY() / 2.f; const float halfFriendlyH = mFriendlyTex->getHeight() * tS.adjustedScaleY() / 2.f;
@@ -194,8 +199,8 @@ namespace Game::AGame {
Object::Transform tS; Object::Transform tS;
tS.rotation = 0.f; tS.rotation = 0.f;
tS.scaleX = 7.f; tS.scaleX = 4.7f;
tS.scaleY = 7.f; tS.scaleY = 4.7f;
const float halfEnemyW = mEnemyTex->getWidth() * tS.adjustedScaleX() / 2.f; const float halfEnemyW = mEnemyTex->getWidth() * tS.adjustedScaleX() / 2.f;
const float halfEnemyH = mEnemyTex->getHeight() * tS.adjustedScaleY() / 2.f; const float halfEnemyH = mEnemyTex->getHeight() * tS.adjustedScaleY() / 2.f;
for (int i = 0; i < enemyCount; ++i) { for (int i = 0; i < enemyCount; ++i) {
@@ -268,9 +273,11 @@ namespace Game::AGame {
const bool spawnSea = sideRoll < static_cast<int>(seaProb * 100.f); const bool spawnSea = sideRoll < static_cast<int>(seaProb * 100.f);
Object::Transform tS; Object::Transform tS;
tS.x = 0.f;
tS.y = 0.f;
tS.rotation = 0.f; tS.rotation = 0.f;
tS.scaleX = 6.f; tS.scaleX = 4.0f;
tS.scaleY = 6.f; tS.scaleY = 4.0f;
const float viewLeft = -mW / 2.f; const float viewLeft = -mW / 2.f;
const float viewRight = mW / 2.f; const float viewRight = mW / 2.f;
const float viewTop = -mH / 2.f; const float viewTop = -mH / 2.f;
@@ -320,7 +327,8 @@ namespace Game::AGame {
} }
void Background::onWindowResized(int newWidth, int newHeight) { void Background::onWindowResized(int newWidth, int newHeight) {
mW = newWidth; // Always maintain logical world dimensions (1280×720)
mH = newHeight; mW = 1280;
mH = 720;
} }
} }

View File

@@ -44,6 +44,22 @@ namespace Game::AGame {
const float dyv = py - ey; const float dyv = py - ey;
mIsVisible = (dxv * dxv + dyv * dyv) <= (revealRadius * revealRadius); mIsVisible = (dxv * dxv + dyv * dyv) <= (revealRadius * revealRadius);
// Check if player is on land (not in ship mode) and within follow distance
const float distanceToPlayer = std::sqrt(dxv * dxv + dyv * dyv);
const bool playerOnLand = !player->isShipMode();
const bool withinFollowRange = distanceToPlayer <= FOLLOW_DISTANCE;
if (playerOnLand && withinFollowRange) {
// Follow player: calculate direction and move at constant speed
mFollowingPlayer = true;
if (distanceToPlayer > 1.f) { // Avoid division by zero
mMoveSpeedX = (dxv / distanceToPlayer) * FOLLOW_SPEED;
mMoveSpeedY = (dyv / distanceToPlayer) * FOLLOW_SPEED;
}
} else {
// Revert to random movement when player is on sea or out of range
mFollowingPlayer = false;
// Semi-random movement with periodic direction changes // Semi-random movement with periodic direction changes
mDirectionChangeTimer += deltaTime; mDirectionChangeTimer += deltaTime;
if (mDirectionChangeTimer > 2.0f) { if (mDirectionChangeTimer > 2.0f) {
@@ -53,6 +69,7 @@ namespace Game::AGame {
mMoveSpeedY = std::sin(angle) * speed; mMoveSpeedY = std::sin(angle) * speed;
mDirectionChangeTimer = 0.f; mDirectionChangeTimer = 0.f;
} }
}
// Move enemy // Move enemy
mTransform.x += mMoveSpeedX * deltaTime; mTransform.x += mMoveSpeedX * deltaTime;
@@ -68,9 +85,9 @@ namespace Game::AGame {
const float halfWidth = entityWidth / 2.f; const float halfWidth = entityWidth / 2.f;
const float halfHeight = entityHeight / 2.f; const float halfHeight = entityHeight / 2.f;
// Get window dimensions for boundary calculations // Use logical world dimensions (1280×720) not actual screen size
int w = 0, h = 0; constexpr int w = 1280;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); constexpr int h = 720;
const float leftEdge = -w / 2.f + 25.f; const float leftEdge = -w / 2.f + 25.f;
if (mTransform.x - halfWidth < leftEdge) { if (mTransform.x - halfWidth < leftEdge) {

View File

@@ -1,9 +1,12 @@
#include <game/agame/friendly.hpp> #include <game/agame/friendly.hpp>
#include <game/gamemanager.hpp> #include <game/gamemanager.hpp>
#include <game/agame/player.hpp> #include <game/agame/player.hpp>
#include <game/agame/trash.hpp>
#include <object/components/boxcollider.hpp> #include <object/components/boxcollider.hpp>
#include <state/gamestate.hpp>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <limits>
#include <utils.hpp> #include <utils.hpp>
#include <window/window.hpp> #include <window/window.hpp>
@@ -13,51 +16,131 @@ namespace Game::AGame {
addComponent<Object::Components::BoxCollider>(); addComponent<Object::Components::BoxCollider>();
LOG("Zaveznik zagnan: " << getName()); LOG("Zaveznik zagnan: " << getName());
// Initialize random movement const float landBoundaryX = GameManager::getSharedData<float>("terrainLandBoundaryX");
const float entityWidth = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() : 0.f;
mOnSea = (mTransform.x + entityWidth / 2.f) > landBoundaryX;
if (!mOnSea) {
// Initialize random movement for land friendlies
const float angle = static_cast<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f; const float angle = static_cast<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f;
const float speed = 20.f + static_cast<float>(Utils::getUtils().rirng32(0, 30)); const float speed = LAND_SPEED_MIN + static_cast<float>(Utils::getUtils().rirng32(0, static_cast<int>(LAND_SPEED_MAX - LAND_SPEED_MIN)));
mMoveSpeedX = std::cos(angle) * speed; mMoveSpeedX = std::cos(angle) * speed;
mMoveSpeedY = std::sin(angle) * speed; mMoveSpeedY = std::sin(angle) * speed;
} else {
mMoveSpeedX = 0.f;
mMoveSpeedY = 0.f;
}
mDirectionChangeTimer = 0.f; mDirectionChangeTimer = 0.f;
} }
void Friendly::update(float deltaTime) { void Friendly::update(float deltaTime) {
(void)deltaTime; // Auto-cleanup nearby trash
const float fw = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() : 0.f;
const float fh = getTexture() ? getTexture()->getHeight() * mTransform.adjustedScaleY() : 0.f;
const float friendlyCenterX = mTransform.x + fw / 2.f;
const float friendlyCenterY = mTransform.y + fh / 2.f;
// Semi-random movement with periodic direction changes const float landBoundaryX = GameManager::getSharedData<float>("terrainLandBoundaryX");
mOnSea = friendlyCenterX > landBoundaryX;
if (mOnSea) {
auto snapshot = State::GameState::getInstance().getEntitiesSnapshot();
Trash* nearestTrash = nullptr;
float nearestDistanceSquared = std::numeric_limits<float>::max();
for (auto* entity : snapshot) {
if (!entity) continue;
auto* trash = dynamic_cast<Trash*>(entity);
if (!trash) continue;
const float tw = trash->getTexture() ? trash->getTexture()->getWidth() * trash->getTransform()->adjustedScaleX() : 0.f;
const float th = trash->getTexture() ? trash->getTexture()->getHeight() * trash->getTransform()->adjustedScaleY() : 0.f;
const float trashCenterX = trash->getTransform()->x + tw / 2.f;
const float trashCenterY = trash->getTransform()->y + th / 2.f;
const float dx = trashCenterX - friendlyCenterX;
const float dy = trashCenterY - friendlyCenterY;
const float distanceSquared = dx * dx + dy * dy;
if (distanceSquared < nearestDistanceSquared) {
nearestDistanceSquared = distanceSquared;
nearestTrash = trash;
}
}
if (nearestTrash) {
const float tw = nearestTrash->getTexture() ? nearestTrash->getTexture()->getWidth() * nearestTrash->getTransform()->adjustedScaleX() : 0.f;
const float th = nearestTrash->getTexture() ? nearestTrash->getTexture()->getHeight() * nearestTrash->getTransform()->adjustedScaleY() : 0.f;
const float trashCenterX = nearestTrash->getTransform()->x + tw / 2.f;
const float trashCenterY = nearestTrash->getTransform()->y + th / 2.f;
const float dx = trashCenterX - friendlyCenterX;
const float dy = trashCenterY - friendlyCenterY;
const float distance = std::sqrt(dx * dx + dy * dy);
if (distance > 0.0001f) {
mMoveSpeedX = (dx / distance) * SEA_SPEED;
mMoveSpeedY = (dy / distance) * SEA_SPEED;
}
}
} else {
// Semi-random movement with periodic direction changes on land
mDirectionChangeTimer += deltaTime; mDirectionChangeTimer += deltaTime;
if (mDirectionChangeTimer > 2.0f) { if (mDirectionChangeTimer > 2.0f) {
const float angle = static_cast<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f; const float angle = static_cast<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f;
const float speed = 20.f + static_cast<float>(Utils::getUtils().rirng32(0, 30)); const float speed = LAND_SPEED_MIN + static_cast<float>(Utils::getUtils().rirng32(0, static_cast<int>(LAND_SPEED_MAX - LAND_SPEED_MIN)));
mMoveSpeedX = std::cos(angle) * speed; mMoveSpeedX = std::cos(angle) * speed;
mMoveSpeedY = std::sin(angle) * speed; mMoveSpeedY = std::sin(angle) * speed;
mDirectionChangeTimer = 0.f; mDirectionChangeTimer = 0.f;
} }
}
auto snapshot = State::GameState::getInstance().getEntitiesSnapshot();
for (auto* entity : snapshot) {
if (!entity) continue;
auto* trash = dynamic_cast<Trash*>(entity);
if (!trash) continue;
// Calculate distance to trash
const float tw = trash->getTexture() ? trash->getTexture()->getWidth() * trash->getTransform()->adjustedScaleX() : 0.f;
const float th = trash->getTexture() ? trash->getTexture()->getHeight() * trash->getTransform()->adjustedScaleY() : 0.f;
const float trashCenterX = trash->getTransform()->x + tw / 2.f;
const float trashCenterY = trash->getTransform()->y + th / 2.f;
const float dx = friendlyCenterX - trashCenterX;
const float dy = friendlyCenterY - trashCenterY;
const float distanceSquared = dx * dx + dy * dy;
if (distanceSquared <= CLEANUP_RADIUS * CLEANUP_RADIUS) {
// Clean up this trash: award points and remove it
GameManager::setSharedData("gameScore", GameManager::getSharedData<int>("gameScore") + CLEANUP_SCORE_BONUS);
GameManager::setSharedData("trashActiveCount", std::max(0, GameManager::getSharedData<int>("trashActiveCount") - 1));
GameManager::destroyEntity(trash);
}
}
// Move friendly // Move friendly
mTransform.x += mMoveSpeedX * deltaTime; mTransform.x += mMoveSpeedX * deltaTime;
mTransform.y += mMoveSpeedY * deltaTime; mTransform.y += mMoveSpeedY * deltaTime;
// Clamp to land section // Clamp to land section
const float landBoundaryX = GameManager::getSharedData<float>("terrainLandBoundaryX"); const float halfWidth = fw / 2.f;
const float entityWidth = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() : 0.f; const float halfHeight = fh / 2.f;
const float entityHeight = getTexture() ? getTexture()->getHeight() * mTransform.adjustedScaleY() : 0.f;
const float halfWidth = entityWidth / 2.f;
const float halfHeight = entityHeight / 2.f;
// Get window dimensions for boundary calculations // Use logical world dimensions (1280×720) not actual screen size
int w = 0, h = 0; constexpr int w = 1280;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); constexpr int h = 720;
const float leftEdge = -w / 2.f + 25.f; const float leftEdge = -w / 2.f + 25.f;
if (mTransform.x - halfWidth < leftEdge) { if (mTransform.x - halfWidth < leftEdge) {
mTransform.x = leftEdge + halfWidth; mTransform.x = leftEdge + halfWidth;
mMoveSpeedX = std::abs(mMoveSpeedX); mMoveSpeedX = std::abs(mMoveSpeedX);
} }
if (mTransform.x + halfWidth > landBoundaryX - 25.f) { if (!mOnSea && mTransform.x + halfWidth > landBoundaryX - 25.f) {
mTransform.x = landBoundaryX - 25.f - halfWidth; mTransform.x = landBoundaryX - 25.f - halfWidth;
mMoveSpeedX = -std::abs(mMoveSpeedX); mMoveSpeedX = -std::abs(mMoveSpeedX);
} }
if (mOnSea && mTransform.x - halfWidth < landBoundaryX + 25.f) {
mTransform.x = landBoundaryX + 25.f + halfWidth;
mMoveSpeedX = std::abs(mMoveSpeedX);
}
if (mTransform.y - halfHeight < -h / 2.f + 25.f) { if (mTransform.y - halfHeight < -h / 2.f + 25.f) {
mTransform.y = -h / 2.f + 25.f + halfHeight; mTransform.y = -h / 2.f + 25.f + halfHeight;
mMoveSpeedY = std::abs(mMoveSpeedY); mMoveSpeedY = std::abs(mMoveSpeedY);

View File

@@ -13,9 +13,9 @@ namespace Game::AGame {
void HUDText::update(float deltaTime) { void HUDText::update(float deltaTime) {
(void)deltaTime; (void)deltaTime;
int windowW = 0; // Use logical world dimensions (1280×720) not actual screen size
int windowH = 0; constexpr int windowW = 1280;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &windowW, &windowH); constexpr int windowH = 720;
float camX = 0.f; float camX = 0.f;
float camY = 0.f; float camY = 0.f;
Object::Camera::getInstance().getPosition(camX, camY); Object::Camera::getInstance().getPosition(camX, camY);

View File

@@ -26,9 +26,9 @@ namespace Game::AGame {
return; return;
} }
int w = 0; // Use logical world dimensions (1280×720) not actual screen size
int h = 0; constexpr int w = 1280;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); constexpr int h = 720;
const float halfWidth = spawnTex->getWidth() * mTransform.adjustedScaleX() / 2.f; const float halfWidth = spawnTex->getWidth() * mTransform.adjustedScaleX() / 2.f;
const float halfHeight = spawnTex->getHeight() * mTransform.adjustedScaleY() / 2.f; const float halfHeight = spawnTex->getHeight() * mTransform.adjustedScaleY() / 2.f;
@@ -64,8 +64,8 @@ namespace Game::AGame {
Game::GameManager::setSharedData("gameStage", 1); Game::GameManager::setSharedData("gameStage", 1);
Game::GameManager::setSharedData("gameScore", 0); Game::GameManager::setSharedData("gameScore", 0);
mTransform.scaleX = 8.f; mTransform.scaleX = 5.3f;
mTransform.scaleY = 8.f; mTransform.scaleY = 5.3f;
if (!mShipTex) { if (!mShipTex) {
mShipTex = mTex; mShipTex = mTex;
@@ -74,9 +74,9 @@ namespace Game::AGame {
mGroundTex = mTex; mGroundTex = mTex;
} }
int w = 0; // Use logical world dimensions (1280×720) not actual screen size
int h = 0; constexpr int w = 1280;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); constexpr int h = 720;
const float halfWidth = mTex->getWidth() * mTransform.adjustedScaleX() / 2.f; const float halfWidth = mTex->getWidth() * mTransform.adjustedScaleX() / 2.f;
const float halfHeight = mTex->getHeight() * mTransform.adjustedScaleY() / 2.f; const float halfHeight = mTex->getHeight() * mTransform.adjustedScaleY() / 2.f;
@@ -134,9 +134,9 @@ namespace Game::AGame {
if (Input::isKeyPressed(SDL_SCANCODE_D)) { mTransform.x += mSpeed * deltaTime; mIsFlipped = true; } if (Input::isKeyPressed(SDL_SCANCODE_D)) { mTransform.x += mSpeed * deltaTime; mIsFlipped = true; }
mSpeed = Input::isKeyPressed(SDL_SCANCODE_LSHIFT) ? 400.f : 200.f; mSpeed = Input::isKeyPressed(SDL_SCANCODE_LSHIFT) ? 400.f : 200.f;
int w = 0; // Use logical world dimensions (1280×720) not actual screen size
int h = 0; constexpr int w = 1280;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); constexpr int h = 720;
const float entityWidth = mTex ? mTex->getWidth() * mTransform.adjustedScaleX() : 0.f; const float entityWidth = mTex ? mTex->getWidth() * mTransform.adjustedScaleX() : 0.f;
const float entityHeight = mTex ? mTex->getHeight() * mTransform.adjustedScaleY() : 0.f; const float entityHeight = mTex ? mTex->getHeight() * mTransform.adjustedScaleY() : 0.f;
const float minX = -w / 2.f; const float minX = -w / 2.f;

View File

@@ -11,15 +11,20 @@ namespace Game::AGame {
void Trash::update(float deltaTime) { void Trash::update(float deltaTime) {
(void)deltaTime; (void)deltaTime;
if (mSeaOnly) { /*if (mSeaOnly) {
const float landBoundaryX = GameManager::getSharedData<float>("terrainLandBoundaryX"); const float landBoundaryX = GameManager::getSharedData<float>("terrainLandBoundaryX");
const float margin = 25.f; const float margin = 25.f;
const float halfWidth = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() / 2.f : 0.f; const float halfWidth = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() / 2.f : 0.f;
if (mTransform.x - halfWidth < landBoundaryX + margin) { if (mTransform.x - halfWidth < landBoundaryX + margin) {
mTransform.x = landBoundaryX + margin + halfWidth; mTransform.x = landBoundaryX + margin + halfWidth;
} }
} }*/
return;
// Naključno premikanje
mTransform.x += static_cast<float>(Utils::getUtils().rirng32(-50, 50)) * deltaTime;
mTransform.y += static_cast<float>(Utils::getUtils().rirng32(-50, 50)) * deltaTime;
//return;
} }
void Trash::onCollisionEnter(Object::Entity* other) { void Trash::onCollisionEnter(Object::Entity* other) {

View File

@@ -2,7 +2,7 @@
#include <algorithm> #include <algorithm>
namespace Game { namespace Game {
GameStateEnum GameManager::mCurrentGameState = GameStateEnum::RUNNING; GameStateEnum GameManager::mCurrentGameState = GameStateEnum::MENU;
void GameManager::run(std::stop_token stopToken) { void GameManager::run(std::stop_token stopToken) {
using namespace std::chrono_literals; using namespace std::chrono_literals;

View File

@@ -30,14 +30,25 @@ int main() {
State::GameState::getInstance().addEntity(std::make_unique<AGame::Background>("BG", std::make_shared<Game::Renderer::Texture>("../resources/bgtest.png", window.getRenderer()->getSDLRenderer()), Object::DEFAULT_TRANSFORM)); State::GameState::getInstance().addEntity(std::make_unique<AGame::Background>("BG", std::make_shared<Game::Renderer::Texture>("../resources/bgtest.png", window.getRenderer()->getSDLRenderer()), Object::DEFAULT_TRANSFORM));
auto* player = dynamic_cast<AGame::Player*>(State::GameState::getInstance().addEntity(std::make_unique<AGame::Player>("Player", std::make_shared<Game::Renderer::Texture>("../resources/l3ladja.png", window.getRenderer()->getSDLRenderer()), Object::DEFAULT_TRANSFORM))); auto* player = dynamic_cast<AGame::Player*>(
State::GameState::getInstance().addEntity(
std::make_unique<AGame::Player>(
"Player",
std::make_shared<Game::Renderer::Texture>(
"../resources/l3ladja.png",
window.getRenderer()->getSDLRenderer()
),
Object::DEFAULT_TRANSFORM
)
)
);
if (player) { if (player) {
player->addComponent<Object::Components::BoxCollider>(); player->addComponent<Object::Components::BoxCollider>();
player->setShipTexture(std::make_shared<Game::Renderer::Texture>("../resources/l3ladja.png", window.getRenderer()->getSDLRenderer())); player->setShipTexture(std::make_shared<Game::Renderer::Texture>("../resources/l3ladja.png", window.getRenderer()->getSDLRenderer()));
player->setGroundTexture(std::make_shared<Game::Renderer::Texture>("../resources/l3player.png", window.getRenderer()->getSDLRenderer())); player->setGroundTexture(std::make_shared<Game::Renderer::Texture>("../resources/l3player.png", window.getRenderer()->getSDLRenderer()));
} }
State::GameState::getInstance().addEntity(std::make_unique<AGame::HUDText>("HUD", std::make_shared<Game::Renderer::Font>("../resources/roboto.ttf", window.getRenderer()->getSDLRenderer(), 72, "HUDFont"), Object::Transform{0.f, 0.f, 0.f, 1.f, 1.f}, 320.f, 40.f)); State::GameState::getInstance().addEntity(std::make_unique<AGame::HUDText>("HUD", std::make_shared<Game::Renderer::Font>("../resources/roboto.ttf", window.getRenderer()->getSDLRenderer(), 60, "HUDFont"), Object::Transform{0.f, 0.f, 0.f, 1.f, 1.f}, 320.f, 40.f));
window.run(); window.run();

View File

@@ -46,7 +46,7 @@ namespace Game::Object {
SDL_GetTextureSize(mTex->getSDLTexture(), &w, &h); SDL_GetTextureSize(mTex->getSDLTexture(), &w, &h);
SDL_FRect dst; SDL_FRect dst;
dst.w = w * mTransform.scaleX * UNIVERSAL_SCALE_COEFFICIENT; // 1.f is HUGE, so this is just a constant to make the default scale more reasonable dst.w = w * mTransform.scaleX * UNIVERSAL_SCALE_COEFFICIENT;
dst.h = h * mTransform.scaleY * UNIVERSAL_SCALE_COEFFICIENT; dst.h = h * mTransform.scaleY * UNIVERSAL_SCALE_COEFFICIENT;
// Top-left origin; Account for camera position (center the camera on the screen) // Top-left origin; Account for camera position (center the camera on the screen)
@@ -73,7 +73,7 @@ namespace Game::Object {
SDL_GetTextureSize(mTex->getSDLTexture(), &tileW, &tileH); SDL_GetTextureSize(mTex->getSDLTexture(), &tileW, &tileH);
SDL_FRect dst; SDL_FRect dst;
dst.w = tileW * mTiledScale; // Tile size is the original texture size multiplied by the universal scale coefficient (ignoring the entity's scale, since that only affects how many times the texture is tiled, not the size of each tile) dst.w = tileW * mTiledScale;
dst.h = tileH * mTiledScale; dst.h = tileH * mTiledScale;
// Top-left origin; Account for camera position (center the camera on the screen) // Top-left origin; Account for camera position (center the camera on the screen)

View File

@@ -1,5 +1,6 @@
#include <object/ui/uitextbox.hpp> #include <object/ui/uitextbox.hpp>
#include <renderer/renderer.hpp> #include <renderer/renderer.hpp>
#include <object/camera.hpp>
namespace Game::Object { namespace Game::Object {
@@ -112,15 +113,29 @@ namespace Game::Object {
bool UITextBox::isFocused() const { return mIsFocused; } bool UITextBox::isFocused() const { return mIsFocused; }
bool UITextBox::isMouseInsideBox() const { bool UITextBox::isMouseInsideBox() const {
const float mouseX = Input::getMouseX(); // Get screen-space mouse coordinates
const float mouseY = Input::getMouseY(); const float screenMouseX = Input::getMouseX();
const float screenMouseY = Input::getMouseY();
// Get window dimensions
int windowW = 0, windowH = 0;
SDL_GetWindowSizeInPixels(SDL_GetMouseFocus(), &windowW, &windowH);
// Get camera position
float camX = 0.f, camY = 0.f;
Object::Camera::getInstance().getPosition(camX, camY);
// Convert screen coordinates to world coordinates
const float worldMouseX = screenMouseX - windowW / 2.f + camX;
const float worldMouseY = screenMouseY - windowH / 2.f + camY;
// Check bounds in world space
const float left = mTransform.x - mConfig.paddingX; const float left = mTransform.x - mConfig.paddingX;
const float right = left + mBoxWidth + 2.f * mConfig.paddingX; const float right = left + mBoxWidth + 2.f * mConfig.paddingX;
const float top = mTransform.y - mConfig.paddingY; const float top = mTransform.y - mConfig.paddingY;
const float bottom = top + mBoxHeight + 2.f * mConfig.paddingY; const float bottom = top + mBoxHeight + 2.f * mConfig.paddingY;
return mouseX >= left && mouseX <= right && mouseY >= top && mouseY <= bottom; return worldMouseX >= left && worldMouseX <= right && worldMouseY >= top && worldMouseY <= bottom;
} }
void UITextBox::refreshVisualText() { void UITextBox::refreshVisualText() {

View File

@@ -60,8 +60,18 @@ namespace Game::Renderer {
Object::Camera::getInstance().getPosition(camX, camY); Object::Camera::getInstance().getPosition(camX, camY);
int screenW, screenH; int screenW, screenH;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &screenW, &screenH); SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &screenW, &screenH);
// Pass the config to avoid wasting time recalculating it for every entity, since it's not gonna change during the frame
RendererConfig config{ camX, camY, screenW, screenH }; // Logical game world is 1280x720; scale rendering if fullscreen is at different resolution
static constexpr int LOGICAL_WIDTH = 1280;
static constexpr int LOGICAL_HEIGHT = 720;
const float scaleX = static_cast<float>(screenW) / LOGICAL_WIDTH;
const float scaleY = static_cast<float>(screenH) / LOGICAL_HEIGHT;
// Apply SDL render scale to handle fullscreen scaling
SDL_SetRenderScale(mRenderer, scaleX, scaleY);
// Pass the config WITHOUT scaling factors (SDL handles it now)
RendererConfig config{ camX, camY, LOGICAL_WIDTH, LOGICAL_HEIGHT, 1.0f, 1.0f };
try { try {
Game::State::GameState::getInstance().withEntitiesLocked([&](auto& entities) { Game::State::GameState::getInstance().withEntitiesLocked([&](auto& entities) {
@@ -89,6 +99,9 @@ namespace Game::Renderer {
} }
mPresent(); mPresent();
// Reset render scale for next frame
SDL_SetRenderScale(mRenderer, 1.0f, 1.0f);
} }
void Renderer::mClear() { void Renderer::mClear() {

View File

@@ -41,7 +41,7 @@ namespace Game::Window {
Audio::Audio::getInstance().init(); Audio::Audio::getInstance().init();
mWindow = SDL_CreateWindow(title.c_str(), width, height, 0); mWindow = SDL_CreateWindow(title.c_str(), width, height, SDL_WINDOW_FULLSCREEN);
if (!mWindow) { if (!mWindow) {
ERROR("Failed to create window: " << SDL_GetError()); ERROR("Failed to create window: " << SDL_GetError());
SDL_Quit(); SDL_Quit();
@@ -95,10 +95,6 @@ namespace Game::Window {
mRunning = false; mRunning = false;
} }
if (event.type == SDL_EVENT_WINDOW_ENTER_FULLSCREEN) {
SDL_SetWindowFullscreen(mWindow, false);
}
// Window resize event - update the renderer's viewport // Window resize event - update the renderer's viewport
if (event.type == SDL_EVENT_WINDOW_RESIZED) { if (event.type == SDL_EVENT_WINDOW_RESIZED) {
std::scoped_lock lock(mMutex); std::scoped_lock lock(mMutex);