Arch icon indicating copy to clipboard operation
Arch copied to clipboard

Add `World.Create(object[])`

Open genaray opened this issue 2 years ago • 5 comments

Discussed in https://github.com/genaray/Arch/discussions/78

Originally posted by Mathetis April 11, 2023 Hi, I'm curious if the following behavior is by design, if I am misusing it, or if this is a bug.

I have a use case where the array of components arrived as an object[], which contains references to struct components (boxing isn't problematic for my case here). I want to create an entity from those components. Here's a simple example:

struct SomeComponent { ... }

object[] components = new object[] { new SomeComponent() { ... } };

Entity entity = world.Create(components);

bool hasComponent = entity.Has<SomeComponent>();  // <-- this will be false.

We should probably add another World.Create overload to create entities by a object[] array :) This should be done quickly though.

genaray avatar Apr 16 '23 15:04 genaray

I didn't realize this wasn't a feature until I ran into needing it. I got it working with the following:

public Entity CreateFromArray(object[] components)
    {
        ComponentType[] types = getComponentTypesForArchetype(components);
        Entity entity = Create(types);
        SetFromArray(entity, components);
        return entity;
    }

    public void SetFromArray(Entity entity, object[] components)
    {
        switch (components.Length)
        {
            case 1:
                entity.Set(components[0]);
                break;
            case 2:
                entity.Set(components[0], components[1]);
                break;
        }
    }

    private ComponentType[] getComponentTypesForArchetype(object[] components)
    {
        ComponentType[] types = new ComponentType[components.Length];
        for(int i = 0; i< components.Length; i++)
        {
            ComponentType type;
            if(!ComponentRegistry.TryGet(components[i].GetType(), out type))
            {
                type = ComponentRegistry.Add(components[i].GetType());
            }
            types[i] = type;
        }
        return types;
    }

Note: The code I'm working on is directly in the World class. I haven't tried moving it anywhere else. As far as I can tell for now, you'll need individual case statements based on the number of components.

proc-gen avatar May 29 '23 18:05 proc-gen

Working on some more tests now and I realized that using Set only works for a single element array. SetRange is needed for everything higher than that because even though the types are known, it's pulling the component array of the first element for all elements. I think that's the intended outcome because Set is for known types at runtime and SetRange is for unknown types at runtime.

proc-gen avatar Jun 03 '23 13:06 proc-gen

Could something like this ever work with a Query? I am currently trying to create entities at runtime with dynamic classes and as soon as it gets added with the above code it is converted to System.RuntimeType(which probably makes sense with object).

In this image I am trying to create an entity with the following components: image

and below is me trying to query components that are Vector3: image

below is the code with Query.

public class TestSystem : SystemBase
{
	private DebugTextSystem _debugText;
	private QueryDescription _queryDescription;

	public override void Start()
	{
		_debugText = Services.GetService<DebugTextSystem>();

		_queryDescription = new QueryDescription().
			WithAny<Vector3>();
	}

	public override void Update(in GameTime state)
	{
		var result = World.CountEntities(in _queryDescription);

		_debugText.Print($"TestSystem: {result}", new Int2(50, 50));
	}
}

Doprez avatar Nov 05 '23 20:11 Doprez

When you're running your version of CreateFromArray, are you passing it an array of types or an array of objects? Based on the screenshot it seems like you're giving it the types.

The intent of that function is to be given the array of actual components to be used. The function will create an entity of that archetype and then set the components provided to that entity. I use it fairly heavily in my networking code because that's how I generate the entities client-side.

proc-gen avatar Nov 05 '23 21:11 proc-gen

Ahhh my goodness thank you! I was using inputting Type instead of object... It seems to work now.

Doprez avatar Nov 05 '23 21:11 Doprez