afegides regles per reescriure la clase Player

This commit is contained in:
2025-10-30 13:36:20 +01:00
parent c207b456f5
commit 1041aaf385
4 changed files with 1846 additions and 186 deletions

1529
PLAYER_MECHANICS.md Normal file

File diff suppressed because it is too large Load Diff

49
PLAYER_RULES.md Normal file
View File

@@ -0,0 +1,49 @@
# Reglas para la clase Player
## Estados
El jugador tiene tres estados diferenciados:
- STANDING
- JUMPING
- FALLING
## Puntos de colision
El jugador tiene un rectabgulo que lo delimita. Se obtiene con getRect()
Tiene ademas dos puntos en un vector de puntos llamado feet_ con el punto inferior izquierdo y el inferior derecho
Tiene otros dos puntos llamados under_feet_ que son los puntos inmediatamente inferiores a los puntos en feet_. Se usan para saber SOBRE qué tipo de superficie esta el jugador o si bajo solo tiene aire
Tiene otros 8 puntos (el jugador ocupa dos tiles en vertical del mapa, los tiles son de 8 pixeles) que son las 4 esquinas de los dos rectangulos de 8px que formarian al jugador. Se usa para comprobar si el jugador está tocando algun tile de los que matan
## Comprobar colisiones con el mapa
Esto es un poco marciano pero, el jugador genera un rectangulo de proyeccion. Digamos que si se va a mover 3 pixeles hacia la derecha, el rectangulo de proyeccion es el rectangulo que cubre la superficie entre donde está el jugador y donde estará el jugador. Se usa para ver si ese rectangulo (que suele ser de ancho o alto 1, ya que el jugador no llega a moverse mas de un pixel cada vez) colisiona con alguna superficie del mapa. En caso de colision se devuelve el punto de colision y se recoloca al jugador en ese punto. Eso para las superficies rectas.
## Reglas
A continuacion las reglas que se han de cumplir para el jugador en todo momento
- Si está STANDING -> vy_ = 0;
- Si no tiene suelo debajo y no está JUMPING -> FALLING
- Si está JUMPING y tropieza contra el techo -> FALLING
- Si está FALLING -> vx_ = 0
- Si está STANDING y tropieza lateralmente con una Slope, se pega a la slope
- Si esta FALLING y tiene una Slope bajo los pies, se pega a la slope
- La unica forma de atravesar una Slope es en estado JUMPING y con vx_ != 0
- Si salta, se guarda la posicion inicial en Y, si durante el salto está mas bajo de Y (es decir, el valor de y pasa a ser superior al inicial) -> FALLING
## Tipos de superficies
Hay tres tipos de superficies:
- Suelo normal
- Suelo tipo conveyor belt
- Rampas
## Reglas explicadas de manera mas abierta
Debido a que las reglas anteriores pueden sonar confusas y ser incompletas, se describen aqui con lenguaje mas natural:
- El jugador camina sobre las superficies
- Si tiene los dos pies sobre el aire -> cae
- El jugador sube y baja por las rampas
- La unica forma de atravesar las rampas es saltando sobre ellas en movimiento
- Un salto recto o una caida (las caidas siempre son rectas) hacen aterrizar al jugador sobre las rampas
- Las conveyor belt desplazan al jugador en la direccion de la conveyor belt
- El jugador cuando esta siendo desplazado por una conveyor belt, no puede cambiar de direccion
- Solo puede saltar y la propia inercia le hace saltar en movimiento
- Hay una excepcion en las conveyor belts y es que cuando el jugador cae sobre ellas, puede mantener la direccion que tenia el jugador. En el momento que el jugador deja de estar pulsando una direccion, ya se acopla al movimiento de la conveyor belt y no puede cambiar de direccion
- El jugador deja de estar ligado al movimiento de la conveyor belt cuando sus dos pies ya no la tienen debajo, bien porque hay suelo o bien porque hay aire -> cae
- El jugador no puede cambiar de direccion en el aire

View File

