From 667ab73fc62ecc33ddad3b47039b1b32e6727ca9 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 7 Nov 2025 23:04:02 +0100 Subject: [PATCH] implementada la logica de so (time based) en Player per a imitar la anterior frame based (amb els seus fallos) --- source/game/entities/player.cpp | 161 +++++++++++++++++++++++--------- source/game/entities/player.hpp | 65 +++++++++---- source/game/scene_manager.hpp | 2 +- 3 files changed, 167 insertions(+), 61 deletions(-) diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index e13bcb1..afd0908 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -35,7 +35,7 @@ void Player::render() { void Player::update(float delta_time) { if (!is_paused_) { handleInput(); - updateState(); + updateState(delta_time); move(delta_time); animate(delta_time); handleBorders(); @@ -122,23 +122,29 @@ void Player::transitionToState(State state) { case State::STANDING: vy_ = 0; handleDeathByFalling(); + jump_sound_ctrl_.reset(); + fall_sound_ctrl_.reset(); break; case State::JUMPING: if (previous_state_ == State::STANDING) { vy_ = -MAX_VY; last_grounded_position_ = y_; updateVelocity(); + jump_sound_ctrl_.start(); } break; case State::FALLING: - last_grounded_position_ = y_; + fall_start_position_ = static_cast(y_); + last_grounded_position_ = static_cast(y_); vy_ = MAX_VY; vx_ = 0.0F; + jump_sound_ctrl_.reset(); + fall_sound_ctrl_.start(y_); break; } } -void Player::updateState() { +void Player::updateState(float delta_time) { switch (state_) { case State::STANDING: handleConveyorBelts(); @@ -149,12 +155,12 @@ void Player::updateState() { break; case State::JUMPING: auto_movement_ = false; - // playJumpSound(); + playJumpSound(delta_time); handleJumpEnd(); break; case State::FALLING: auto_movement_ = false; - // playFallSound(); + playFallSound(delta_time); break; } } @@ -188,20 +194,18 @@ void Player::handleBorders() { // Comprueba el estado del jugador void Player::handleState(float delta_time) { - (void)delta_time; // No usado actualmente - // Reproduce sonidos según el estado if (state_ == State::FALLING) { - playFallSound(); + playFallSound(delta_time); } else if (state_ == State::STANDING) { // Si no tiene suelo debajo y no está en rampa descendente -> FALLING if (!isOnFloor() && !isOnConveyorBelt() && !isOnDownSlope()) { last_grounded_position_ = static_cast(y_); // Guarda Y actual al SALIR de STANDING transitionToState(State::FALLING); // setState() establece vx_=0, vy_=MAX_VY - playFallSound(); + playFallSound(delta_time); } } else if (state_ == State::JUMPING) { - playJumpSound(); + playJumpSound(delta_time); } } @@ -438,35 +442,26 @@ void Player::handleJumpEnd() { } } -// Calcula y reproduce el sonido de salto basado en distancia vertical recorrida -void Player::playJumpSound() { - // Sistema basado en distancia vertical, no en tiempo (PLAYER_MECHANICS.md línea 107-120) - const float DISTANCE_FROM_START = std::abs(y_ - static_cast(last_grounded_position_)); - const int SOUND_INDEX = static_cast(DISTANCE_FROM_START / SOUND_DISTANCE_INTERVAL); - - // Calcular índice previo (frame anterior) - const float PREV_DISTANCE = std::abs(y_prev_ - static_cast(last_grounded_position_)); - const int PREVIOUS_INDEX = static_cast(PREV_DISTANCE / SOUND_DISTANCE_INTERVAL); - - // Solo reproduce cuando cambia de índice (nuevo hito alcanzado) - if (SOUND_INDEX != PREVIOUS_INDEX && SOUND_INDEX < static_cast(jumping_sound_.size())) { - Audio::get()->playSound(jumping_sound_[SOUND_INDEX], Audio::Group::GAME); +// Calcula y reproduce el sonido de salto basado en tiempo transcurrido +void Player::playJumpSound(float delta_time) { + size_t sound_index; + if (jump_sound_ctrl_.shouldPlay(delta_time, sound_index)) { + if (sound_index < jumping_sound_.size()) { + Audio::get()->playSound(jumping_sound_[sound_index], Audio::Group::GAME); + std::cout << sound_index << "\n"; + } } } + // Calcula y reproduce el sonido de caída basado en distancia vertical recorrida -void Player::playFallSound() { - // Sistema basado en distancia vertical, no en tiempo (PLAYER_MECHANICS.md línea 193-206) - const float DISTANCE_FALLEN = y_ - static_cast(last_grounded_position_); - const int SOUND_INDEX = static_cast(DISTANCE_FALLEN / SOUND_DISTANCE_INTERVAL); - - // Calcular índice previo (frame anterior) - const float PREV_DISTANCE = y_prev_ - static_cast(last_grounded_position_); - const int PREVIOUS_INDEX = static_cast(PREV_DISTANCE / SOUND_DISTANCE_INTERVAL); - - // Solo reproduce cuando cambia de índice (nuevo hito alcanzado) - if (SOUND_INDEX != PREVIOUS_INDEX && SOUND_INDEX < static_cast(falling_sound_.size())) { - Audio::get()->playSound(falling_sound_[SOUND_INDEX], Audio::Group::GAME); +void Player::playFallSound(float delta_time) { + size_t sound_index; + if (fall_sound_ctrl_.shouldPlay(delta_time, y_, sound_index)) { + if (sound_index < falling_sound_.size()) { + Audio::get()->playSound(falling_sound_[sound_index], Audio::Group::GAME); + std::cout << sound_index << "\n"; + } } } @@ -573,19 +568,99 @@ void Player::updateFeet() { // Inicializa los sonidos de salto y caida void Player::initSounds() { - jumping_sound_.clear(); - falling_sound_.clear(); + for (int i = 0; i < 24; ++i) { + std::string sound_file = "jump" + std::to_string(i + 1) + ".wav"; + jumping_sound_[i] = Resource::get()->getSound(sound_file); - for (int i = 1; i <= 24; ++i) { - std::string sound_file = "jump" + std::to_string(i) + ".wav"; - jumping_sound_.push_back(Resource::get()->getSound(sound_file)); - - if (i >= 11) { - falling_sound_.push_back(Resource::get()->getSound(sound_file)); + if (i >= 10) { // i+1 >= 11 + falling_sound_[i - 10] = Resource::get()->getSound(sound_file); } } } +// Implementación de JumpSoundController::start +void Player::JumpSoundController::start() { + current_index_ = 0; + elapsed_time_ = 0.0F; + active_ = true; +} + +// Implementación de JumpSoundController::reset +void Player::JumpSoundController::reset() { + active_ = false; + current_index_ = 0; + elapsed_time_ = 0.0F; +} + +// Implementación de JumpSoundController::shouldPlay +auto Player::JumpSoundController::shouldPlay(float delta_time, size_t& out_index) -> bool { + if (!active_) { + return false; + } + + // Acumula el tiempo transcurrido durante el salto + elapsed_time_ += delta_time; + + // Calcula qué sonido debería estar sonando según el tiempo + size_t target_index = FIRST_SOUND + static_cast(elapsed_time_ / SECONDS_PER_SOUND); + target_index = std::min(target_index, LAST_SOUND); + + // Reproduce si hemos avanzado a un nuevo sonido + if (target_index > current_index_) { + current_index_ = target_index; + out_index = current_index_; + return true; + } + + return false; +} + +// Implementación de FallSoundController::start +void Player::FallSoundController::start(float start_y) { + current_index_ = 0; + distance_traveled_ = 0.0F; + last_y_ = start_y; + active_ = true; +} + +// Implementación de FallSoundController::reset +void Player::FallSoundController::reset() { + active_ = false; + current_index_ = 0; + distance_traveled_ = 0.0F; +} + +// Implementación de FallSoundController::shouldPlay +auto Player::FallSoundController::shouldPlay(float delta_time, float current_y, size_t& out_index) -> bool { + (void)delta_time; // No usado actualmente, pero recibido por consistencia + + if (!active_) { + return false; + } + + // Acumula la distancia recorrida (solo hacia abajo) + if (current_y > last_y_) { + distance_traveled_ += (current_y - last_y_); + } + last_y_ = current_y; + + // Calcula qué sonido debería estar sonando según el intervalo + size_t target_index = FIRST_SOUND + static_cast(distance_traveled_ / PIXELS_PER_SOUND); + + // El sonido a reproducir se limita a LAST_SOUND (13), pero el índice interno sigue creciendo + size_t sound_to_play = std::min(target_index, LAST_SOUND); + + // Reproduce si hemos avanzado a un nuevo índice (permite repetición de sonido 13) + if (target_index > current_index_) { + current_index_ = target_index; // Guardamos el índice real (puede ser > LAST_SOUND) + out_index = sound_to_play; // Pero reproducimos LAST_SOUND cuando corresponde + return true; + } + + return false; +} + + // Aplica los valores de spawn al jugador void Player::applySpawnValues(const SpawnData& spawn) { x_ = spawn.x; diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index 858f598..bdd48aa 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -3,10 +3,10 @@ #include #include // Para array +#include // Para numeric_limits #include // Para shared_ptr, __shared_ptr_access #include // Para string #include -#include // Para vector #include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite #include "game/gameplay/room.hpp" @@ -29,6 +29,12 @@ class Player { STAY }; + // --- Constantes de física (públicas para permitir cálculos en structs) --- + static constexpr float HORIZONTAL_VELOCITY = 40.0F; // Velocidad horizontal en pixels/segundo (0.6 * 66.67fps) + static constexpr float MAX_VY = 80.0F; // Velocidad vertical máxima en pixels/segundo (1.2 * 66.67fps) + static constexpr float JUMP_VELOCITY = -80.0F; // Velocidad inicial del salto en pixels/segundo + static constexpr float GRAVITY_FORCE = 155.6F; // Fuerza de gravedad en pixels/segundo² (0.035 * 66.67²) + struct SpawnData { float x = 0; float y = 0; @@ -67,6 +73,37 @@ class Player { room(std::move(room)) {} }; + struct JumpSoundController { + // Duración del salto calculada automáticamente con física: t = 2 * v0 / g + static constexpr float JUMP_DURATION = (2.0F * MAX_VY) / GRAVITY_FORCE; + static constexpr size_t FIRST_SOUND = 1; // Primer sonido a reproducir (índice 1) + static constexpr size_t LAST_SOUND = 17; // Último sonido a reproducir (índice 17) + static constexpr float SECONDS_PER_SOUND = JUMP_DURATION / (LAST_SOUND - FIRST_SOUND + 1); + + size_t current_index_ = 0; // Índice del sonido actual + float elapsed_time_ = 0.0F; // Tiempo transcurrido durante el salto + bool active_ = false; // Indica si el controlador está activo + + void start(); // Inicia el controlador + void reset(); // Resetea el controlador + bool shouldPlay(float delta_time, size_t& out_index); // Comprueba si debe reproducir un sonido + }; + + struct FallSoundController { + static constexpr float PIXELS_PER_SOUND = 5.0F; // Intervalo de píxeles por sonido (configurable) + static constexpr size_t FIRST_SOUND = 1; // Primer sonido a reproducir (índice 1) + static constexpr size_t LAST_SOUND = 13; // Último sonido a reproducir (índice 13) + + size_t current_index_ = 0; // Índice del sonido actual + float distance_traveled_ = 0.0F; // Distancia acumulada durante la caída + float last_y_ = 0.0F; // Última posición Y registrada + bool active_ = false; // Indica si el controlador está activo + + void start(float start_y); // Inicia el controlador + void reset(); // Resetea el controlador + bool shouldPlay(float delta_time, float current_y, size_t& out_index); // Comprueba si debe reproducir un sonido + }; + // --- Constructor y Destructor --- explicit Player(const Data& player); ~Player() = default; @@ -91,16 +128,7 @@ class Player { static constexpr int HEIGHT = 16; // ALto del jugador static constexpr int MAX_FALLING_HEIGHT = TILE_SIZE * 4; // Altura maxima permitida de caída en pixels - // --- Constantes de física (per-second values) --- - static constexpr float HORIZONTAL_VELOCITY = 40.0F; // Velocidad horizontal en pixels/segundo (0.6 * 66.67fps) - static constexpr float MAX_VY = 80.0F; // Velocidad vertical máxima en pixels/segundo (1.2 * 66.67fps) - static constexpr float JUMP_VELOCITY = -80.0F; // Velocidad inicial del salto en pixels/segundo - static constexpr float GRAVITY_FORCE = 155.6F; // Fuerza de gravedad en pixels/segundo² (0.035 * 66.67²) - - // --- Constantes de sonido --- - static constexpr float SOUND_DISTANCE_INTERVAL = 3.0F; // Distancia en píxeles entre cada sonido de salto/caída - - // --- --- Objetos y punteros --- --- + // --- Objetos y punteros --- std::shared_ptr room_; // Objeto encargado de gestionar cada habitación del juego std::unique_ptr sprite_; // Sprite del jugador @@ -133,15 +161,18 @@ class Player { int last_grounded_position_ = 0; // Ultima posición en Y en la que se estaba en contacto con el suelo (hace doble función: tracking de caída + altura inicial del salto) // --- Variables de renderizado y sonido --- - Uint8 color_ = 0; // Color del jugador - std::vector jumping_sound_; // Vecor con todos los sonidos del salto - std::vector falling_sound_; // Vecor con todos los sonidos de la caída + Uint8 color_ = 0; // Color del jugador + std::array jumping_sound_; // Array con todos los sonidos del salto + std::array falling_sound_; // Array con todos los sonidos de la caída + JumpSoundController jump_sound_ctrl_; // Controlador de sonidos de salto + FallSoundController fall_sound_ctrl_; // Controlador de sonidos de caída + int fall_start_position_ = 0; // Posición Y al iniciar la caída void handleHorizontalMovement(float delta_time); void handleVerticalMovement(float delta_time); void handleConveyorBelts(); void handleShouldFall(); - void updateState(); + void updateState(float delta_time); void moveAndCollide(float delta_time); // --- Funciones de inicialización --- @@ -182,8 +213,8 @@ class Player { void handleBorders(); // Comprueba si se halla en alguno de los cuatro bordes void handleJumpEnd(); // Comprueba si ha finalizado el salto al alcanzar la altura de inicio auto handleKillingTiles() -> bool; // Comprueba que el jugador no toque ningun tile de los que matan - void playJumpSound(); // Calcula y reproduce el sonido de salto - void playFallSound(); // Calcula y reproduce el sonido de caer + void playJumpSound(float delta_time); // Calcula y reproduce el sonido de salto + void playFallSound(float delta_time); // Calcula y reproduce el sonido de caer void handleDeathByFalling(); // Gestiona la muerte al caer desde muy alto void updateVelocity(); // Calcula la velocidad en x }; \ No newline at end of file diff --git a/source/game/scene_manager.hpp b/source/game/scene_manager.hpp index e718ee9..9ee627a 100644 --- a/source/game/scene_manager.hpp +++ b/source/game/scene_manager.hpp @@ -34,7 +34,7 @@ enum class Options { // --- Variables de estado globales --- #ifdef _DEBUG -inline Scene current = Scene::TITLE; // Escena actual +inline Scene current = Scene::GAME; // Escena actual inline Options options = Options::LOGO_TO_LOADING_SCREEN; // Opciones de la escena actual #else inline Scene current = Scene::LOGO; // Escena actual