Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   Related Pages  

Details: Using the Grid

Here you will find some grid usage scenarios. The code given here much like real code, but is intended for explanatory purposes only, so be warned... For entity type I've used CEntity here, but you will most likely use your own entity subclass.

So, here are: Shooting the entities, Bombs and slimes, Managing crowds, If precision is needed.

Shooting the entities

The problem: we want to shoot entities. That is, we have some ray, and we want to test the entities for intersection with it.

The naive approach: test all the entities. This fails pretty quickly, as entity count increases.

So, instead let's have a grid, whose sectors contain the ray or contain nothing:

    struct SRaySector {
        bool        mHasRay;
        D3DXVECTOR3 mOrigin; // origin of the ray
        D3DXVECTOR3 mDir;    // direction of the ray
    };
    typedef CGrid<SRaySector,??,??> TRayGrid;

Now we can "put" the ray only into the sectors it crosses:

    CEntityRayPicker<TRayGrid>::putRay( grid, origin, direction );
Also, don't forget to "clear" the grid when ray is not needed anymore (or you are putting in another ray).

Then for each entity that can be shot:

    bool shootEntity( TRayGrid& grid, CEntity& entity ) {
        // get our sector
        const SRaySector& s = grid.getSectorByPosition( entity.getPosition().x, entity.getPosition().y );
        if( s.mHasRay ) {
            // if it has a ray, check for intersection with entity
            return CEntityRayPicker<TRayGrid>::intersects( entity, s );
        }
        return false; // no ray in my sector - no intersection
    }
This early-outs on the first if for most of the entities, and performs checks only for those that are in "relevant" sectors.

Almost whole code that is needed for this case already exists in the engine.

Bombs and slimes

The problem: we want to drop bombs, or have some dangerous areas.

Again: let's have sectors that can contain bombs:

    struct SBombSector {
        bool        mHasBomb;
        float       mBombX, mBombY; // precise location, if you need it
    };
    typedef CGrid<SBombSector,??,??>    TBombGrid;

Now we put bombs into "relevant" sectors. Then for each entity that can be blasted off:

    void blastEntity( TBombGrid& grid, CEntity& entity ) {
        // get our sector
        const SBombSector& s = grid.getSectorByPosition( entity.getPosition().x, entity.getPosition().y );
        if( s.mHasRay ) {
            // if it has a bomb - do something
            doSomethingWithIt();
        }
    }

Managing crowds

The problem: we don't want all the entities to gather into one spot.

So, lets have them "mark" the area they're in (much like dogs do). Each entity increments the "there are many of us here" counter in it's sector. If the counter exceeds some limit, the entity may set it's velocity to run away, for example. Or it can see the sector it will be in soon (by adding some if it's own velocity to position), and stop if there are already many other entities.

This is very crude approximation, though. You're invited to invent your own methods :)

So:

    struct SCrowdSector {
        int     mCounter;
    };
    typedef CGrid<SCrowdSector,??,??>   TCrowdGrid;

Then for each entity:

    void walkEntity( TCrowdGrid& grid, CEntity& entity ) {
        const D3DXVECTOR3& pos = entity.getPosition();  // position
        D3DXVECTOR3& vel = entity.getVelocity();        // velocity
        D3DXVECTOR3 future = pos + someFactor * vel;    // future position
        // leave mark here
        grid.getSectorByPosition( pos.x, pos.y ).mCounter++;
        // check future
        if( grid.getSectorByPosition( future.x, future.y ).mCounter > SOME_AMOUNT )
            vel.x = vel.y = 0.0f; // stop
    }
Note that base engine entity (CEntity) does not have velocity information. I assume that you use your own entity here, which has velocity.

The same idea could be used for fighting armies: have a sector with two (or more) counters, each entity increments it's army's counter. After that, you go through the sectors and see force balances (and act accordingly, for example, set "I should die" flag in the sector for losing army).

If precision is needed

The problem: I don't want crappy approximations! Give me precise spatial information!

Common solution is: let sectors contain collections of pointers to the entities they contain. Also let entity contain a pointer to it's current sector, and it's index in sector's collection.

This imposes some work to be done: each time the entity moves, it must remove itself from it's previous sector, and place itself into new sector.

If you plan to do this for all your thousands and thousands of entities, be warned: when we implemented this, we observed total performance loss of 10% or more just for managing the sector-entity relations.

But, of course, you may choose to do this only for a subset of Very Important Entities.

So, our sector is like this:

    struct SPreciseSector {
        enum { MIN_CAPACITY = 16 }; // minimum capacity
        SPhysicsSector();
        ~SPhysicsSector();

        void    increaseCapacity(); // allocate bigger array, copy old data
        void    decreaseCapacity(); // allocate smaller array, copy old data

        int         mCapacity; // capacity of array
        int         mCount;    // entity count
        CEntity**   mEntities; // array of pointers to entitites
    };
Our entity has these:
    short mSectorNumber; // say, -1 for "none"
    short mIndexInSector;
You should also have the code to remove entity from it's sector, and to insert it into new one. Removal:
    if( entity.mSectorNumber == NONE_SECTOR )
        return;
    // get sector
    SPreciseSector& s = grid.getSectorByIndex( entity.mSectorNumber );
    int o = entity.mIndexInSector;
    assert( s.mEntities[o] == &entity ); // just in case
    int lastIndex = --s.mCount;
    if( lastIndex != o ) { // if we're not last
        // place last one into our place
        s.mEntities[o] = s.mEntities[lastIndex];
        s.mEntities[o]->mIndexInSector = o; // update last one's index
    }
    if( s.mCount * 2 <= s.mCapacity ) // like this
        s.decreaseCapacity();
    entity.mSectorNumber = NONE_SECTOR;
Insertion:
    // get sector
    SPreciseSector& s = grid.getSectorByIndex( newSectorIndex );
    if( s.mCount >= s.mCapacity )
        s.increaseCapacity();
    assert( s.mCount < s.mCapacity ); // just in case
    // put at end of entity list
    int o = s.mCount++;
    s.mEntities[o] = &entity;
    entity.mSectorNumber = newSectorNumber;
    entity.mIndexInSector = o;
Before any update of entity position:
    int newSectorNumber = grid.position2index( newPosition.x, newPosition.y );
    if( entity.mSectorNumber != newSectorNumber ) {
        removeFromOldSector();
        insertIntoSector( newSectorNumber );
    }

Now, with this kind of grid, you always have the precise information about who is where. So collision and stuff is the traditional one - get relevant sectors, check with other entities in these sectors. These utilities are not in the engine, but they are pretty easy to code.


Prev: Case study: selecting entities.
Next: Details: The Entities.


Generated on Thu Dec 5 17:27:58 2002 for LT Game Jam Session by doxygen1.2.17