void PhysicsSystem::update(GameComponents& components, Entities const& entities, float dt) { std::vector<Contact> contacts; std::vector<StaticContact> static_contacts; std::vector<Collider*> colliders(entities.size()); int collider_index = 0; float top = 600.0f; float bottom = 40.0f; // find contacts for (Entities::const_iterator it0 = entities.begin(); it0 != entities.end(); ++it0) { Entity entity0 = *it0; Collider* collider0 = &components.get<ColliderComponent>(entity0); collider0->touching.clear(); colliders[collider_index++] = collider0; static_contacts.push_back({ top - collider0->position(1) - collider0->radius, Mth::make_cvector(0.0f, 1.0f), collider0, entity0, 0.0f }); static_contacts.push_back({ collider0->position(1) - bottom - collider0->radius, Mth::make_cvector(0.0f, -1.0f), collider0, entity0, 0.0f }); Entities::const_iterator it1 = it0; ++it1; for (; it1 != entities.end(); ++it1) { Entity entity1 = *it1; Collider* collider1 = &components.get<ColliderComponent>(entity1); if ((collider0->group == CG_PB && collider1->group == CG_PB) || (collider0->group == CG_EB && collider1->group == CG_EB) || (collider0->group == CG_PB && collider1->group == CG_EB) || (collider0->group == CG_EB && collider1->group == CG_PB) || (collider0->group == CG_P && collider1->group == CG_PB) || (collider0->group == CG_PB && collider1->group == CG_P) || (collider0->group == CG_E && collider1->group == CG_EB) || (collider0->group == CG_EB && collider1->group == CG_E)) { continue; } Mth::CVector<float, 2> d = collider1->position - collider0->position; Contact c { Mth::length(d) - collider0->radius - collider1->radius, Mth::normal(d), collider0, collider1, entity0, entity1, 0.0f }; contacts.push_back(c); } } // solve contacts int iterations = 4; for (int i = 0; i < iterations; ++i) { for (auto& contact : static_contacts) { float relative_velocity = Mth::dot(-contact.collider->velocity, contact.normal); float remove_impuls = contact.distance / dt + relative_velocity; float new_impuls = std::min(contact.impuls + remove_impuls, 0.0f); float impuls_change = new_impuls - contact.impuls; contact.impuls = new_impuls; contact.collider->velocity += contact.normal * impuls_change; } for (auto& contact : contacts) { float relative_velocity = Mth::dot(contact.collider1->velocity - contact.collider0->velocity, contact.normal); float remove = contact.distance / dt + relative_velocity; float remove_impuls = remove / 2.0f; float new_impuls = std::min(contact.impuls + remove_impuls, 0.0f); float impuls_change = new_impuls - contact.impuls; contact.impuls = new_impuls; contact.collider0->velocity += contact.normal * impuls_change; contact.collider1->velocity -= contact.normal * impuls_change; if (i == iterations-1) { if (new_impuls < 0.0f) { contact.collider0->touching.push_back(contact.entity1); contact.collider1->touching.push_back(contact.entity0); } } } } // evaluate simulation for (Collider* collider : colliders) { collider->position += collider->velocity * dt; collider->velocity = Mth::make_cvector(0.0f, 0.0f); } }
// --- void QGAMES::Scene::drawOn (QGAMES::Screen* s, const QGAMES::Position& p) { assert (_activeMap); if (!_entitiesPerLayer.empty ()) { // This means, there are entities that has to be drawn in an specific layer. // So it starts to iterate over the layers, // drawing them, and the entities (or characters) in each // and also keeping control which one are drawn in each layer // to avoid they to be drawing on top of all layers. // At this point on the code, we don't still know which entities are in each layer std::vector <int> ePainted; // The entities that are being painted in its specific layer... QGAMES::Layers l = _activeMap -> layers (); // Let's go to iterate per layer... for (QGAMES::Layers::const_iterator i = l.begin (); i != l.end (); i++) { (*i) -> drawOn (s, p); // First of all the layer is drawn.. QGAMES::EntitiesPerLayer::const_iterator j; // Is there something in this specific layer to be drawn?. if ((j = _entitiesPerLayer.find ((*i) -> name ())) != _entitiesPerLayer.end ()) { // The list of the entities and characteres to be drawn... std::vector <int> es = (*j).second; // ...then the entities are drawn (if they have been defined in the layer) for (Entities::const_iterator k1 = _entities.begin (); k1 != _entities.end (); k1++) { bool eFound = false; for (std::vector <int>::const_iterator l = es.begin (); l != es.end () && !eFound; l++) { if ((*k1).second ->id () == (*l)) { eFound = true; ePainted.push_back ((*l)); } } if (eFound) (*k1).second -> drawOn (s); // The entities are drawn where they are... } // ...and the characters too (if they have been defined in the layer as well) for (Entities::const_iterator k2 = _characteres.begin (); k2 != _characteres.end (); k2++) { bool eFound = false; for (std::vector <int>::const_iterator l = es.begin (); l != es.end () && !eFound; l++) { if ((*k2).second ->id () == (*l)) { eFound = true; ePainted.push_back ((*l)); } } if (eFound) (*k2).second -> drawOn (s); // The entities are drawn where they are... } } } // Entities and characteres are put into the same structure // ...and ordered to be painted... Entities eTP = _entities; eTP.insert (_characteres.begin (), _characteres.end ()); // Add the characters... eTP = orderEntitiesToDraw (eTP); // Order the set... for (Entities::const_iterator k3 = eTP.begin (); k3 != eTP.end (); k3++) { bool eFound = false; for (std::vector <int>::const_iterator l = ePainted.begin (); l != ePainted.end () && !eFound; l++) if ((*k3).second ->id () == (*l)) eFound = true; if (!eFound) // If the entity was not drawn before... (*k3).second -> drawOn (s); // Every entity is drawn where it is... } } else { // This means that there are no specific entities per layer... _activeMap -> drawOn (s, p); // It is possible to alter where the map is drawn... Entities eTP = _entities; eTP.insert (_characteres.begin (), _characteres.end ()); // Add the characters... eTP = orderEntitiesToDraw (eTP); // Order everything... for (Entities::const_iterator i = eTP.begin (); i != eTP.end (); i++) (*i).second -> drawOn (s); // ...but the entities are drawn where they are... } }