bool EnemyBullet::CollisionTest(shared_ptr<GameObject> o) { if (o->GetType() != GameObjectType("Asteroid") && o->GetType() != GameObjectType("SmallAsteroid") && o->GetType() != GameObjectType("Spaceship")) return false; if (mBoundingShape.get() == NULL) return false; if (o->GetBoundingShape().get() == NULL) return false; return mBoundingShape->CollisionTest(o->GetBoundingShape()); }
bool Asteroid::CollisionTest(shared_ptr<GameObject> o) { if (o->GetType() == GameObjectType("PowerUp") || o->GetType() == GameObjectType("SmallAsteroid")) return false; if (GetType() == o->GetType()) return false; if (mBoundingShape.get() == NULL) return false; if (o->GetBoundingShape().get() == NULL) return false; return mBoundingShape->CollisionTest(o->GetBoundingShape()); }
bool Life::CollisionTest(shared_ptr<GameObject> o) { if (o->GetType() != GameObjectType("Spaceship")) return false; if (GetType() == o->GetType()) return false; if (mBoundingShape.get() == NULL) return false; if (o->GetBoundingShape().get() == NULL) return false; return mBoundingShape->CollisionTest(o->GetBoundingShape()); }
bool Drone::CollisionTest(SmartPtr<GameObject> o) { // if (o->GetType() != GameObjectType("Bullet")) return false; if (o->GetType() == GameObjectType("Drone")) return false; if (GetType() == o->GetType()) return false; if (mBoundingShape.GetPtr() == NULL) return false; if (o->GetBoundingShape().GetPtr() == NULL) return false; return mBoundingShape->CollisionTest(o->GetBoundingShape()); }
void Ore::OnCollision(const GameObjectList& objects) { SmartPtr<GameObject> object; GameObjectType const* type = NULL; GameObjectList::const_iterator it = objects.begin(); GameObjectList::const_iterator end = objects.end(); for (; it != end; ++it) { object = *it; type = &(object->GetType()); // Compare Object type if((*type) == GameObjectType("Spaceship")) { mWorld->RemoveObject(this); cout << "ORE COLIDES WITH SPACESHIP\n"; } } }
void Drone::OnCollision(const GameObjectList& objects) { SmartPtr<GameObject> object; GameObjectType const* type = NULL; GameObjectList::const_iterator it = objects.begin(); GameObjectList::const_iterator end = objects.end(); for (; it != end; ++it) { // const GameObjectType * type = &((*it)->GetType() ); object = *it; type = &(object->GetType()); // const char * type_name = object->GetType().GetTypeName(); // cout << type_name << ";" << endl; // Compare Object type if((*type) == GameObjectType("Bullet")) { // TODO add damage related stuff to GameObject which should allow you to bypass this whole awkward pointer stuff here... Bullet* b = (Bullet*) object.GetPtr(); // cout << "colision with bullet" << endl; // Bullet* b = dynamic_cast<Bullet*>(object.GetPtr()); int damage = b->GetDamage(); int health = mHealth; int newHealth = mHealth - damage; DealDamage(b->GetDamage()); //cout << "HEALTH " << mHealth << endl; // Check if alive if(mHealth <= 0) mWorld->RemoveObject(this); } } }
void Asteroid::OnCollision(const GameObjectList& objects) { SmartPtr<GameObject> object; GameObjectType const* type = NULL; GameObjectList::const_iterator it = objects.begin(); GameObjectList::const_iterator end = objects.end(); for (; it != end; ++it) { // const GameObjectType * type = &((*it)->GetType() ); object = *it; type = &(object->GetType()); // const char * type_name = object->GetType().GetTypeName(); // cout << type_name << ";" << endl; // Compare Object type if((*type) == GameObjectType("Bullet")) { mWorld->RemoveObject(this); // @todo mWorld-> see if you can do the spliting of asteroids from here rather than the asteroids.cpp } } }
namespace Engine{ // GameObject type for static const GameObjectType OBJ_INVALID = GameObjectType(0); // GameObject type for filtering by "any object" (i.e. no filtering is applied) static const GameObjectType OBJ_ANY = GameObjectType(1); // First valid GameObjectType index (to reserve lower indices for special meanings) static const GameObjectType OBJ_OFFSET = GameObjectType(16); class WorldManager : public Singleton<WorldManager> { public: // Initializes the game world void Initialize(); // Destroys the game world void Terminate(); // Updates all game objects in the game world void Update(const GameTime& gameTime); // Draws all game objects in the game world void Draw(const GameTime& gameTime); //////////////////////////////////////////////////////////////// // Game object creation and removal // //////////////////////////////////////////////////////////////// // Adds a game object to the world and returns the handle GameObjectGUID AddGameObject(GameObject* gameObject); // Adds a group of game objects to the world void AddGameObjects(const GameObjectCollection& gameObjects); // Removes a game object from the world void RemoveGameObject(GameObject* gameObject); // Removes a group of game objects from the world void RemoveGameObjects(const GameObjectCollection& gameObjects); //////////////////////////////////////////////////////////////// // Game object retrieval // //////////////////////////////////////////////////////////////// // Retrieves all game objects size_t RetrieveAll(GameObjectCollection& out_GameObjectCollection) const; // Retrieves the game object that matches the specified GUID size_t RetrieveByGUID(GameObjectGUID guid, GameObjectCollection& out_GameObjectCollection) const; // Retrieves all game objects that matchs the specified type size_t RetrieveByType(GameObjectType type, GameObjectCollection& out_GameObjectCollection) const; ///////////////////////////////////////////////////////// Legacy // Retrieves the nearest game object to the specified position considering x and y coordinates (returns whether a game object was found) bool RetrieveNearestGameObject(const f2& position, GameObject*& out_GameObject, GameObjectType typeFilter = GameObjectType(Engine::OBJ_ANY)); // Retrieves the nearest game object to the specified position considering x, y and z coordinates (returns whether a game object was found) bool RetrieveNearestGameObject(const f3& position, GameObject*& out_GameObject, GameObjectType typeFilter = GameObjectType(Engine::OBJ_ANY)); // Retrieves the k-nearest game object to the specified position considering x and y coordinates (returns the number of game objects found) size_t RetrieveKNearestGameObjects(const f2& position, size_t k, std::vector<GameObject*>& out_GameObjects, GameObjectType typeFilter = GameObjectType(Engine::OBJ_ANY)); // Retrieves the k-nearest game object to the specified position considering x, y and z coordinates (returns the number of game objects found) size_t RetrieveKNearestGameObjects(const f3& position, size_t k, std::vector<GameObject*>& out_GameObjects, GameObjectType typeFilter = GameObjectType(Engine::OBJ_ANY)); // Retrieves all game objects closer than the specified distance to a point considering x and y coordinates (returns the number of game objects found) size_t RetrieveGameObjectsNearPosition(const f2& position, float maxDistance, std::vector<GameObject*>& out_GameObjects, GameObjectType typeFilter = GameObjectType(Engine::OBJ_ANY)); // Retrieves all game objects closer than the specified distance to a point considering x, y and z coordinates(returns the number of game objects found) size_t RetrieveGameObjectsNearPosition(const f3& position, float maxDistance, std::vector<GameObject*>& out_GameObjects, GameObjectType typeFilter = GameObjectType(Engine::OBJ_ANY)); //////////////////////////////////////////////////////////////// // Collision-aware movement // //////////////////////////////////////////////////////////////// // Possible responses on collisions enum class CollisionResponse { IGNORE, // Ignore collisions STOP, // Stop moving on collision SLIDE, // Remove the motion component parallel to the collision normal (removes speed) REDIRECT, // Redirects the motion to move perpendicular to the collision normal (does not remove speed, except for perpendicular collisions) REFLECT // Reflects the object of the colliding surface (does not remove speed) }; private: // Maximum number of iterations in movement solver static const unsigned char s_MaxMovementIterations; // Minimum separation distance to keep between objects // NOTE: when a collision occurs, the moving object is pushed back out // of the colliding object by this distance. Placing objects next to // each other with a separation of 0 units is not compatible with the // ray-casting approach used for collision detection as an inter-distance // of 0 units is registered as a collision. static const float s_ObjectSeparationDistance; ///////////////////////////////////////////////////////////// 2D // Pushes an object out of a colliding object along the motion vector template<typename valuetype> inline void PushOut2D(GameObject& gameObject, const vector2D<valuetype>& motion) { gameObject.t() -= (motion * s_ObjectSeparationDistance) / motion.length(); } // Finds the nearest collision along a specified motion vector template<typename valuetype> inline bool FindFirstCollision2D(GameObject& gameObject, const vector2D<valuetype>& motion, const GameObjectCollection& other, vector2D<valuetype>& out_Position, vector2D<valuetype>& out_Normal, valuetype& out_Progression) { // Initialize to full motion (1.0 progression) out_Position = gameObject.t2D() + motion; out_Progression = 1.0f; vector2D<valuetype> currentPosition; vector2D<valuetype> currentNormal; valuetype currentProgression = 1.0f; bool collision = false; // Ray cast all other objects to find the nearest collision CollisionManager c = CollisionManager::GetInstance(); for (GameObject* g : other.objects()) { if (gameObject.guid() == g->guid()) { continue; } if (c.IsIntersecting(gameObject.aabb2D_world(), g->aabb2D_world(), motion, currentPosition, currentNormal, currentProgression)) { if (currentProgression < out_Progression) { out_Progression = currentProgression; out_Position = currentPosition; out_Normal = currentNormal; collision = true; if (currentProgression == 0.0f) { return true; } } } } return collision; } // Move a game object along the specified motion vector, ignoring collisions template<typename valuetype> inline void MoveIgnore2D(GameObject& gameObject, const vector2D<valuetype>& motion) { gameObject.t() += motion; } // Move a game object along the specified motion vector, stopping at the first collision template<typename valuetype> inline bool MoveStop2D(GameObject& gameObject, const vector2D<valuetype>& motion, const GameObjectCollection& other, bool updateVelocity) { // Broadphase filtering based on AABB sweep CollisionManager& c(CollisionManager::GetInstance()); aabb2D<valuetype> sweep(gameObject.aabb2D_world().sweep(motion)); GameObjectCollection otherBP(other); otherBP.FilterByOverlap(sweep); // Output variables for collision finder vector2D<valuetype> position; vector2D<valuetype> normal; valuetype progression; // Move the object and push it out of colliding objects bool collision = FindFirstCollision2D(gameObject, motion, otherBP, position, normal, progression); if (progression == 0.0f) { gameObject.velocity(f3(0.0)); return true; } gameObject.t() += motion * progression; if (collision) { PushOut2D(gameObject, motion); if (updateVelocity) { gameObject.velocity(f3(0.0)); } } return collision; } // Move a game object along the specified motion vector, sliding along colliding objects template<typename valuetype> inline bool MoveSlide2D(GameObject& gameObject, const vector2D<valuetype>& motion, const GameObjectCollection& other) { // Broadphase filtering based on AABB sweep CollisionManager& c(CollisionManager::GetInstance()); aabb2D<valuetype> sweep(gameObject.aabb2D_world().sweep(motion)); GameObjectCollection otherBP(other); otherBP.FilterByOverlap(sweep); // Output variables for collision finder vector2D<valuetype> position; vector2D<valuetype> normal; valuetype progression; // Make a copy of the motion vector that is modified throughout iterations vector2D<valuetype> motion_i(motion); unsigned char iteration = 0; bool collision = false; do { // Move the object and push it out of colliding objects bool collisionCurrent = FindFirstCollision2D(gameObject, motion_i, otherBP, position, normal, progression); if (progression == 0.0f) { return true; } gameObject.t() += motion_i * progression; collision |= collisionCurrent; if (!collisionCurrent) { return collision; } PushOut2D(gameObject, motion_i); // Calculate the new motion vector to have sliding behavior motion_i = motion_i * (1.0f - progression); motion_i = motion_i - motion_i.project(normal); iteration++; } while (iteration < s_MaxMovementIterations); return collision; } // Move a game object along the specified motion vector, redirecting motion along colliding objects template<typename valuetype> inline bool MoveRedirect2D(GameObject& gameObject, const vector2D<valuetype>& motion, const GameObjectCollection& other, bool updateVelocity) { // Output variables for collision finder vector2D<valuetype> position; vector2D<valuetype> normal; valuetype progression; // Make a copy of the motion vector that is modified throughout iterations vector2D<valuetype> motion_i(motion); unsigned char iteration = 0; bool collision = false; do { // Broadphase filtering based on AABB sweep CollisionManager& c(CollisionManager::GetInstance()); aabb2D<valuetype> sweep(gameObject.aabb2D_world().sweep(motion_i)); GameObjectCollection otherBP(other); otherBP.FilterByOverlap(sweep); // Move the object and push it out of colliding objects bool collisionCurrent = FindFirstCollision2D(gameObject, motion_i, otherBP, position, normal, progression); if (progression == 0.0f) { return true; } gameObject.t() += motion_i * progression; collision |= collisionCurrent; if (!collisionCurrent) { return collision; } PushOut2D(gameObject, motion); // Calculate the new motion vector to have redirecting behavior motion_i = motion_i * (1.0f - progression); vector2D<valuetype> direction = motion_i - motion_i.project(normal); motion_i = motion_i.align(direction); if (updateVelocity) { gameObject.velocity(gameObject.velocity2D().align(motion_i)); } iteration++; } while (iteration < s_MaxMovementIterations); return collision; } // Move a game object along the specified motion vector, reflecting of colliding objects template<typename valuetype> inline bool MoveReflect2D(GameObject& gameObject, const vector2D<valuetype>& motion, const GameObjectCollection& other, bool updateVelocity) { // Output variables for collision finder vector2D<valuetype> position; vector2D<valuetype> normal; valuetype progression; // Make a copy of the motion vector that is modified throughout iterations vector2D<valuetype> motion_i(motion); unsigned char iteration = 0; bool collision = false; do { // Broadphase filtering based on AABB sweep CollisionManager& c(CollisionManager::GetInstance()); aabb2D<valuetype> sweep(gameObject.aabb2D_world().sweep(motion_i)); GameObjectCollection otherBP(other); otherBP.FilterByOverlap(sweep); // Move the object and push it out of colliding objects bool collisionCurrent = FindFirstCollision2D(gameObject, motion_i, otherBP, position, normal, progression); if (progression == 0.0f) { return true; } gameObject.t() += motion_i * progression; collision |= collisionCurrent; if (!collisionCurrent) { return collision; } PushOut2D(gameObject, motion_i); // Calculate the new motion vector to have redirecting behavior motion_i = (motion_i.reflect(normal)) * (1.0f - progression); if (updateVelocity) { gameObject.velocity(gameObject.velocity2D().align(motion_i)); } iteration++; } while (iteration < s_MaxMovementIterations); return collision; } public: // Moves a game object along the specified motion vector, resolving collisions on the way template<typename valuetype> inline bool Move2D(GameObject& gameObject, const vector2D<valuetype>& motion, CollisionResponse response, const GameObjectCollection& other, bool updateVelocity = false) { // If collisions should be ignored, skip broadphase filtering if (response == CollisionResponse::IGNORE) { MoveIgnore2D(gameObject, motion); return false; } // Move the object based on the specified collision response switch (response) { case CollisionResponse::STOP: return MoveStop2D(gameObject, motion, other, updateVelocity); case CollisionResponse::SLIDE: return MoveSlide2D(gameObject, motion, other); case CollisionResponse::REDIRECT: return MoveRedirect2D(gameObject, motion, other, updateVelocity); case CollisionResponse::REFLECT: return MoveReflect2D(gameObject, motion, other, updateVelocity); } } // Moves the game object based on its velocity, resolving collisions on the way template<typename valuetype> inline bool Move2D(GameObject& gameObject, valuetype deltaTimeSeconds, CollisionResponse response, const GameObjectCollection& other, bool updateVelocity = true) { // Calculate the motion vector from the velocity and delta time vector2D<valuetype> motion = gameObject.velocity2D() * deltaTimeSeconds; return Move2D(gameObject, motion, response, other, updateVelocity); } //////////////////////////////////////////////////////////////// // Debug rendering // //////////////////////////////////////////////////////////////// // Draws the bounding boxes of all game objects void DrawBoundingBoxes() const; private: // Incrementing counter for handles unsigned int m_Handles; // Data structure holding all GameObjects (mapped by GUID) std::unordered_map<GameObjectGUID, GameObject*> m_GameObjects; // Data structure holding all GameObjects (mapped by GameObjectType) std::unordered_map<GameObjectType, std::list<GameObject*>> m_GameObjectsByType; // Adds a GameObject to the by-type indexed map void AddToByTypeMap(GameObject* gameObject); // Removes a GameObject from the by-type indexed map void RemoveFromByTypeMap(GameObject* gameObject); // List of game objects that are marked to be removed std::list<GameObjectGUID> m_GameObjectRemoveList; // Removes all game objects that have been marked for removal void RemoveMarkedGameObjects(); }; }
void Asteroids::OnObjectRemoved(GameWorld* world, SmartPtr<GameObject> object) { if (object->GetType() == GameObjectType("Asteroid")) { SmartPtr<GameObject> explosion = CreateExplosion(); explosion->SetPosition(object->GetPosition()); explosion->SetRotation(object->GetRotation()); mGameWorld->AddObject(explosion); mAsteroidCount--; if(object->GetScale() > 0.2) { float newVel[4][2] = { {-1.0,-1.0}, { 1.0,-1.0}, { 1.0, 1.0}, {-1.0, 1.0} }; GLVector3f obPos = object->GetPosition(); for(int i = 0; i < 4; i++) { SmartPtr<GameObject> newAsteroid = CreateAsteroid(obPos.x, obPos.y); float f = 5; //force newAsteroid->SetVelocity(GLVector3f(newVel[i][0] * f + (rand() % 4), newVel[i][1] * f + (rand() % 4), 0.0)); newAsteroid->SetScale(object->GetScale()/2); mGameWorld->AddObject(newAsteroid); mAsteroidCount++; } } if (mAsteroidCount <= 0) { SetTimer(500, START_NEXT_LEVEL); } } if (object->GetType() == GameObjectType("AsteroidOre")) { SmartPtr<GameObject> explosion = CreateExplosion(); explosion->SetPosition(object->GetPosition()); explosion->SetRotation(object->GetRotation()); mGameWorld->AddObject(explosion); float newPos[4][2] = { {-1.0,-1.0}, { 1.0,-1.0}, { 1.0, 1.0}, {-1.0, 1.0} }; GLVector3f obPos = object->GetPosition(); float obRot = object->GetRotation(); int sep = 5; // how much to separate the ore fromt he position of the asteroid for(int i = 0; i < 4; i++) { SmartPtr<GameObject> ore = CreateOre(); ore->SetPosition(GLVector3f(obPos.x + newPos[i][0] * sep + (rand() % 4), obPos.y + newPos[i][1] * sep + (rand() % 4), 0.0)); ore->SetRotation(obRot); mGameWorld->AddObject(ore); } int noA = 1 + (rand() % 3); cout << "RAND A: " << noA << endl; for (int i = 0; i < noA; i++) { SmartPtr<GameObject> newAsteroid = CreateAsteroid(obPos.x, obPos.y); float rA = rand() % 360; // a random angle cout << "RAND ANGLE: " << rA << endl; newAsteroid->SetVelocity(GLVector3f( cos(DEG2RAD * rA), sin(DEG2RAD * rA), 0.0)); cout << "Object Scale: " << object->GetScale() << endl; newAsteroid->SetScale(object->GetScale()); mGameWorld->AddObject(newAsteroid); mAsteroidCount++; } } }
void Asteroids::OnMouseButton(int button, int state, int x, int y) { if(state == GLUT_DOWN) { if(button == GLUT_RIGHT_BUTTON) { mMoveCamera = true; // Calculate world coordinates const int zl = mGameWindow->GetZoomLevel(); GLVector3f m2w((x - mGameDisplay->GetWidth()/2)/zl + mCameraFocus->x, (mGameDisplay->GetHeight()/2 - y)/zl + mCameraFocus->y, 0.0); SmartPtr<GameObject> checkSelect = mGameWorld->GetOnClickSelect(m2w); SmartPtr<Order> o = NULL; if(checkSelect.GetPtr() == NULL) { o = new Order(ORDER_MOVE); o->SetDestination(m2w); } else { o = new Order(ORDER_ATTACK); o->SetTarget(checkSelect); } mGameWorld->AssignOrderToSelected(o); } if(button == GLUT_LEFT_BUTTON) { // cout << endl; // cout << "GLUT MOUSE: " << x << "\t" << y << endl; // GLVector2i mouseVector(GLVector2i(x, mGameDisplay->GetHeight() - y)); // cout << "GUI COORDS: " << mouseVector.x << "\t" << mouseVector.y << endl; // float dw = mGameDisplay->GetWidth(); // float dh = mGameDisplay->GetHeight(); // cout << "DISPLAY WH: " << dw << "\t" << dh << endl; // Get game zoom level // cout << "ZOOM LEVEL: " << zl << endl; // GLVector3f mouse2world((x - dw/2)/zl + mCameraFocus->x, (dh/2 - y)/zl + mCameraFocus->y, 0.0); // // cout << "M2W COORDS: " << mouse2world.x << "\t" << mouse2world.y << endl; // cout << "CAM FOCUS: " << mCameraFocus->x << "\t" << mCameraFocus->y << endl; // mGameWorld->AddObject(CreateAsteroid(x,y)); // mGameWorld->AddObject(CreateAsteroid()); // mAsteroidCount++; // get zoom level of the game to calculate world coordinates const int zl = mGameWindow->GetZoomLevel(); // Calculate the world coordinate corresponding to mouse coordinates (m2w = mouse to world). GLVector3f m2w((x - mGameDisplay->GetWidth()/2)/zl + mCameraFocus->x, (mGameDisplay->GetHeight()/2 - y)/zl + mCameraFocus->y, 0.0); // Make the selection box visible mSelectionBox->SetVisible(true); // Set the starting point for the box to be the mouse coordinates mSelectionBox->SetStart(GLVector2i(x,y)); // Set the second point of the box (where mouse is) to also be the mouse coordinates to reset it mSelectionBox->SetMouse(GLVector2i(x,y)); // Set the world coordinates coresponding to the mouse coordinates mSelectionBox->SetWorldCoord1(m2w); mSelectionBox->SetWorldCoord2(m2w); SmartPtr<GameObject> selectedGO = mGameWorld->GetOnClickSelect(m2w); if(selectedGO.GetPtr() != NULL){ if(selectedGO->GetType() == GameObjectType("Drone")) { selectedGO->MakeSelected(); } } cout << "CLICK FOUND: " << selectedGO.GetPtr() << endl; //mGameWorld->AddObject(CreateAsteroid(mouse2world.x, mouse2world.y)); // SmartPtr<GameObject> a = CreateAsteroid(mGameWorld->GetHeight(), mGameWorld->GetWidth()); //a->SetVelocity(GLVector3f(0.0,-100.0,0.0)); //mGameWorld->AddObject(a); //mAsteroidCount++; } } else if (state == GLUT_UP) { mSelectionBox->SetVisible(false); mMoveCamera = false; } }