feat(collisions): filtre per altura — saltar esquiva momies, roques i altres jugadors
This commit is contained in:
1 parent
7b09a076b2
commit
cd50626b88
11 files changed
+88
-1
No files matched your search
@@ -113,6 +113,17 @@ class Entity {
|
||||
// Entitats sense animació retornen `nullopt` → frame 0 per defecte.
|
||||
[[nodiscard]] auto getAnimation() const -> const std::optional<AnimationComponent>& { return anim_; }
|
||||
|
||||
// Alçada del cos de l'entitat en píxels. La consulten els filtres
|
||||
// de col·lisió per altura: si un jugador en l'aire passa per damunt
|
||||
// d'aquest valor (jump_z_ > height), no col·lisiona amb l'entitat —
|
||||
// és la mecànica del salt per esquivar momies, roques, etc. Valor
|
||||
// 0 = sense altura (no es pot esquivar saltant; per a entitats amb
|
||||
// collides=false el camp és irrellevant). Per a obstacles que
|
||||
// representen "murs" (sarcs, portes), el YAML els pot donar un
|
||||
// valor molt alt (p.ex. 999) per fer-los infranquejables.
|
||||
[[nodiscard]] auto getHeight() const -> int { return height_; }
|
||||
void setHeight(int h) { height_ = h; }
|
||||
|
||||
// Caixa de col·lisió: per defecte és el `pixel_rect_` sencer (entitats
|
||||
// AABB-aligned). Si s'ha configurat un `CollisionBox`, retorna el
|
||||
// sub-rect dins del sprite — vida útil: validar col·lisions amb el
|
||||
@@ -150,6 +161,7 @@ class Entity {
|
||||
std::shared_ptr<Surface> surface_{nullptr};
|
||||
bool collides_{false};
|
||||
bool interactable_{false};
|
||||
int height_{0};
|
||||
SDL_FlipMode flip_{SDL_FLIP_NONE};
|
||||
EventBus* bus_{nullptr};
|
||||
};
|
||||
@@ -63,8 +63,15 @@ void EntityManager::checkPlayerCollisions(const std::vector<Player*>& players) {
|
||||
for (const auto& entity : entities_) {
|
||||
if (!entity->collides()) { continue; }
|
||||
const auto BOX = entity->getCollisionRect();
|
||||
const int E_HEIGHT = entity->getHeight();
|
||||
for (auto* p : players) {
|
||||
if (p == nullptr) { continue; }
|
||||
// Filtre per altura: si el jugador està en l'aire i està per
|
||||
// damunt de l'alçada del cos de l'entitat, no hi ha contacte.
|
||||
// És el mecanisme del salt per esquivar momies, roques, etc.
|
||||
// Quan height és 0 (entitat sense altura declarada), el filtre
|
||||
// no es dispara mai i la col·lisió funciona com abans.
|
||||
if (p->isAirborne() && E_HEIGHT > 0 && p->getJumpZ() > static_cast<float>(E_HEIGHT)) { continue; }
|
||||
// Contacte actor-actor amb hit-box petita: la caixa base
|
||||
// 16×16 servia per moure (xoc amb murs), però per a l'event
|
||||
// "toca una entitat" volem precisió — que la momia mate quan
|
||||
@@ -109,6 +116,18 @@ namespace {
|
||||
return info;
|
||||
}
|
||||
|
||||
// Llig `collision.height` (alçada del cos en píxels) de la plantilla.
|
||||
// 0 si no està declarada — ho usen els filtres de col·lisió per altura
|
||||
// del salt: un jugador per damunt d'aquest valor no col·lisiona amb
|
||||
// l'entitat. Per a obstacles "tipus mur" (sarc, porta) el YAML els
|
||||
// dóna un valor molt alt per fer-los infranquejables.
|
||||
auto readHeight(const fkyaml::node& tmpl) -> int {
|
||||
if (!tmpl.contains("collision")) { return 0; }
|
||||
const auto& c = tmpl["collision"];
|
||||
if (!c.contains("height")) { return 0; }
|
||||
return c["height"].get_value<int>();
|
||||
}
|
||||
|
||||
// Bloc `collision: { size: [w, h], base_size: [bw, bh]?, offset: [x, y]? }`
|
||||
// de la plantilla d'entitat.
|
||||
// - Sense `base_size` ni `offset`: la caja queda alineada a la vora
|
||||
@@ -212,6 +231,7 @@ namespace {
|
||||
if (const auto BOX = readCollisionBox(tmpl, sprite_out.width, sprite_out.height); BOX.has_value()) {
|
||||
entity->setCollisionBox(*BOX);
|
||||
}
|
||||
entity->setHeight(readHeight(tmpl));
|
||||
anchor_out = readAnchor(tmpl);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -300,9 +300,18 @@ auto Player::collidesWithWorld(const SDL_FRect& box) const -> bool {
|
||||
// amb la hit-box actual de l'altre jugador. Així la caixa de moviment
|
||||
// (16×16) segueix topant amb murs/sarcs però els jugadors poden
|
||||
// acostar-se fins al contacte real.
|
||||
//
|
||||
// Filtre per altura: si aquest jugador (this) està en l'aire i passa
|
||||
// per damunt del cos de l'altre, no hi ha col·lisió — saltar permet
|
||||
// sobrevolar l'altre. `body_height` és estàtic (compartit entre
|
||||
// jugadors), però per al filtre val la pena consultar-lo per actor
|
||||
// perquè en el futur cada character podria tenir-ne un de propi.
|
||||
const SDL_FRect PROPOSED_HIT = hitBoxFromBase(box);
|
||||
return std::ranges::any_of(*all_players_, [this, &PROPOSED_HIT](const Player* p) {
|
||||
const float Z = jump_z_;
|
||||
const bool AIRBORNE = (state_ == State::AIRBORNE);
|
||||
return std::ranges::any_of(*all_players_, [this, &PROPOSED_HIT, AIRBORNE, Z](const Player* p) {
|
||||
if (p == nullptr || p == this) { return false; }
|
||||
if (AIRBORNE && body_height > 0 && Z > static_cast<float>(body_height)) { return false; }
|
||||
const auto OTHER = p->getHitBox();
|
||||
return SDL_HasRectIntersectionFloat(&PROPOSED_HIT, &OTHER);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user