Autor Wątek: Platformówka - reakcja kolizje 2D  (Przeczytany 1140 razy)

Offline steckel

  • Użytkownik

# Listopad 17, 2010, 18:32:10
Witam!
Mam problem z obsługa kolizji w platformówce 2D.

Na początku ustawiam wartości:
void Character::Reset(double dt){
    canMove_ = Direction::All; //wszystkie kierunki
    maxMoveX_ = vX_*dt; //maksymalne przemieszczenie w osi X
    maxMoveY_ = vY_*dt; //maksymalne przemieszczenie w osi Y
}
Następnie sprawdzam kolizje z poziomem:
void EntityManager::CheckCollision(CharacterPtr entity,double dt) {
    double tw = Engine::Get().GetRenderer()->GetTileWidth();  //wysokosc kafla
    double th = Engine::Get().GetRenderer()->GetTileHeight(); //szerokosc kafla
    size_t x1 = std::floor(entity->X() / tw) - 1;                //lewa granica sprawdzania kolizji
    size_t x2 = std::ceil((entity->X() + entity->W()) / tw) + 1; //prawa --||--
    size_t y1 = std::floor(entity->Y() / th) - 1;                //dolna --||--
    size_t y2 = std::ceil((entity->Y() + entity->H()) / th) + 1; //górna --||--
    for (int x = x1; x < x2; x++) {
        for (int y = y1; y < y2; y++) {
            bool check = false; //czy sprawdzać kolizje
            if (x < 0 || x >= width_ || y < 0 || y >= height_) {  //kolizja z granicami planszy
                check = true;
            }
            else if(ground_[x][y]){   //kolizja z 'gruntem'
                check = true;
            }
            if(check){
                Entity temp(x*tw,y*th,tw,th,"null");  //utworzenie 'klocka' o odpowiedim położeniu i rozmiarze
                entity->CheckCollision(temp,dt);  //sprawdzanie kolizji postaci z nowym klockiem
            }
        }
    }
}
Teraz sprawdzam kolizje z nowym klockiem:
bool Character::CheckCollision(Entity& entity, double dt) {
    int relation = CheckRelation(entity);
    if (vY_ > 0 && (relation & Direction::Up)) {  //jezeli postac przemiesza się w górę i klocek jest nad postacią to sprawdzam kolizję
        CheckCollisionUp(entity);
    }
    if (vY_ < 0 && (relation & Direction::Down)) {
        CheckCollisionDown(entity);
    }
    if (vX_ > 0 && (relation & Direction::Right)) {
        CheckCollisionRight(entity);
    }
    if (vX_ < 0 && (relation & Direction::Left)) {
        CheckCollisionLeft(entity);
    }
}
Sprawdzenie położenia klocka wobec postaci:
int Character::CheckRelation(Entity& entity) {
    int relation = 0;
    if (entity.Y() >= y_ + h_) {
        relation |= Direction::Up;
    }
    if (entity.Y() + entity.H() <= y_) {
        relation |= Direction::Down;
    }
    if (entity.X() >= x_ + w_) {
        relation |= Direction::Right;
    }
    if (entity.X() + entity.W() <= x_) {
        relation |= Direction::Left;
    }
    return relation;
}
Sprawdzenie kolizji z klockiem nad postacią:
void Character::CheckCollisionUp(Entity& entity) {
    double minH = Engine::Get().GetRenderer()->GetMinHeight();  //minimaljna wysokosc (jednostka)
    MyRectangle rec(GetX(), GetY() + GetMaxMoveY() + 1, GetW(), GetH());  //tworzenie prostokąta z potencjalnym przemieszczeniem o prędkość oraz o 1 jednostkę. //GetX() to liczba całkowita x_ / minW itd. Czyli tworzy prostokąt, gdzie położenie i rozmiar jest wyrażony w jednostkach, do łatwiejszego liczenia.
    if (Collision(rec, entity.GetRectangle())) {  //sprawdzenie prostokąta z klocka z prostokątem z przemieszczonej postaci
        canMove_ & ~Direction::Up;  //postac nie może iść w górę
        maxMoveY_ = std::min(maxMoveY_, (entity.GetY() - GetY() - GetH() - 1) * minH);  //maksymalne przemieszczenie postaci wyrażone w normalej skali (nie w tej z jednostkami)
        vY_ = 0;
    }
}