@@ -35,61 +35,42 @@ void Player::render() {
// Actualiza las variables del objeto
void Player::update(float delta_time) {
if (!is_paused_) {
checkInput(); // Comprueba las entradas y establece las velocidades y el flip
move(delta_time); // Recalcula la posición del jugador
animate(delta_time); // Establece la animación del jugador
updateState(delta_time); // Comprueba el estado del jugador
checkBorders(); // Comprueba si está situado en alguno de los cuatro bordes de la habitación
checkJumpEnd(); // Comprueba si ha finalizado el salto al alcanzar la altura de inicio
checkKillingTiles(); // Comprueba que el jugador no toque ningun tile de los que matan}
// 1. Procesamiento de entrada: captura las intenciones del jugador
checkInput();
// 2. Física: aplica gravedad y actualiza velocidades
applyGravity(delta_time);
updateVelocity();
// 3. Movimiento: ejecuta movimiento, actualiza estado durante movimiento, resuelve colisiones
move(delta_time);
// 4. Finalización: animación, comprobaciones y efectos
animate(delta_time);
checkBorders();
checkJumpEnd();
checkKillingTiles();
setColor();
}
}
// Comprueba las entradas y establece las velocidades y el flip
// Comprueba las entradas y establece las banderas de intención
void Player::checkInput() {
// Solo comprueba las entradas de dirección cuando está sobre una superficie
if (state_ != State::STANDING) {
return;
}
// Resetea las banderas de intención
want_to_jump_ = false;
want_to_move_left_ = false;
want_to_move_right_ = false;
if (!auto_movement_) {
// Comprueba las entradas de desplazamiento lateral solo en el caso de no estar enganchado a una superficie automatica
// Captura las intenciones de movimiento
if (Input::get()->checkInput(InputAction::LEFT)) {
vx_ = -HORIZONTAL_VELOCITY;
sprite_->setFlip(SDL_FLIP_HORIZONTAL);
left_or_right_is_pressed_ = true;
}
else if (Input::get()->checkInput(InputAction::RIGHT)) {
vx_ = HORIZONTAL_VELOCITY;
sprite_->setFlip(SDL_FLIP_NONE);
left_or_right_is_pressed_ = true;
}
else {
// No se pulsa ninguna dirección
vx_ = 0.0F;
left_or_right_is_pressed_ = false;
}
} else {
// El movimiento lo proporciona la superficie
vx_ = HORIZONTAL_VELOCITY * room_->getAutoSurfaceDirection();
sprite_->setFlip(vx_ > 0.0F ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL);
want_to_move_left_ = true;
} else if (Input::get()->checkInput(InputAction::RIGHT)) {
want_to_move_right_ = true;
}
// Captura la intención de salto
if (Input::get()->checkInput(InputAction::JUMP)) {
/*
Solo puede saltar si ademas de estar (State::STANDING)
Esta sobre el suelo, rampa o suelo que se mueve
Esto es para evitar el salto desde el vacio al cambiar de pantalla verticalmente
Ya que se coloca el estado State::STANDING al cambiar de pantalla
*/
if (isOnFloor() || isOnAutoSurface()) {
setState(State::JUMPING);
}
want_to_jump_ = true;
}
}
@@ -120,38 +101,120 @@ void Player::checkBorders() {
}
}
// Comprueba el estado del jugador
// Actualiza el estado del jugador basado en física e intenciones
void Player::updateState(float delta_time) {
// Actualiza las variables en función del estado
// Guarda el estado anterior para detectar transiciones
previous_state_ = state_;
// Máquina de estados: determina transiciones basándose en PLAYER_RULES.md
switch (state_) {
case State::STANDING: {
// Calcula la distancia de caída en pixels (velocidad * tiempo)
// Comprueba muerte por caída desde altura
if (previous_state_ == State::FALLING) {
const int FALLING_DISTANCE = static_cast<int>(y_) - last_grounded_position_;
if (previous_state_ == State::FALLING && FALLING_DISTANCE > MAX_FALLING_HEIGHT) {
// Si cae de muy alto, el jugador muere
if (FALLING_DISTANCE > MAX_FALLING_HEIGHT) {
is_alive_ = false;
}
vy_ = 0.0F;
jumping_time_ = 0.0F;
}
// Actualiza la posición de tierra
last_grounded_position_ = static_cast<int>(y_);
auto_movement_ = !isOnFloor() && isOnAutoSurface();
if (!isOnFloor() && !isOnAutoSurface() && !isOnDownSlope()) {
jumping_time_ = 0.0F;
// Regla: Si no tiene suelo debajo y no está JUMPING -> FALLING
if (shouldFall()) {
setState(State::FALLING);
return;
}
// Regla: Puede saltar si está sobre suelo o superficie automática
if (want_to_jump_ && canJump()) {
setState(State::JUMPING);
return;
}
// Nota: auto_movement_ se gestiona en updateVelocity() basado en el input
break;
}
case State::JUMPING: {
// Actualiza el tiempo de salto
jumping_time_ += delta_time;
playJumpSound();
// Regla: Si durante el salto Y > jump_init_pos -> FALLING
if (static_cast<int>(y_) >= jump_init_pos_ && vy_ > 0.0F) {
setState(State::FALLING);
return;
}
break;
}
case State::FALLING: {
vx_ = 0.0F;
vy_ = MAX_VY;
// Reproduce sonido de caída
playFallSound();
break;
}
default:
break;
}
}
// Comprueba si el jugador puede saltar
auto Player::canJump() -> bool {
// Solo puede saltar si está STANDING y sobre suelo o superficie automática
return state_ == State::STANDING && (isOnFloor() || isOnAutoSurface());
}
// Comprueba si el jugador debe caer
auto Player::shouldFall() -> bool {
// Cae si no tiene suelo, no está en superficie automática, y no está en rampa descendente
return !isOnFloor() && !isOnAutoSurface() && !isOnDownSlope();
}
// Actualiza velocidad basada en estado e intenciones
void Player::updateVelocity() {
switch (state_) {
case State::STANDING: {
// Regla: Si está STANDING -> vy_ = 0
vy_ = 0.0F;
if (!auto_movement_) {
// Movimiento normal: el jugador controla la dirección
if (want_to_move_left_) {
vx_ = -HORIZONTAL_VELOCITY;
sprite_->setFlip(SDL_FLIP_HORIZONTAL);
} else if (want_to_move_right_) {
vx_ = HORIZONTAL_VELOCITY;
sprite_->setFlip(SDL_FLIP_NONE);
} else {
// No se pulsa ninguna dirección
vx_ = 0.0F;
// Regla conveyor belt: cuando el jugador deja de pulsar, se acopla al movimiento
if (isOnAutoSurface()) {
auto_movement_ = true;
}
}
} else {
// Movimiento automático: conveyor belt controla la dirección
// Regla conveyor belt: el jugador no puede cambiar de dirección
vx_ = HORIZONTAL_VELOCITY * room_->getAutoSurfaceDirection();
sprite_->setFlip(vx_ > 0.0F ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL);
}
break;
}
case State::JUMPING: {
last_grounded_position_ = static_cast<int>(y_);
jumping_time_ += delta_time;
playJumpSound();
// Durante el salto, mantiene la velocidad horizontal
// La velocidad vertical la controla applyGravity()
break;
}
case State::FALLING: {
// Regla: Si está FALLING -> vx_ = 0 (no puede cambiar dirección en el aire)
vx_ = 0.0F;
// La velocidad vertical es MAX_VY (ya configurada por setState)
break;
}
@@ -199,88 +262,78 @@ void Player::applyGravity(float delta_time) {
}
}
// Maneja el movimiento horizontal hacia la izquierda
void Player::moveHorizontalLeft(float delta_time) {
// Crea el rectangulo de proyección en el eje X para ver si colisiona
// Maneja el movimiento sobre rampas
// direction: -1 para izquierda, 1 para derecha
void Player::handleSlopeMovement(int direction) {
// No procesa rampas durante el salto (permite atravesarlas cuando salta con movimiento horizontal)
// Pero SÍ procesa en STANDING y FALLING (para pegarse a las rampas)
if (state_ == State::JUMPING) {
return;
}
// Regla: Si está bajando la rampa, se pega a la slope
if (isOnDownSlope()) {
y_ += 1;
return;
}
// Regla: Si está STANDING y tropieza lateralmente con una Slope, se pega a la slope
// Comprueba si hay rampa en contacto lateral (solo los dos pixels inferiores)
const int SIDE_X = direction < 0 ? static_cast<int>(x_) : static_cast<int>(x_) + WIDTH - 1;
const LineVertical SIDE = {
.x = SIDE_X,
.y1 = static_cast<int>(y_) + HEIGHT - 2,
.y2 = static_cast<int>(y_) + HEIGHT - 1
};
// Comprueba la rampa correspondiente según la dirección
const int SLOPE_Y = direction < 0 ? room_->checkLeftSlopes(&SIDE) : room_->checkRightSlopes(&SIDE);
if (SLOPE_Y > -1) {
// Hay rampa: sube al jugador para pegarlo a la rampa
y_ = SLOPE_Y - HEIGHT;
}
}
// Maneja el movimiento horizontal
// direction: -1 para izquierda, 1 para derecha
void Player::moveHorizontal(float delta_time, int direction) {
const float DISPLACEMENT = vx_ * delta_time;
SDL_FRect proj = {
// Crea el rectangulo de proyección en el eje X para ver si colisiona
SDL_FRect proj;
if (direction < 0) {
// Movimiento a la izquierda
proj = {
.x = x_ + DISPLACEMENT,
.y = y_,
.w = std::ceil(std::fabs(DISPLACEMENT)), // Para evitar que tenga un ancho de 0 pixels
.h = HEIGHT};
// Comprueba la colisión con las superficies
const int POS = room_->checkRightSurfaces(&proj);
// Calcula la nueva posición
if (POS == -1) {
// Si no hay colisión
x_ += DISPLACEMENT;
.w = std::ceil(std::fabs(DISPLACEMENT)),
.h = HEIGHT
};
} else {
// Si hay colisión lo mueve hasta donde no colisiona
x_ = POS + 1;
std::cout << "LEFT\n";
}
// Si ha tocado alguna rampa mientras camina (sin saltar)
if (state_ == State::STANDING) {
// Si está bajando la rampa, recoloca al jugador
if (isOnDownSlope() && state_ == State::STANDING) {
y_ += 1;
std::cout << "RAMP DOWN LEFT\n";
}
// Asciende
else {
const LineVertical LEFT_SIDE = {.x = static_cast<int>(x_), .y1 = static_cast<int>(y_) + static_cast<int>(HEIGHT) - 2, .y2 = static_cast<int>(y_) + static_cast<int>(HEIGHT) - 1}; // Comprueba solo los dos pixels de abajo
const int LY = room_->checkLeftSlopes(&LEFT_SIDE);
if (LY > -1) {
y_ = LY - HEIGHT;
std::cout << "RAMP UP LEFT\n";
}
}
}
}
// Maneja el movimiento horizontal hacia la derecha
void Player::moveHorizontalRight(float delta_time) {
// Crea el rectangulo de proyección en el eje X para ver si colisiona
const float DISPLACEMENT = vx_ * delta_time;
SDL_FRect proj = {
// Movimiento a la derecha
proj = {
.x = x_ + WIDTH,
.y = y_,
.w = std::ceil(DISPLACEMENT), // Para evitar que tenga un ancho de 0 pixels
.h = HEIGHT};
.w = std::ceil(DISPLACEMENT),
.h = HEIGHT
};
}
// Comprueba la colisión
const int POS = room_->checkLeftSurfaces(&proj);
// Comprueba la colisión con las superficies
const int POS = direction < 0 ? room_->checkRightSurfaces(&proj) : room_->checkLeftSurfaces(&proj);
// Calcula la nueva posición
if (POS == -1) {
// Si no hay colisión
// No hay colisión: mueve al jugador
x_ += DISPLACEMENT;
} else {
// Si hay colisión lo mueve hasta donde no colisiona
x_ = POS - WIDTH;
std::cout << "RIGHT\n";
// Hay colisión: reposiciona al jugador en el punto de colisión
x_ = direction < 0 ? POS + 1 : POS - WIDTH;
}
// Si ha tocado alguna rampa mientras camina (sin saltar)
if (state_ == State::STANDING) {
// Si está bajando la rampa, recoloca al jugador
if (isOnDownSlope() && state_ == State::STANDING) {
y_ += 1;
std::cout << "RAMP DOWN RIGHT\n";
}
// Asciende
else {
const LineVertical RIGHT_SIDE = {.x = static_cast<int>(x_) + static_cast<int>(WIDTH) - 1, .y1 = static_cast<int>(y_) + static_cast<int>(HEIGHT) - 2, .y2 = static_cast<int>(y_) + static_cast<int>(HEIGHT) - 1}; // Comprueba solo los dos pixels de abajo
const int RY = room_->checkRightSlopes(&RIGHT_SIDE);
if (RY > -1) {
y_ = RY - HEIGHT;
std::cout << "RAMP UP RIGHT\n";
}
}
}
// Maneja el movimiento sobre rampas
handleSlopeMovement(direction);
}
// Maneja el movimiento vertical hacia arriba
@@ -302,9 +355,9 @@ void Player::moveVerticalUp(float delta_time) {
// Si no hay colisión
y_ += DISPLACEMENT;
} else {
// Si hay colisión lo mueve hasta donde no colisiona y entra en caída
// Si hay colisión lo mueve hasta donde no colisiona
// Regla: Si está JUMPING y tropieza contra el techo -> FALLING
y_ = POS + 1;
std::cout << "TOP\n";
setState(State::FALLING);
}
}
@@ -325,47 +378,45 @@ void Player::moveVerticalDown(float delta_time) {
if (POS > -1) {
// Si hay colisión lo mueve hasta donde no colisiona y pasa a estar sobre la superficie
y_ = POS - HEIGHT;
std::cout << "BOTTOM\n";
setState(State::STANDING);
auto_movement_ = false; // Desactiva conveyor belt al aterrizar
} else {
// Si no hay colisión con los muros, comprueba la colisión con las rampas
if (state_ != State::JUMPING) { // Las rampas no se miran si se está saltando
auto frect = getRect();
auto rect = toSDLRect(frect);
// Regla: La unica forma de atravesar una Slope es en estado JUMPING y con vx_ != 0
if (state_ != State::JUMPING || vx_ == 0.0F) {
// No está saltando o salta recto: se pega a las rampas
auto rect = toSDLRect(proj);
const LineVertical LEFT_SIDE = {.x = rect.x, .y1 = rect.y, .y2 = rect.y + rect.h - 1};
const LineVertical RIGHT_SIDE = {.x = rect.x + rect.w - 1, .y1 = rect.y, .y2 = rect.y + rect.h - 1};
std::cout << RIGHT_SIDE.x << " " << RIGHT_SIDE.y1 << " " << RIGHT_SIDE.y2 << "\n";
const float POINT = std::max(room_->checkRightSlopes(&RIGHT_SIDE), room_->checkLeftSlopes(&LEFT_SIDE));
if (POINT > -1) {
// No está saltando y hay colisión con una rampa
// Calcula la nueva posición
// Hay colisión con una rampa: se pega a ella
y_ = POINT - HEIGHT;
std::cout << "BOTTOM SLOPE\n";
setState(State::STANDING);
} else {
// No está saltando y no hay colisón con una rampa
// Calcula la nueva posición
// No hay colisión con rampa: continúa cayendo
y_ += DISPLACEMENT;
}
} else {
// Esta saltando y no hay colisión con los muros
// Calcula la nueva posición
// Está saltando con movimiento horizontal: atraviesa las rampas
y_ += DISPLACEMENT;
}
}
}
// Recalcula la posición del jugador y su animación
// Orquesta el movimiento del jugador
void Player::move(float delta_time) {
applyGravity(delta_time); // Aplica gravedad al jugador
// Movimiento horizontal
if (vx_ < 0.0F) {
moveHorizontalLeft(delta_time);
moveHorizontal(delta_time, -1); // Izquierda
} else if (vx_ > 0.0F) {
moveHorizontalRight(delta_time);
moveHorizontal(delta_time, 1); // Derecha
}
// Actualización de estado DURANTE el movimiento (después de horizontal, antes de vertical)
// Esto asegura que el estado se actualice con la posición correcta
updateState(delta_time);
// Movimiento vertical
if (vy_ < 0.0F) {
moveVerticalUp(delta_time);
@@ -373,7 +424,8 @@ void Player::move(float delta_time) {
moveVerticalDown(delta_time);
}
placeSprite(); // Coloca el sprite en la nueva posición
// Actualiza la geometría del collider y sprite
updateColliderGeometry();
}
// Establece la animación del jugador
@@ -545,42 +597,37 @@ void Player::updateFeet() {
// Cambia el estado del jugador
void Player::setState(State value) {
// Solo actualiza el estado y configura las variables iniciales
// NO llama a updateState() para evitar recursión circular
previous_state_ = state_;
state_ = value;
switch (state_) {
case State::STANDING:
vy_ = 0.0F;
last_grounded_position_ = static_cast<int>(y_);
std::cout << "SET STANDING\n";
// Se establecerá vy_ = 0 en updateVelocity()
break;
case State::JUMPING:
last_grounded_position_ = static_cast<int>(y_);
// Configura el salto
vy_ = JUMP_VELOCITY;
jump_init_pos_ = y_;
jumping_time_ = 0.0F;
std::cout << "SET JUMPING\n";
break;
case State::FALLING:
vx_ = 0.0F;
// Configura la caída
vy_ = MAX_VY;
playFallSound();
// vx_ = 0 se establecerá en updateVelocity()
if (previous_state_ == State::STANDING) {
last_grounded_position_ = static_cast<int>(y_);
}
auto_movement_ = false;
jumping_time_ = 0.0F;
std::cout << "SET FALLING\n";
break;
default:
break;
}
// Llama a checkState con delta_time 0 porque esto es un cambio de estado inmediato
updateState(0.0F);
}
// Inicializa los sonidos de salto y caida
@@ -619,9 +666,14 @@ void Player::initSprite(const std::string& animations_path) {
sprite_->setCurrentAnimation("walk");
}
// Coloca el sprite en la posición del jugador
void Player::placeSprite() {
sprite_->setPos(x_, y_); // Recoloca el sprite
// Actualiza collider_box y collision points
void Player::updateColliderGeometry() {
placeSprite(); // Coloca el sprite en la posición del jugador
collider_box_ = getRect(); // Actualiza el rectangulo de colisión
updateColliderPoints(); // Actualiza los puntos de colisión
}
// Coloca el sprite en la posición del jugador
void Player::placeSprite() {
sprite_->setPos(x_, y_);
}

View File

@@ -98,53 +98,83 @@ class Player {
std::shared_ptr<Room> room_; // Objeto encargado de gestionar cada habitación del juego
std::unique_ptr<SurfaceAnimatedSprite> sprite_; // Sprite del jugador
// --- Variables ---
// --- Variables de posición y física ---
float x_ = 0.0F; // Posición del jugador en el eje X
float y_ = 0.0F; // Posición del jugador en el eje Y
float vx_ = 0.0F; // Velocidad/desplazamiento del jugador en el eje X
float vy_ = 0.0F; // Velocidad/desplazamiento del jugador en el eje Y
Uint8 color_ = 0; // Color del jugador
// --- Variables de estado ---
State state_ = State::STANDING; // Estado en el que se encuentra el jugador. Util apara saber si está saltando o cayendo
State previous_state_ = State::STANDING; // Estado previo en el que se encontraba el jugador
// --- Variables de entrada (input intent) ---
bool want_to_jump_ = false; // Indica si el jugador quiere saltar
bool want_to_move_left_ = false; // Indica si el jugador quiere moverse a la izquierda
bool want_to_move_right_ = false; // Indica si el jugador quiere moverse a la derecha
// --- Variables de colisión ---
SDL_FRect collider_box_; // Caja de colisión con los enemigos u objetos
std::array<SDL_FPoint, 8> collider_points_{}; // Puntos de colisión con el mapa
std::array<SDL_FPoint, 2> under_feet_{}; // Contiene los puntos que hay bajo cada pie del jugador
std::array<SDL_FPoint, 2> feet_{}; // Contiene los puntos que hay en el pie del jugador
State state_ = State::STANDING; // Estado en el que se encuentra el jugador. Util apara saber si está saltando o cayendo
State previous_state_ = State::STANDING; // Estado previo en el que se encontraba el jugador
// --- Variables de juego ---
bool is_on_border_ = false; // Indica si el jugador esta en uno de los cuatro bordes de la pantalla
bool is_alive_ = true; // Indica si el jugador esta vivo o no
bool is_paused_ = false; // Indica si el jugador esta en modo pausa
bool auto_movement_ = false; // Indica si esta siendo arrastrado por una superficie automatica
Room::Border border_ = Room::Border::TOP; // Indica en cual de los cuatro bordes se encuentra
int jump_init_pos_ = 0; // Valor del eje Y en el que se inicia el salto
int last_grounded_position_ = 0; // Ultima posición en Y en la que se estaba en contacto con el suelo
// --- Variables de renderizado y sonido ---
Uint8 color_ = 0; // Color del jugador
std::vector<JA_Sound_t*> jumping_sound_; // Vecor con todos los sonidos del salto
std::vector<JA_Sound_t*> falling_sound_; // Vecor con todos los sonidos de la caída
float jumping_time_ = 0.0F; // Tiempo acumulado de salto en segundos
int last_grounded_position_ = 0; // Ultima posición en Y en la que se estaba en contacto con el suelo
bool left_or_right_is_pressed_ = false; // Indica si se está pulsando una de las dos direcciones. Sirve para las conveyor belts
// --- Funciones ---
void checkInput(); // Comprueba las entradas y modifica variables
void checkBorders(); // Comprueba si se halla en alguno de los cuatro bordes
void updateState(float delta_time); // Comprueba el estado del jugador
// --- Funciones de inicialización ---
void initSprite(const std::string& animations_path); // Inicializa el sprite del jugador
void initSounds(); // Inicializa los sonidos de salto y caida
void applySpawnValues(const SpawnData& spawn); // Aplica los valores de spawn al jugador
// --- Funciones de procesamiento de entrada ---
void checkInput(); // Comprueba las entradas y establece las banderas de intención
// --- Funciones de gestión de estado ---
void updateState(float delta_time); // Actualiza el estado del jugador basado en física e intenciones
void setState(State value); // Cambia el estado del jugador
auto canJump() -> bool; // Comprueba si el jugador puede saltar
auto shouldFall() -> bool; // Comprueba si el jugador debe caer
// --- Funciones de física ---
void applyGravity(float delta_time); // Aplica gravedad al jugador
void move(float delta_time); // Recalcula la posición del jugador y su animación
void moveHorizontalLeft(float delta_time); // Maneja el movimiento horizontal hacia la izquierda
void moveHorizontalRight(float delta_time); // Maneja el movimiento horizontal hacia la derecha
void updateVelocity(); // Actualiza velocidad basada en estado e intenciones
// --- Funciones de movimiento y colisión ---
void move(float delta_time); // Orquesta el movimiento del jugador
void moveHorizontal(float delta_time, int direction); // Maneja el movimiento horizontal (-1: izq, 1: der)
void moveVerticalUp(float delta_time); // Maneja el movimiento vertical hacia arriba
void moveVerticalDown(float delta_time); // Maneja el movimiento vertical hacia abajo
void animate(float delta_time); // Establece la animación del jugador
void checkJumpEnd(); // Comprueba si ha finalizado el salto al alcanzar la altura de inicio
void playJumpSound(); // Calcula y reproduce el sonido de salto
void playFallSound(); // Calcula y reproduce el sonido de caer
void handleSlopeMovement(int direction); // Maneja el movimiento sobre rampas
// --- Funciones de detección de superficies ---
auto isOnFloor() -> bool; // Comprueba si el jugador tiene suelo debajo de los pies
auto isOnAutoSurface() -> bool; // Comprueba si el jugador esta sobre una superficie automática
auto isOnDownSlope() -> bool; // Comprueba si el jugador está sobre una rampa hacia abajo
auto checkKillingTiles() -> bool; // Comprueba que el jugador no toque ningun tile de los que matan
// --- Funciones de actualización de geometría ---
void updateColliderGeometry(); // Actualiza collider_box y collision points
void updateColliderPoints(); // Actualiza los puntos de colisión
void updateFeet(); // Actualiza los puntos de los pies
void setState(State value); // Cambia el estado del jugador
void initSounds(); // Inicializa los sonidos de salto y caida
void placeSprite(); // Coloca el sprite en la posición del jugador
void applySpawnValues(const SpawnData& spawn); // Aplica los valores de spawn al jugador
void initSprite(const std::string& animations_path); // Inicializa el sprite del jugador
// --- Funciones de finalización ---
void animate(float delta_time); // Establece la animación del jugador
void checkBorders(); // Comprueba si se halla en alguno de los cuatro bordes
void checkJumpEnd(); // Comprueba si ha finalizado el salto al alcanzar la altura de inicio
auto checkKillingTiles() -> 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
};