Component stays alive when the GameObject goes out of scope

Started by
2 comments, last by Andor Patho 7 years, 10 months ago

I've got a component manager that stores a reference to the component,

this component is created as a new instance or obtained thru a singleton.

and data type for it is boost::shared_ptr.


void Truck::Create()
{
    SimObject::Create();    

    boost::shared_ptr<Arrivals> l_arrival = Arrivals::getInstance();
    this->AddComponent(ComponentManager::getInstance()->AddComponent(l_arrival));
    
    CMinMax<float> arrivalTime(10000.0f, 20000.0f);
    this->FindCompoentByType<Arrivals>()->PostRequest(*this, arrivalTime.GetRandomNumber(), arrivalTime.GetRandomNumber());

When the component is first created, the strong count is 4,

1st from the getInstance itself

2nd from l_arrival

3rd from ComponentManager::getInstance()->AddComponent(l_arrival)

4th from this->AddComponent();

which is quite strongly referenced.

When the game exits, the game object dies,but the component stays alive,

the arrival component is basically an observable by the game object (the truck),

the truck dies, the observable has no observers to deliver messages to,

so the program crashes.

I like the idea of self-managed pointers, but it seems not applies here

Also the arrivals component has a threadpool in itself,

but the destructor of arrivals is too late to destruct, when it dies, all pending requests will be purged.

Any better idea for me?

Thanks

Jack

Advertisement

At first glance what you are doing seems severely overcomplicated.

First you have to figure out your ownership semantics, and go from there:

  • Who owns the component? Seems like the game object should be the owner, since it's destruction has to destroy the component as well (since you mentioned it will not work otherwise).
  • Is there any kind of shared ownership going on? Not really, since we alreay established the component cannot outlive the game object, so shared ownership just doesn't make sense.
  • Who needs to have access to (store pointers to) the component (other than the owner)? The ComponentManager maybe? Here you have two choices: the ComponentManager will store a weak_ptr to the component (this means the game object will need to store a shared_ptr), or the ComponentManager will store a raw pointer, which means the game object can store a unique_ptr. IMHO the second approach is better, unless there is some specific issue preventing you from using it. When your game object is destroyed, it can simply unregister all it's components from the ComponentManager so no dangling pointer is left behind (which it should do anyway, even if using weak_ptr).

Not everyone would agree, so take this with a grain of salt, but IMO using smart pointers everywhere is a poor way of managing object lifetimes, in most cases they are just a poor attempt at masking an underlying ownership issue. I very rarely encounter a justified use case for shared_ptr and weak_ptr - the use cases definitely exist, but just aren't very common.

When the game object store a shared instance of the physics mover,

I am wondering, if the game object uses a unique_ptr to store components,

in order to keep the interface consistent, should I make up 2 separate kinds of components,

one sharable to be used by components like astar, timeastar and physics

and non-sharable (solely owned) by one object, like the state machines?

the common data type is a Component, so that I can add sharable and non-sharable

components to the same container?

The only problem is when the container is a unique_ptr, it is not sharable in the first place....


// Step 2 ---- should be a singleton, shared by many objects
PhysicsMover* l_physicsmover = PhysicsMover::getInstance();    
l_physicsmover->addMesh(this);
this->AddComponent(ComponentManager::getInstance()->AddComponent(l_physicsmover));   

Sorry I haven't responded, I've been AFK a few days.

First, to answer your question: unique_ptr is just a convenient way to automatically delete objects when the pointer goes out of scope, but it is not essential to use it. You can just store raw pointers and delete the objects at the appropriate time (e.g. in the game object destructor). You would need to determine which components you own and should delete, and which components are shared. Even though it's technically possible however, I would not mix objects with different ownership together in one container, seems like asking for trouble.

Second, I will ask some questions of my own if you don't mind. Why is PhysicsMover a shared singleton? If it makes sense for it to be a singleton, should it even be a component? Maybe it should be a subsystem (that acts on components) instead? Astar doesn't really seem like a component to me. If you have a single shared Astar component, where would the calculated path be even stored? How about a Pathfinder component instead, which can have parameters like pathfinding speed vs. accuracy, and uses the Astar subsystem (or can use any associated pathfinding system) to calculate paths that it can store?

This topic is closed to new replies.

Advertisement