Problem with understanding data access and responding to events in component based system

Started by
7 comments, last by Aristotel 10 years, 11 months ago

Hi GameDev,

I have a problem figuring out how something like this would work:

In a component based system I have solid objects (which can collide) and gameplay type objects (which have various stats like damage...) which are combined (inherited).

Now if I detect a collision between 2 solid objects, in C++ how should I go about checking if the objects are gameplay type and consequently do damage to each other?

I will elaborate when I get more time, thanks in advance

Advertisement

Components are objects stored within a class as members, components are not inherited and thus it's not a component based system.

Assuming Foo is inheriting SolidObject and GameplayObject you could do something like:


vector<Foo> solidsAndGameplayObjects; //A container of the class inheriting from SolidObject and GameplayObject

...

for(auto foos1 = solidsAndGameplayObjects.begin(); foos1 != solidsAndGameplayObjects.end(); ++foos1)  //Iterate through all foos
{
    for(auto foos2 = foos1+1; foos2 != solidsAndGameplayObjects.end(); ++foos2)   //Nestled for-loop to compare all Foo's with eachother exactly one time
    {
        if( collision( foos1, foos2) == true) //Check for collision
        {
            foos1.hitpoint -= foos2.collisionDamage;  //collisionDamage lives in Foo
            foos2.hitpoint -= foos1.collisionDamage;
        }
    }
}

While I wouldn't recommend this approach since you will find yourself programming HORRIBLY TERRIFYING CODE which is not maintainable whence you create more classes inheriting from SolidObject, as you would need another vector for each subclass of SolidComponent...

This is a simple and direct approach to solve your issue though, but I wouldn't recommend it as it will be difficult to maintain.

If you don't understand the stuff written here, please sharpen your C++ skills.

In a component based system I have solid objects (which can collide) and gameplay type objects (which have various stats like damage...) which are combined (inherited).

I'm not 100% sure about the actual definition, but I doubt that even counts still as a component system. Composition, thats what its all about, as the name suggest. So to solve all your problems, simply don't inherit from your components, but compositie them into a generic entity object. Then one option was to use systems to manage this:


class CollisionSystem
{
    void Update(EntityManager& entityManager, MessageManager& messageManager)
    {
        EntityManager::entityVector vEntities;
	    entityManager.EntitiesWithComponents<BoundingVolume>(vEntities);
    
        for(Entity* pLeftEntity : vEntities)
        {
            for(Entity* pRightEntity : vEntities)
            {
                // ...

                if(collision)
                    messageManager.Send<CollidedEntities>(pLeftEntity, pRightEntity);
            }
        }
    }
};

Systems just act upon entities with specific components, bounding volume it is in that case. It doesn' matter what else is attached - it got a bounding volume, it can collide (of course this can be extended). Note how I'm using a messaging system to notify about the collision - each system registered to this specifiy message will recieve it, so for example the damage-system, which can then look if the entities have the needed components (stats, weapon ...) and then performance damage calculation.

For a good libary for C++11, just google EntityX. Free to use, appearently easy to implement (preferred my own variation though), should be more what you are looking for.

Note that collision detection shouldn't be even part of the entity/components at all eigther. It will do for small uses, but really you should rather put that into a special physics module, with the component approach you finally use just taking care of registering and dispatching collisons, and so on.


...
EntityManager::entityVector vEntities;
     entityManager.EntitiesWithComponents<BoundingVolume>(vEntities);
...

^ So each frame you must build/extract list of entities based on system?

How do you do that with minimal performance loss?

So each frame you must build/extract list of entities based on system?

How do you do that with minimal performance loss?

Good catch, thats the one part I havn't gotten right now. It would be way faster to just iterate through all entities directly since this is what happens in EntitiesWithComponents anyway, so its a double performance loss. I think about caching entities by often used combinations of components, this should speed it up a bit. There are many other possible ways to handle this though, just havn't found the motivation to tackle this, CPU Performance isn't a real problem right now too

Thank you eveyone for the replies, it cleared some things up about the definitions.

I am still interested in how a system like this would work:

there are different managers, let's say CollisionManager and HealthManager(more like a general gameplay object manager, just for the simplicity) and you have a object that inherits from 2 interfaces ICollidable and IHealth. This object subscribes to the managers to have its respective states updated each frame. Now, if a collision happens between 2 objects (that both inherit from ICollidable) how should the HealthManager be informed?

I was thinking that when I inform the respective objects of having a collision (by sending ICollidable* of the other object) I tryed to cast those pointers to IHealth and if that failed nothing would happen but if it was successful I update their health parts.

Or should the physics (like mentioned) be completely seperate from the gameplay part. Then where does the connection happen in that case? Does the HealthManager poll for collisions between objects and likewise all the other managers? I presume the CollisionManager would remember already caluclated collisions as to be faster.

Sorry if I am a bother, can you also point me to some articles or examples from which I could learn for myself.

Thank you all very much :)

Recently I've been experimenting with component-based game objects. I came to conclusion that the best (for me) approach is to have a GameObject class which is just a container for class derived from IGOComponent. Creation and usage of a game object looks like this:


GameObject* go=world->createObject("Object's unique name");
go->addComponent(world->createComponent("Transform")); // position+rotation, written by the physics component, read, by the graphics component
go->addComponent(world->createComponent("Graphics"));
go->addComponent(world->createComponent("Physics"));
go->addComponent(world->createComponent("NPC AI"));
go->addComponent(world->createComponent("Health"));
go->initialise();

//...

IGOComponent* GameWorld::createComponent(const std::string& type)
{
	IGOComponentMgr* manager=m_managers[type];
	return manager->createComponent(type);	// one manager could handle several types, e.g. "NPC AI" and "Alien AI" would belong the the same manager
}

//...

void GameObject::initialise()
{
	for(IGOComponent* goc : m_components)
		goc->initialise(this); // setup "connections" between components in one game object
}

//...

void PhysicsComponent::initialise(GameObject* go)
{
	m_owner=go;
	m_transformGOC=go->component("Transform");	// components can communicate directly
}

//..

void PhysicsComponent::update()
{
	for(PhysicsComponent* comp : this->colliders())
	{
		GameObject* otherGO=comp->owner();
		if(otherGO->hasComponent("Bullet"))
		{
			BulletComponent* bt=(BulletComponent*)otherGO->component("Bullet");
			DamageMessage* msg=new DamageMessage();
			msg->causedBy=otherGO;
			msg->damage=bt->calcDamage();
			m_owner->sendMessage(msg);	// components can communicate also "indirectly", using messages. All components of "go" receive msg and can react to it.
		}
	}
}

This design was inspired by an article in Game Programming Gems, part 6.

there are different managers, let's say CollisionManager and HealthManager(more like a general gameplay object manager, just for the simplicity) and you have a object that inherits from 2 interfaces ICollidable and IHealth. This object subscribes to the managers to have its respective states updated each frame. Now, if a collision happens between 2 objects (that both inherit from ICollidable) how should the HealthManager be informed?

Messaging. As in my example code, you have a message system that you use to send out a "collided(left, right)" - message. Each manager can subscribe to messages, and so will the health manager subscribe to the collided-message. The message manager will delegate that message to the health manager, which will translate it and respond accordingly. Once again, you might be way better of using components, not inheritance, but thats in the end up to you.

Thanks again eveyone for your help,

great advice and I really like the idea behind ppodsiadly's code.

Have a nice day, Arhim

This topic is closed to new replies.

Advertisement