void Character::CheckCollisionDown(Entity& entity) {
    double minH = Engine::Get().GetRenderer()->GetMinHeight();
    MyRectangle rec(GetX(), GetY() + GetMaxMoveY() - 1, GetW(), GetH());
    if (Collision(rec, entity.GetRectangle())) {
        canMove_ & ~Direction::Down;
        maxMoveY_ = std::max(maxMoveY_, (entity.GetY() + entity.GetH() - GetY() + 1) * minH);
        vY_ = 0;
    }
 
}

void Character::CheckCollisionRight(Entity& entity) {
    double minW = Engine::Get().GetRenderer()->GetMinWidth();
    MyRectangle rec(GetX() + GetMaxMoveX() + 1, GetY(), GetW(), GetH());
    if (Collision(rec, entity.GetRectangle())) {
        canMove_ & ~Direction::Right;
        maxMoveX_ = std::min(maxMoveX_, (entity.GetX() - GetX() - GetW() + 1) * minW);
        vY_ = 0;
    }
}

void Character::CheckCollisionLeft(Entity& entity) {
    double minW = Engine::Get().GetRenderer()->GetMinWidth();
    MyRectangle rec(GetX() + GetMaxMoveX() - 1, GetY(), GetW(), GetH());
    if (Collision(rec, entity.GetRectangle())) {
        canMove_ & ~Direction::Left;
        maxMoveX_ = std::max(maxMoveX_, (entity.GetX() + entity.GetW() - GetX() - 1) * minW);
        vX_ = 0;
    }
}
Kolizja prostokątów:
bool Collision(MyRectangle a, MyRectangle b){
    if(a.x1_>b.x2_){
        return false;
    }
    if(a.x2_<b.x1_){
        return false;
    }
    if(a.y1_>b.y2_){
        return false;
    }
    if(a.y2_<b.y1_){
        return false;
    }
    return true;
}
Na końcu jest aktualizacja ruchu postaci:
void Character::UpdateMove() {
    x_ += maxMoveX_;
    y_ += maxMoveY_;
}

void Character::UpdateAccelerate(double dt) {
    double tw = Engine::Get().GetRenderer()->GetTileWidth();
    double th = Engine::Get().GetRenderer()->GetTileHeight();
    if (accelerate_ & Direction::Left & canMove_) {  //przyspiesz w lewo jeżeli ma przyspieszyć i może
        vX_ -= data_->accX_*dt;
        vX_ = std::max(vX_, -data_->maxVX_);  //upewnienie się, że prędkość nie przekracza maksymalnej
    }
    if (accelerate_ & Direction::Right & canMove_) {
        vX_ += data_->accX_*dt;
        vX_ = std::min(vX_, data_->maxVX_);
    }
    if (accelerate_ & Direction::Down & canMove_) {
        vY_ -= data_->accY_*dt;
        vY_ = std::max(vY_, -data_->maxVY_);
    }
    if (accelerate_ & Direction::Up & canMove_) {
        vY_ += data_->accY_*dt;
        vY_ = std::min(vY_, data_->maxVY_);
    }
}

void Character::UpdateAirForce(double dt) {
    double airPower = data_->airPower_*dt;  //opory powietrza
    if (vX_ > 0) {
        if (vX_ < airPower) {
            vX_ = 0;
        }
        else {
            vX_ -= airPower;
        }
    }
    else {
        if (vX_> -airPower) {
            vX_ = 0;
        }
        else {
            vX_ += airPower;
        }
    }
}

void Character::UpdateGravity(double dt) {
    if (canMove_ & Direction::Down) {  //grawitacja, jeżeli może poruszyć się w dół
        vY_ -= data_->grav_*dt;
    }
}
Problem polega na tym, że postać zachowuje się jakby ciągle spadała, mimo że drga na podłożu. Gdy nadejdzie na dziurę to nie spada. Męczę się z tym już 3 dni, ale mam nadzieję, że ktoś to ogarnie.
Tu jest link do całego projektu: http://www.speedyshare.com/files/25239959/Kirby_Adventures.rar

Offline Mr. Spam

  • Miłośnik przetworów mięsnych