From ed0f6784d96c589e2b842330525a92a35553db4e Mon Sep 17 00:00:00 2001 From: David Hermann <redeagle.private@gmail.com> Date: Wed, 5 Feb 2025 20:37:31 +0100 Subject: [PATCH] Implementing animated movement of units (tile-based) - Creating `Box2dHelper.hpp` with inline converting helper functions of Box2D coordinates, tiles and pixels - Outsourcing `PIXELS_PER_METER` into `Box2dHelper.hpp` - Setting `PIXELS_PER_METER` from 32 to 16 (tile length = 1 meter) --- src/game/Box2dHelper.hpp | 42 ++++++++++++++++ src/game/Bullet.cpp | 1 + src/game/Bullet.hpp | 4 -- src/game/Level.cpp | 5 +- src/game/Unit.cpp | 103 +++++++++++++++++++++++++++++---------- src/game/Unit.hpp | 14 ++++++ 6 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 src/game/Box2dHelper.hpp diff --git a/src/game/Box2dHelper.hpp b/src/game/Box2dHelper.hpp new file mode 100644 index 0000000..cbec14e --- /dev/null +++ b/src/game/Box2dHelper.hpp @@ -0,0 +1,42 @@ +namespace advanced_wars +{ + +constexpr float PIXELS_PER_METER = 16.0f; + +/// Tile → Pixel +inline int tileToPixel(int tile) +{ + return tile * 16; +} + +/// Pixel → Tile +inline int pixelToTile(int pixel) +{ + return pixel / 16; +} + +/// Box2D (Meter) → Pixel +inline float worldToPixel(float world) +{ + return world * PIXELS_PER_METER; +} + +/// Pixel → Box2D (Meter) +inline float pixelToWorld(float pixel) +{ + return pixel / PIXELS_PER_METER; +} + +/// Tile → Box2D (Meter) (Oberer linker Punkt des Tiles) +inline float tileToWorld(int tile) +{ + return pixelToWorld(tileToPixel(tile)); +} + +/// Box2D (Meter) → Tile (Berechnung über Pixel) +inline int worldToTile(float world) +{ + return pixelToTile(worldToPixel(world)); +} + +} \ No newline at end of file diff --git a/src/game/Bullet.cpp b/src/game/Bullet.cpp index fbb35d9..974b834 100644 --- a/src/game/Bullet.cpp +++ b/src/game/Bullet.cpp @@ -1,4 +1,5 @@ #include "Bullet.hpp" +#include "Box2dHelper.hpp" #include "Engine.hpp" #include "UnitContactListener.hpp" #include <iostream> diff --git a/src/game/Bullet.hpp b/src/game/Bullet.hpp index 6886ea2..60a8faa 100644 --- a/src/game/Bullet.hpp +++ b/src/game/Bullet.hpp @@ -8,10 +8,6 @@ namespace advanced_wars { -// Wir definieren einen Umrechnungsfaktor, um zwischen Pixeln und Box2D-Metern umzurechnen. -// Passe diesen Wert an deine Spielwelt an. Hier gehen wir von 32 Pixel pro Meter aus. -constexpr float PIXELS_PER_METER = 32.0f; - class Bullet { public: diff --git a/src/game/Level.cpp b/src/game/Level.cpp index 585d558..b0883c0 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -1,4 +1,5 @@ #include "Level.hpp" +#include "Box2dHelper.hpp" #include "Building.hpp" #include "Effect.hpp" #include "Engine.hpp" @@ -241,8 +242,7 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) } else { - - m_units.at(m_selectedUnit)->updatePosition(tileX, tileY); + m_units.at(m_selectedUnit)->moveToTile(tileX, tileY); } } else @@ -309,6 +309,7 @@ void Level::render(Engine& engine) // Units for (auto& [id, unit] : m_units) { + unit->update(); unit->render(engine, RENDERING_SCALE); } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 479ba75..8ca69fd 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -1,4 +1,5 @@ #include "Unit.hpp" +#include "Box2dHelper.hpp" #include "Bullet.hpp" #include "UnitContactListener.hpp" #include <iostream> @@ -51,6 +52,8 @@ void Unit::setWorld(b2World* world) void Unit::render(Engine& engine, int scale) { + b2Vec2 pos = m_body->GetPosition(); + Spritesheet* spritesheet = engine.getSpritesheet(); int step = engine.getStage() % spritesheet->getUnitTextures() @@ -58,56 +61,44 @@ void Unit::render(Engine& engine, int scale) .at(static_cast<int>(m_id)) .at(static_cast<int>(m_state)) .second; - + SDL_Rect src; + SDL_Rect dst; if (m_state == UnitState::IDLE || m_state == UnitState::UNAVAILABLE) { - SDL_Rect src; src.x = step * spritesheet->getUnitWidth(); src.y = 0; src.w = spritesheet->getUnitWidth(); src.h = spritesheet->getUnitHeight(); - SDL_Rect dst; - dst.x = m_x * spritesheet->getUnitWidth() * scale; - dst.y = m_y * spritesheet->getUnitHeight() * scale; + dst.x = worldToTile(pos.x) * spritesheet->getUnitWidth() * scale; + dst.y = worldToTile(pos.y) * spritesheet->getUnitHeight() * scale; dst.w = spritesheet->getUnitWidth() * scale; dst.h = spritesheet->getUnitHeight() * scale; - - SDL_RenderCopyEx( - engine.renderer(), - spritesheet->getUnitTextures() - .at(static_cast<int>(m_faction)) - .at(static_cast<int>(m_id)) - .at(static_cast<int>(m_state)) - .first, - &src, &dst, 0, NULL, SDL_FLIP_NONE); } else { // The moving states have a resolution of 24x24 instead of 16x16 and need to // be handled separately - SDL_Rect src; src.x = step * spritesheet->getUnitMovingWidth(); src.y = 0; src.w = spritesheet->getUnitMovingWidth(); src.h = spritesheet->getUnitMovingHeight(); - SDL_Rect dst; - dst.x = ((m_x * spritesheet->getUnitWidth()) - 4) * scale; - dst.y = ((m_y * spritesheet->getUnitHeight()) - 4) * scale; + dst.x = ((worldToTile(pos.x) * spritesheet->getUnitWidth()) - 4) * scale; + dst.y = ((worldToTile(pos.y) * spritesheet->getUnitHeight()) - 4) * scale; dst.w = spritesheet->getUnitMovingWidth() * scale; dst.h = spritesheet->getUnitMovingHeight() * scale; - - SDL_RenderCopyEx( - engine.renderer(), - spritesheet->getUnitTextures() - .at(static_cast<int>(m_faction)) - .at(static_cast<int>(m_id)) - .at(static_cast<int>(m_state)) - .first, - &src, &dst, 0, NULL, SDL_FLIP_NONE); } + + SDL_RenderCopyEx( + engine.renderer(), + spritesheet->getUnitTextures() + .at(static_cast<int>(m_faction)) + .at(static_cast<int>(m_id)) + .at(static_cast<int>(m_state)) + .first, + &src, &dst, 0, NULL, SDL_FLIP_NONE); } void Unit::attack(Unit& enemy) @@ -287,4 +278,62 @@ int Unit::getMapId() return this->m_mapId; } +void Unit::moveToTile(int targetX, int targetY) +{ + // Speichere die Ziel-Tile-Koordinaten + m_targetTileX = targetX; + m_targetTileY = targetY; + + // Zielposition in Meter umrechnen (Mitte des Tiles) + float worldTargetX = (targetX * 16 + 8) / PIXELS_PER_METER; + float worldTargetY = (targetY * 16 + 8) / PIXELS_PER_METER; + + // Aktuelle Position abrufen + b2Vec2 currentPos = m_body->GetPosition(); + + // Differenz berechnen + float deltaX = worldTargetX - currentPos.x; + float deltaY = worldTargetY - currentPos.y; + + // Distanz berechnen + float distance = std::sqrt(deltaX * deltaX + deltaY * deltaY); + if (distance < 0.1f) + { + return; // Falls schon fast da, nichts tun + } + + // Normierte Richtung berechnen + float directionX = deltaX / distance; + float directionY = deltaY / distance; + + // Geschwindigkeit setzen + float speed = 2.0f; // Tiles pro Sekunde + m_body->SetLinearVelocity(b2Vec2(directionX * speed, directionY * speed)); + + // State setzen + calcState(targetX, targetY); +} + +void Unit::update() +{ + b2Vec2 pos = m_body->GetPosition(); + + // Berechne aktuelle Tile-Position + int currentTileX = static_cast<int>((pos.x * PIXELS_PER_METER) / 16); + int currentTileY = static_cast<int>((pos.y * PIXELS_PER_METER) / 16); + /* if (getMapId() == 66) + { + std::cout << "Current Tile: " << currentTileX << ", " << currentTileY << std::endl; + } */ + // Prüfe, ob wir am Ziel sind + if (currentTileX == m_targetTileX && currentTileY == m_targetTileY) + { + m_body->SetLinearVelocity(b2Vec2(0, 0)); // Bewegung stoppen + m_x = m_targetTileX; // Stelle sicher, dass die Unit auf dem richtigen Tile registriert + // ist + m_y = m_targetTileY; + m_state = UnitState::IDLE; + } +} + } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Unit.hpp b/src/game/Unit.hpp index ff26387..7450a81 100644 --- a/src/game/Unit.hpp +++ b/src/game/Unit.hpp @@ -201,6 +201,17 @@ class Unit */ int getMapId(); + /** + * @brief Aktualisiert den Zustand der Unit. + * + * Diese Methode prüft, ob sich die Unit gerade bewegt und ob sie nahe genug am Ziel ist. + * Ist das der Fall, wird die Bewegung gestoppt, und die Tile-Koordinaten werden + * aktualisiert. + */ + void update(); + + void moveToTile(int targetX, int targetY); + private: UnitFaction m_faction; UnitId m_id; @@ -209,6 +220,9 @@ class Unit b2World* m_world = nullptr; int m_mapId = -1; + int m_targetTileX; + int m_targetTileY; + int m_maxHealth; // max_health required for damage_scaling int m_range; int m_fuel; -- GitLab