0

I'm currently working on a Entity Component System (ECS) for a game I'm developing in C# and Monogame.

My GameObject class has a dictionary with all its components, and this method is written to retrieve them.

internal T GetComponent<T> (bool imperative = false) where T : Component 
    {
      string name = typeof(T).Name;
      if (!components.ContainsKey(name))
      {
        if(imperative)
        {
          T newComp = default(T);
          AddComponent(newComp);
          return newComp;
        }
        throw new Exception("Component not found");
      }
      return (T)components[name];
    }

The thing is, that I want to pass this same function (inject this method, if you will) to a component in particular. This is because I want this component to be able to communicate with other components, without passing a reference to the dictionary.

I imagine this component should have something like this

public class UpdaterComponent: Component
{
  internal Func<T> GetComponent; //where T : Component 
  //...
}

And then in the constructor I could pass the function from the GameObject to the Component. However I have no idea how to write this. I have read about delegates, but I don't want to create a delegate for everytype of Component there is. Is there a clean way to write this? Thanks!

2
  • 1
    Why not make GetComponent<T> a method of Component? Commented Dec 8, 2024 at 4:24
  • GetComponent is a generic method that isn't very generic because you have to know the "type" of the object you want to "communicate with". An inventory of "game objects" that would expose as an "is" or an "as" under further interrogation would be more flexible (IMO); as well as being able to indentify (retrieve): resting; animating; etc. "Communication" implies a bi-directional (entity) relationship which can be implmented in any number of ways. Commented Dec 8, 2024 at 18:52

1 Answer 1

0

For what it's worth I think this might be an anti-pattern you want to avoid in ECS. Components should be containers of data that are attached to entities, and systems should be the pieces making changes and acting on that data for entities they own/watch.

But you can make an overload that takes a Type as a parameter instead of taking a generic type parameter:

internal Component GetComponent(Type type, bool imperative)
{
    string name = type.Name;
    if (!components.ContainsKey(name))
    {
      if(imperative)
      {
        T newComp = default(T);
        AddComponent(newComp);
        return newComp;
      }
      throw new Exception("Component not found");
    }
    return components[name];
}

And to pass it to a Component:

public class MySpecialComponent(Func<Type, Component> componentLookup)
{
}

Then for convenience where a type is known at compile time you can still have a generic overload:

internal TComponent GetComponent<TComponent>(bool imperative)
where TComponent : Component
    => (TComponent)GetComponent(typeof(TComponent), imperative);

And unless you have a specific reason for using Dictionary<string, Component> I would use Dictionary<Type, Component>. Then you don't need to use string literals to refer to types, which could become invalid as your code changes.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.