Sign in to follow this  
Aristotel

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

Recommended Posts

Aristotel    228

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

Share this post


Link to post
Share on other sites
Joakim Thor    212

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.

Edited by Kuxe

Share this post


Link to post
Share on other sites
Juliean    7068

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.

Share this post


Link to post
Share on other sites
belfegor    2834
...
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?

Edited by belfegor

Share this post


Link to post
Share on other sites
Juliean    7068

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

Edited by Juliean

Share this post


Link to post
Share on other sites
Aristotel    228

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 :)

Share this post


Link to post
Share on other sites

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.

Edited by ppodsiadly

Share this post


Link to post
Share on other sites
Juliean    7068

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.

Share this post


Link to post
Share on other sites
Aristotel    228

Thanks again eveyone for your help,

 

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

 

 

Have a nice day, Arhim

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this