feat(player): resol el solapament en aterrar damunt d'un altre jugador
This commit is contained in:
1 parent
a64f33fe43
commit
c4caa9021e
2 files changed
+82
-2
No files matched your search
@@ -502,12 +502,81 @@ auto Player::integrateJump(float delta_time) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::resolveLandingOverlap() {
|
||||||
|
if (all_players_ == nullptr) { return; }
|
||||||
|
for (Player* other : *all_players_) {
|
||||||
|
if (other == nullptr || other == this) { continue; }
|
||||||
|
const SDL_FRect MY_HIT = getHitBox();
|
||||||
|
const SDL_FRect OT_HIT = other->getHitBox();
|
||||||
|
SDL_FRect inter;
|
||||||
|
if (!SDL_GetRectIntersectionFloat(&MY_HIT, &OT_HIT, &inter)) { continue; }
|
||||||
|
|
||||||
|
// Eix de separació: el que té els centres més separats. És
|
||||||
|
// l'axis-aligned MTV "natural" — equivalent a empentar l'altre
|
||||||
|
// cap a fora seguint la direcció centre→centre, projectada al
|
||||||
|
// costat dominant. Coincideix amb la intuïció: si el saltador
|
||||||
|
// li cau una mica per sobre i a la dreta, l'altre va cap a la
|
||||||
|
// dreta-baix; si li cau directament al damunt (dx≈0), l'altre
|
||||||
|
// s'aparta verticalment.
|
||||||
|
const float MCX = MY_HIT.x + (MY_HIT.w * 0.5F);
|
||||||
|
const float MCY = MY_HIT.y + (MY_HIT.h * 0.5F);
|
||||||
|
const float OCX = OT_HIT.x + (OT_HIT.w * 0.5F);
|
||||||
|
const float OCY = OT_HIT.y + (OT_HIT.h * 0.5F);
|
||||||
|
const float DX = OCX - MCX;
|
||||||
|
const float DY = OCY - MCY;
|
||||||
|
float push_x = 0.0F;
|
||||||
|
float push_y = 0.0F;
|
||||||
|
if (std::fabs(DX) >= std::fabs(DY)) {
|
||||||
|
push_x = (DX >= 0.0F ? 1.0F : -1.0F) * inter.w;
|
||||||
|
} else {
|
||||||
|
push_y = (DY >= 0.0F ? 1.0F : -1.0F) * inter.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1r intent: empentar l'altre. Si la nova posició no col·lisiona
|
||||||
|
// amb res (mur, sarc, tercer jugador), commit i marquem-lo com
|
||||||
|
// a empentat per coherència de l'animació de l'altre frame.
|
||||||
|
const float OSX = other->getPosX();
|
||||||
|
const float OSY = other->getPosY();
|
||||||
|
other->setPosX(OSX + push_x);
|
||||||
|
other->setPosY(OSY + push_y);
|
||||||
|
if (!other->collidesWithWorld(other->getBaseBox())) {
|
||||||
|
other->setBeingPushed(true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// L'altre xocaria amb un mur: revertim i provem d'apartar el
|
||||||
|
// saltador cap al sentit oposat. Així respectem la regla "mai
|
||||||
|
// ficar ningú dins d'un mur": si no hi ha camí per a l'altre,
|
||||||
|
// qui es desplaça és el qui ha triat saltar.
|
||||||
|
other->setPosX(OSX);
|
||||||
|
other->setPosY(OSY);
|
||||||
|
const float MSX = getPosX();
|
||||||
|
const float MSY = getPosY();
|
||||||
|
setPosX(MSX - push_x);
|
||||||
|
setPosY(MSY - push_y);
|
||||||
|
if (!collidesWithWorld(getBaseBox())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap dels dos pot apartar-se sense xocar amb un mur. Cas
|
||||||
|
// degenerat (jugador encaixonat entre obstacles); el deixem com
|
||||||
|
// estava i acceptem el solapament residual fins al pròxim frame
|
||||||
|
// amb input.
|
||||||
|
setPosX(MSX);
|
||||||
|
setPosY(MSY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Player::applyMovement(float delta_time) {
|
void Player::applyMovement(float delta_time) {
|
||||||
// Integració del salt: si estem AIRBORNE, avança jump_z_/jump_vz_.
|
// Integració del salt: si estem AIRBORNE, avança jump_z_/jump_vz_.
|
||||||
// El facing i intended_v* es mantenen del moment del despegue (no es
|
// El facing i intended_v* es mantenen del moment del despegue (no es
|
||||||
// recalculen). Si toquem el sòl aquest frame, transicionem a
|
// recalculen). Si toquem el sòl aquest frame, transicionem a
|
||||||
// LANDING_STUN ja al final d'`integrateJump`.
|
// LANDING_STUN i resolem el possible solapament amb un altre
|
||||||
integrateJump(delta_time);
|
// jugador que estigués sota — apartem-lo (o, si no es pot, ens
|
||||||
|
// apartem nosaltres).
|
||||||
|
if (integrateJump(delta_time)) {
|
||||||
|
resolveLandingOverlap();
|
||||||
|
}
|
||||||
|
|
||||||
// En LANDING_STUN no movem el jugador horitzontalment (la velocitat
|
// En LANDING_STUN no movem el jugador horitzontalment (la velocitat
|
||||||
// ja s'ha posat a 0 al `tick`). Encara així animem (per estar al
|
// ja s'ha posat a 0 al `tick`). Encara així animem (per estar al
|
||||||
|
|||||||
@@ -401,6 +401,17 @@ class Player : public AnimatedSprite {
|
|||||||
// `applyMovement` puga disparar la resolució del solapament PvP).
|
// `applyMovement` puga disparar la resolució del solapament PvP).
|
||||||
auto integrateJump(float delta_time) -> bool;
|
auto integrateJump(float delta_time) -> bool;
|
||||||
|
|
||||||
|
// En aterrar, si la hit-box del jugador solapa amb la d'un altre,
|
||||||
|
// separa els dos sense ficar-los dins d'un mur. Algoritme:
|
||||||
|
// 1. Calcula la direcció centre→centre i la magnitud de
|
||||||
|
// solapament (MTV axis-aligned).
|
||||||
|
// 2. Empenta l'altre cap a fora; si no cap (mur al darrere),
|
||||||
|
// reverteix i empenta el saltador cap al sentit oposat.
|
||||||
|
// 3. Si tampoc cap, es deixa l'estat com està (overlap residual);
|
||||||
|
// a la pràctica aquest cas és molt rar i el usuari ja ha
|
||||||
|
// acceptat que no és crític.
|
||||||
|
void resolveLandingOverlap();
|
||||||
|
|
||||||
// Estat del gir en curs. Mentre `turning_remaining_ms_ > 0`, el facing/
|
// Estat del gir en curs. Mentre `turning_remaining_ms_ > 0`, el facing/
|
||||||
// flip es manté forçat a `intermediate_cardinal_` i l'animació es força
|
// flip es manté forçat a `intermediate_cardinal_` i l'animació es força
|
||||||
// a `stand` encara que el moviment continuï. En esgotar-se el timer,
|
// a `stand` encara que el moviment continuï. En esgotar-se el timer,
|
||||||
|
|||||||
Reference in new issue
Block a user