0
\$\begingroup\$

I am unsure which method to use to handle my enemies in SFML.

The idea that I am trying to recreate is to spawn enemies every so many seconds. Initially I wanted to do this using a vector such as std::vector SpriteVector; The advantages of this method would be that in theory it is easy to add and remove my enemies from the vector. Using the push_back and erase method.

However the error with this at the moment is that when I am trying to check whether the sprite or the arrows are hitting an enemy within my vector it will not work.

I know that I could possibly use an array to store the enemies then set their state when hit to false however I am not sure how this will effect spawning my enemies. The functionality of being able to add to a vector is what I think I will need. Otherwise I am sure not sure how to get around the issue of having a certain number of enemies within the array then spawning more over a series of time.

Is this the best way to go about the task I am trying to achieve?

\$\endgroup\$
3
  • \$\begingroup\$ Why can't you loop through the vector and check collisions? \$\endgroup\$ – congusbongus Mar 15 '15 at 23:57
  • \$\begingroup\$ When I try to do that I get the following error 19 IntelliSense: no instance of overloaded function "sf::Rect<T>::intersects [with T=float]" matches the argument list argument types are: (sf::Sprite) object type is: sf::FloatRect Error 17 error C2664: 'bool sf::Rect<T>::intersects(const sf::Rect<T> &) const' : cannot convert parameter 1 from 'sf::Sprite' to 'const sf::Rect<T> \$\endgroup\$ – PapaSmurf Mar 16 '15 at 0:15
  • \$\begingroup\$ Have you tried "sprite_vector_name[#].getGlobalBounds()" to compare with rather than just "sprite_vector_name[#]"? \$\endgroup\$ – NovaCrist May 16 '15 at 3:32
1
\$\begingroup\$

I think what you're looking for is an object pool. You can create such a pool with constant time (O(1) complexity) creation and deletion, which is perfect for your case, I believe.

Let's say you have an Entity class:

class Entity
{
public:
    Entity* getNext(void) const { return m_state.next; }
    void setNext(Entity* next) { m_state.next = next; }

private:
    // Entity data...

    // Store either entity data or the next one in the pool.
    union{
        // Entity data.
        struct{
            int x, y;
        } position;

        // Next entity pointer used by object pool.
        Entity* next;
    } m_state;
};

You'll need a pool class for managing all entities:

class EntityPool
{
public:
    explicit EntityPool(void) :
    m_entities(new Entity[MAX_ENTITIES])
    m_firstAvailable(nullptr) {
        // Assign first available entity.
        m_firstAvailable = m_entities[0];

        // Assign each entity's next pointer.
        for (int i = 0; i < MAX_ENTITIES - 1; ++i){
            m_entities[i].setNext(&m_entities[i + 1]);
        }

        // Terminate end of array.
        m_entities[MAX_ENTITIES - 1].setNext(nullptr);
    }

    ~EntityPool(void){
        delete[] m_entities;
    }

    Entity* create(void){
        // Check if pool is full.
        assert(m_firstAvailable != nullptr);

        // Pull out the next available entity.
        Entity* e = m_firstAvailable;
        m_firstAvailable = e->getNext();

        // Init entity if needed...
        // ...

        return e;
    }

    void destroy(Entity* e){
        // Reset entity data...
        // ...

        e->setNext(m_firstAvailable);
        m_firstAvailable = e;
    }

private:
    static const int MAX_ENTITIES = 256;
    Entity m_entities[MAX_ENTITIES];
    Entity* m_firstAvailable;
};

So then, creating and destroying entities is simple. To create one, use it, and destroy it would look something like this:

Entity* monster = m_entityPool.create();
// Do things with monster...
// ...
// Monster is dead, remove it.
m_entityPool.destroy(monster);

You can also add an update() function to the pool to iterate through all the entities. This is a very simple example of a pool, and should be expanded upon in your own implementation.

\$\endgroup\$
1
  • \$\begingroup\$ An object pool is a good idea, but I just want to add that it might be better to take the last object in the pool to fill in a deletion rather than trying to keep count of all the holes made by removed objects. There's also no shame in iterating through a linked list if MAX_ENTITIES is small or the entities don't need to be searched. \$\endgroup\$ – NovaCrist May 16 '15 at 3:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.