Пример #1
0
	/*! \fn GridLocation getNeighbor (const GridLocation & location, const Direction & direction)
	 * 	\brief Fonction permettant de connaitre le voisin d'une GridLocation en fonction d'une Direction.
	 * 	\param location La GridLocation
	 * 	\param direction La direction
	 * 	\return GridLocation
	 */
	static GridLocation getNeighbor (const GridLocation & location,
									 const Direction & direction)
	{
		switch (direction)
		{
			case UP:
				return GridLocation(location.first,location.second - 1);
			case DOWN:
				return GridLocation(location.first,location.second + 1);
			case LEFT:
				return GridLocation(location.first - 1,location.second);
			case RIGHT:
				return GridLocation(location.first + 1,location.second);
			default:
				break;
		}
		return UNDEFINED_LOCATION;
	}
void System<CollisionComp>::update(double elapsed)
{
//    std::cout << "Updating CollisionSys" << std::endl;

    //create hash and clear it
    CollisionHash* hash = static_cast<CollisionHash*>(extensions_);
////    static_cast<CollisionHash*>(extensions_);
//    extensions_ = hash;
//    std::cout << "Clearing hash" << std::endl;
    hash->clear();
//    std::cout << "Cleared" << std::endl;

    //update all collision components

    int screenWidth = core_->getUi()->getWindow()->GetWidth();
    int screenHeight = core_->getUi()->getWindow()->GetHeight();
    std::multimap<ObjectId,CollisionComp>::iterator iCom = components_.begin();
    while(iCom!=components_.end())
    {
        CollisionComp* currComponent = &iCom->second;

        //grab coordinates of entity
        CoordsComp* coordsComponent = core_->getCoordsSub()->getComponent(iCom->second.getId());

        //get object id
        ObjectId objId = iCom->second.getId();
        //get position and dimension
        Vector2d topLeft = coordsComponent->getCoords();
        Vector2d dimensions = coordsComponent->getDimensions();
        Vector2d bottomRight = topLeft + dimensions;

        //get rid of entities that go off bottom of screen TODO: Could be implemented as an onOffScreen message?
        if(topLeft.y < -100 || bottomRight.x < -100 || bottomRight.y > screenWidth +100 || topLeft.y > screenHeight + 100)
        {
            Parameters params;
            params.push_back("object");
            params.push_back("destroy");
            Message msg(objId, objId, params);
            Telegram telegram(objId, objId, 0.0, msg);
            core_->getMessageCentre()->addTelegram(telegram);
            ++iCom;
            continue;
        }
        //get hash locations for all four corners
        std::set<GridLocation> locations;
        locations.insert(std::make_pair<int,int>(int(topLeft.x)/MAX_OBJECT_DIMENSION,int(topLeft.y)/MAX_OBJECT_DIMENSION));
        locations.insert(GridLocation(int(topLeft.x)/MAX_OBJECT_DIMENSION,int(bottomRight.y)/MAX_OBJECT_DIMENSION));
        locations.insert(GridLocation(int(bottomRight.x)/MAX_OBJECT_DIMENSION,int(topLeft.y)/MAX_OBJECT_DIMENSION));
        locations.insert(GridLocation(int(bottomRight.x)/MAX_OBJECT_DIMENSION,int(bottomRight.y)/MAX_OBJECT_DIMENSION));
//        std::cout << "SETSIZE: " << locations.size();

        //set hash on component
        currComponent->setSpatialHash(locations);

        //add to hash
        std::set<GridLocation>::iterator iLocs=locations.begin();
        while(iLocs!=locations.end())
        {
            //check if it already exists

            CollisionHash::iterator iFind  = hash->find(*iLocs);

            if(iFind==hash->end())
            {
                std::vector<ObjectId> vec;
                vec.push_back(objId);
//                hash->insert(std::make_pair<GridLocation,std::vector<ObjectId> >(*iLocs,vec));

                hash->insert(std::pair<GridLocation,std::vector<ObjectId> >(*iLocs,vec));
            }
            else
            {
                iFind->second.push_back(objId);
            }
//             hash[*iLocs].push_back(objId);
//            hash->insert(std::make_pair<GridLocation,ObjectId>(*iLocs, objId));
            ++iLocs;
        }

        ++iCom;
    }

    //analyse hash and look for collisions
    std::set<std::pair<ObjectId, ObjectId> > collisions;

    CollisionHash::iterator iHash = hash->begin();
    while(iHash!=hash->end())
    {
        if (iHash->second.size()>1)
        {
            //check all combinations of possible collisions between objects, e.g. A, B, C, D => AB, AC, AD, BC, BD, CD and, by nature, their reverses BA, CA, DA, CB, DB, DC
            for (unsigned int oA = 0; oA < iHash->second.size(); ++oA)
            {
                for (unsigned int oB = oA+1; oB < iHash->second.size(); ++oB) // oB = oA+1 so that it automatically prevents cases of AA checks or BB checks. i.e. if checking oB vs oA, that would be A vs A, whereas oB(oA+1) vs oA would be  B vs A
                {
                    //std::cout << "Checking collision between Objects " << iHash->second[oA] << " and " << iHash->second[oB] << std::endl;
                    //do radial check for collision on coords of objects
                    ObjectId objIdA = iHash->second[oA];
                    ObjectId objIdB = iHash->second[oB];
                    Vector2d coordsA = core_->getCoordsSub()->getComponent(objIdA)->getCoords();
                    Vector2d coordsB = core_->getCoordsSub()->getComponent(objIdB)->getCoords();
                    Vector2d dimsA = core_->getCoordsSub()->getComponent(objIdA)->getDimensions();
                    Vector2d dimsB = core_->getCoordsSub()->getComponent(objIdB)->getDimensions();
                    //mock up a 'radius' TODO: This should be done on creation in coords comp or something!
                    float radiusA = (dimsA.x >= dimsA.y ? float(dimsA.y) /2.0 : float(dimsA.x) / 2.0);
                    float radiusB = (dimsB.x >= dimsB.y ? float(dimsB.y) /2.0 : float(dimsB.x) / 2.0);
                    //calculate max distance between objects to obtain a collision
                    float distForCollision = radiusA + radiusB;
                    float distAB = sqrt(pow((coordsA.x - coordsB.x),2) + pow((coordsA.y - coordsB.y),2));
//                    float distAB = sqrt(pow((coordsA.x + float(dimsA.x) /2.0) - (coordsB.x + float(dimsB.x)/2.0),2) +
//                                        pow((coordsA.y + float(dimsA.y) /2.0) - (coordsB.y + float(dimsB.y)/2.0),2));
                    //now, check for collision
                    if (distAB <= distForCollision)
                    {
                        //std::cout << "Collision! " << iHash->second[oA] << " and " << iHash->second[oB] << std::endl;
                        //add collision
                        collisions.insert(std::pair<ObjectId,ObjectId>(iHash->second[oA], iHash->second[oB]));
                        //TODO: Ensure that multiple collisions between same objects does not occur due to entites being present in multiple grid squares, thus being assessed multiple times.
                    }

                }
            }
        }
        ++iHash;
    }

    //Send collision messages
    std::set<std::pair<ObjectId,ObjectId> >::iterator iCol = collisions.begin();
    while(iCol!=collisions.end())
    {
        //grab messages for object A and B
        std::vector<Parameters> aMessages = getComponent(iCol->first)->getOnCollisionMessages();
        std::vector<Parameters> bMessages = getComponent(iCol->second)->getOnCollisionMessages();
        //send a message for each onCollisionMessage that the collision components have
        //aObject messages
        std::vector<Parameters>::iterator iMessage;
        iMessage = aMessages.begin();
        while(iMessage!= aMessages.end())
        {
            //calculate target
            ObjectId targetObject;
            if((*iMessage)[0]=="self")
            {
                targetObject = iCol->first;
            }
            else if ((*iMessage)[0]=="target")
            {
                targetObject = iCol->second;
            }
            else
            {
                std::cout << "ERROR: Incorrect parameter for onCollisionMessage in object (i.e. not 'self' or 'target': " << ((*iMessage)[0]) << std::endl;
                exit(EXIT_FAILURE);
            }


            //grab all params except first one from onCollisionMessage (first one is whether target is self or other)
            Parameters params(iMessage->begin()+1, iMessage->end());
            //build message and dispatch
            Message msg(iCol->first, targetObject, params);
            Telegram telegram(iCol->first, targetObject, 0.0, msg);
            core_->getMessageCentre()->addTelegram(telegram);

            ++iMessage;
        }

        //bObject messages
        iMessage = bMessages.begin();
        while(iMessage!= bMessages.end())
        {
            //calculate target
            ObjectId targetObject;
            if((*iMessage)[0]=="self")
            {
                std::cout << "Sending collision message to self" << std::endl;
                targetObject = iCol->second;    //not iCol->first, as we're now dealing with how B deals with A, not A deals with B.
            }
            else if ((*iMessage)[0]=="target")
            {
                std::cout << "Sending collision message to target" << std::endl;
                targetObject = iCol->first;
            }
            else
            {
                std::cout << "ERROR: Incorrect parameter for onCollisionMessage in object (i.e. not 'self' or 'target': " << ((*iMessage)[0]) << std::endl;
                exit(EXIT_FAILURE);
            }

             //grab all params except first one from onCollisionMessage (first one is whether target is self or other)
            Parameters params(iMessage->begin()+1, iMessage->end());
            //build message and dispatch
            std::cout << "Sending collision message: ";
            for (int x=0;x<params.size();++x)
            {
                std::cout << "\t>" << x << ": " << params[x] << std::endl;
            }
            Message msg(iCol->second, targetObject, params);
            Telegram telegram(iCol->second, targetObject, 0.0, msg);
            core_->getMessageCentre()->addTelegram(telegram);
            ++iMessage;
        }

        ++iCol;
    }
}