refactor(player): divideix update en tick+applyMovement i exposa velocitat intencionada

This commit is contained in:
JailDesigner committed 2026-04-24 18:49:52 +02:00
1 parent 820b9f2d21
commit 1cc2e28b1b
2 files changed
+88 -23

No files matched your search

+38 -23
View File
@@ -241,6 +241,21 @@ Player::Player(const std::string& anim_key, int id, float x, float y, const Cont
}
void Player::update(float delta_time) {
tick(delta_time);
applyMovement(delta_time);
}
auto Player::collidesWithWorld(const SDL_FRect& box) const -> bool {
if (map_.collides(box)) { return true; }
if (all_players_ == nullptr) { return false; }
return std::ranges::any_of(*all_players_, [this, &box](const Player* p) {
if (p == nullptr || p == this) { return false; }
const auto OTHER = p->getBaseBox();
return SDL_HasRectIntersectionFloat(&box, &OTHER);
});
}
void Player::tick(float delta_time) {
// Via `InputState` en lloc de `SDL_GetKeyboardState` directe: quan el
// service menu (o qualsevol UI modal) està obert, el wrapper retorna
// un buffer tot-a-zero perquè el jugador no es mogui mentre l'usuari
@@ -253,31 +268,32 @@ void Player::update(float delta_time) {
if (keys[controls_.up]) { vy -= SPEED_PX_PER_S; }
if (keys[controls_.down]) { vy += SPEED_PX_PER_S; }
// Cruceta i stick esquerre del primer gamepad (si n'hi ha). També
// Cruceta i stick esquerre del mando assignat (si n'hi ha). També
// respecten el bloqueig d'`InputState`, així que amb el menú obert
// els botons tornen false/0 i no contribueixen a la velocitat.
// Direccions oposades (teclat left + d-pad right) es cancel·len de
// forma natural; premuda simultània en la mateixa direcció no duplica
// la velocitat perquè cada branca només resta/suma una vegada.
accumulateGamepadMovement(id_, vx, vy);
intended_vx_ = vx;
intended_vy_ = vy;
setVelX(vx);
setVelY(vy);
updateFacingAndTurn(delta_time, vx, vy);
}
void Player::applyMovement(float delta_time) {
// El resolver del GameScene pot haver escalat `intended_vx_/vy_`
// (cas push: velocitat * push_factor) o haver-les anul·lat (cas
// head-on). Consumim-les aquí com a velocitat definitiva del frame.
const float VX = intended_vx_;
const float VY = intended_vy_;
// Snap al passadís: si només hi ha moviment horitzontal (o vertical) i
// la caixa està prou prop d'una lane perpendicular (≤ mitja caixa), la
// Y (o X) es corregeix gradualment per enfilar el passadís. Amb
// moviment diagonal no es fa snap — l'usuari pilota manualment.
const bool ONLY_H = (vx != 0.0F) && (vy == 0.0F);
const bool ONLY_V = (vy != 0.0F) && (vx == 0.0F);
// Y (o X) es corregeix gradualment per enfilar el passadís.
const bool ONLY_H = (VX != 0.0F) && (VY == 0.0F);
const bool ONLY_V = (VY != 0.0F) && (VX == 0.0F);
// Pos d'inici de frame (abans de snap i integració): si en acabar el
// frame coincideix amb la posició final, el jugador no s'ha mogut
// realment (ni per input, ni per snap, ni ha superat la col·lisió) i cal
// forçar l'animació `stand` corresponent al facing — així s'atura el
// caminat quan xoca contra un mur o un sarcòfag.
const float FRAME_START_X = getPosX();
const float FRAME_START_Y = getPosY();
@@ -288,20 +304,19 @@ void Player::update(float delta_time) {
}
// Animació del frame + moviment col·lisió-aware. `tryMove` aplica els
// deltes per eix i reverteix l'eix que col·lisiona, així el jugador
// llisca contra sarcòfags i murs en compte de quedar-se bloquejat en
// sec. La caixa que s'envia al callback és `collisionBox()` (override
// a Player retornant `getBaseBox()`), no el sprite sencer.
// deltes per eix i reverteix l'eix que col·lisiona; així el jugador
// llisca contra sarcòfags, murs i altres jugadors en compte de quedar
// bloquejat en sec. La callback tracta els altres jugadors com a
// sòlids per evitar overlaps (el resolver del GameScene deixa les
// velocitats "segures", i aquesta comprovació és la xarxa de
// seguretat davant de casos d'ordre d'aplicació).
animate(delta_time);
tryMove(vx * delta_time, vy * delta_time, [this](const SDL_FRect& box) { return map_.collides(box); });
tryMove(VX * delta_time, VY * delta_time, [this](const SDL_FRect& box) {
return collidesWithWorld(box);
});
clampToPlayfield();
// Animació basada en moviment real: si la col·lisió (o l'absència
// d'input) ha deixat el jugador al mateix lloc que al principi del
// frame, queda en `stand` del facing actual. Durant un gir de 180°
// forcem també `stand` encara que el jugador s'estiga movent — la
// pose intermèdia és sempre estàtica per disseny.
const bool MOVED = (getPosX() != FRAME_START_X) || (getPosY() != FRAME_START_Y);
const bool ANIM_AS_MOVING = MOVED && (turning_remaining_ms_ <= 0.0F);
setCurrentAnimation(animationName(ANIM_AS_MOVING));