Jump to content
  • Advertisement
Sign in to follow this  
Telanor

Generic way of registering entity component with system

This topic is 1869 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have an entity-component system where all components inherit from an abstract Component class. Along with that I have a base ComponentSystem class for systems to inherit from (systems being manager classes that handle updating/rendering/etc of components). Not all components require a manager system (in fact most don't) but for the ones that do, they need to override the Register function, which is called when a component is added to an entity. From there, they can call the RegisterComponent function of their system, which adds the component to the list the manager holds. So a very basic example would be something like this:
public abstract class ComponentSystem<T> where T : Component
{
	protected readonly List<T> components = new List<T>();

	public void RegisterComponent(T component)
	{
		components.Add(component);
	}

	public void UnregisterComponent(T component)
	{
		components.Remove(component);
	}
}

public sealed class StateSystem : ComponentSystem<StateComponent>
{
	private static readonly StateSystem instance = new StateSystem();

	public static StateSystem Instance
	{
		get { return instance; }
	}

	private StateSystem()
	{
		...
	}
}

public class StateComponent : Component
{
	public string State { get; set; }

	public override void Register()
	{
		StateSystem.Instance.RegisterComponent(this);
	}

	public override void Unregister()
	{
		StateSystem.Instance.UnregisterComponent(this);
	}
}
I was wondering if there's some way to set it up so that a Component can specify what system it belongs to and the base Component class code can just handle calling the Register/Unregister functions without the need of repeatedly implementing them. Thoughts?

Share this post


Link to post
Share on other sites
Advertisement
You sound like you should already know this so I hate to reply, but isn't that the point of virtual functions and manager classes is so that you don't have to readd them. Then again I'm used to c++ instead so...

Share this post


Link to post
Share on other sites
Well the issue is I don't know how to inform the base class of what manager class to call the function on. Its one thing if they all called the same manager class, but they're all different. Since the manager classes are using generics, I wasn't able to do something like this:
public abstract class Component
{
	public Actor Entity { get; set; }
	public bool Enabled { get; set; }
	protected ComponentSystem<SomethingNeedsToBeHere> system;

	protected Component()
	{
		Enabled = true;
	}

	/// <summary>
	/// Called after a component is added to an entity.
	/// </summary>
	public virtual void Register()
	{
		system.RegisterComponent(this);
	}

	/// <summary>
	/// Called before a component is removed from an entity.  Clean up should be done here
	/// </summary>
	public virtual void Unregister() { ... }
}

Share this post


Link to post
Share on other sites

I would do it slightly differently. In your system, implement an Initialise or Configure method that adds the relevant components to itself.

 

Something like:

 

public abstract class ComponentSystem<T> where T : Component
{
	protected readonly List<T> components = new List<T>();

        public abstract void Configure();

	public void RegisterComponent(T component)
	{
		components.Add(component);
	}

	public void UnregisterComponent(T component)
	{
		components.Remove(component);
	}
}

public sealed class StateSystem : ComponentSystem<StateComponent>
{
	private static readonly StateSystem instance = new StateSystem();

	public static StateSystem Instance
	{
		get { return instance; }
	}

        public void Configure()
        {
           this.RegisterComponent(new StateComponent() { State="New" });
        }

	private StateSystem()
	{
		...
	}
}

public class StateComponent : Component
{
	public string State { get; set; }
}

 

This way, the system manages the components, not the components managing their memberships of systems. And it also means that the components no longer depend on the system.

Share this post


Link to post
Share on other sites
Hmm, but the components are constructed for and added to entities. If I make 5 entities, I need 5 StateComponent objects. It may work if I made a factory function within the system, but there's two minor issues with that. The first is that not all components need a system to manage them, so for those, they'd either have to have a useless system that provides a factory function, or they'd have to just be created with a constructor, which could be confusing if half the components are created via constructor and the other half are created via factory function....

The second issue is that it would register the component the instant it's created, rather than after it's been attached to an entity. Probably a mostly meaningless concern, but it does change the intent of the register function and require an extra "if component.Entity != null" check. Edited by Telanor

Share this post


Link to post
Share on other sites

The most straight-forward solution to your problem would probably be to make ComponentSystem<T> implement an interface that knows RegisterComponent(Component) and UnregisterComponent(Component) and make ComponentSystem<T> cast the argument to T so it can call the generic RegisterComponent(T).

 

The system I designed for my current project is a bit more generic, because I want it to allow components registered to multiple systems, etc.  It basically looks like this:

 

public class Entity {
    public List<Component> Components { get; set; }
}

// holds only data
public class Component {
    public Entity Parent { get; set; } // may not even need this
}

public abstract class ComponentSystem {
    public abstract void Initialize(ComponentSystemManager csm);
    public abstract void Update();
}

public class ComponentSystemManager {
    private Dictionary<Type, List<Action<Component>>> onComponentCreated;

    public void SubscribeComponentCreated<T>(Action<T> onAdded) where T : Component
    {
        var key = typeof(T);
        Action<Component> value = c => onAdded((T)c);
        this.onComponentCreated[key].Add(value);
    }

    // this is the only place where component are created
    public void CreateComponent<T>(Entity parent, Action<T> init) where T : Component, new()
    {
        var key = typeof(T);
        var component = new T();
        component.Parent = parent;
        init(component);
        parent.Components.Add(component);
        foreach (var subscriber in onComponentCreated[key]) {
            subscriber(component);
        }
    }

    public void DestroyComponent(Component c) { ... }
}

public class ExampleComponent : Component
{

}

public class ExampleSystem : ComponentSystem
{
    public override void Initialize(ComponentSystemManager csm)
    {
        csm.SubscribeComponentCreated<ExampleComponent>(this.ExampleComponentCreated);
    }

    private void ExampleComponentCreated(ExampleComponent c)
    {
        // ...
    }
}
Edited by lwm

Share this post


Link to post
Share on other sites

I see. My system is sort of similar. My model is based off Unity3d. I have an Entity class (GameObject) and a Component class. Components are created and added to the Entities, and the Component holds a reference back to the Entity it is attached to. This reference is set by the process of adding a component to the entity.

 

I don't have ComponentSystems, as Entities can have any types of Component added to them. If I we're to introduce Systems, I'd look at making them manage Entities, and then grab the required components off them. My GameObject class allows me to get a component by type, for example. I have constrained it by allowing only one component of a given type to be attached, but this was arbitrary.

Share this post


Link to post
Share on other sites
Ah, that interface idea works. Now I only have to override a virtual property instead of two functions.

Evolutional: What you described is pretty much exactly what I've done, especially the retrieving by type thing. How are you managing without any systems though? One of the main things I'm using systems for is rendering. I have a Renderable component that can be attached to an entity which holds a reference to a model. Then there's a RenderableSystem that has the code for going through and drawing any Renderable components

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!