Critique my project, few questions about implementing new functionality

Hello guys,
I recently started my first ECS project, and I need someone to look at it and tell me if I’m going in the right direction. GitHub
I also have some questions regarding the implementation of new functionality.

Simulation constrains:

  • beehives - around 100
  • bees - around 50k
  • flowers 500k+
  1. Right now, bees fly in a straight line from beehives to flowers. I don’t need any pathfinding, but I’d like to implement some kind of Height map of the whole world. This height map would be added to the current bees Y position based on X and Z position when calculating next move. How should I go about it? should I divide world into chunks? Maybe add unsafe array to a singleton? or hold this array in moveSystem, and pass it to some updater by reference.
    Let’s say world is 2000x2000, and since I need to apply some smoothing, the final array comes out to 4000x4000. I can also use bytes instead of floats.

2 I have lot’s of flowers. Each bee needs to find a flower within range to fly to. Based on the info I found the best approach would be to just divide flowers into chunks with SharedComponent as id with subchunk as secondary id for more granular control. Or is there a better implementation.
Red - sharedComponent
Green IComponentData

  1. My simulation spikes up in one moment. I already found the offender, but I am not entirely sure how to resolve it.
[BurstCompile]
[WithAll(typeof(BeeColonyStats))]
[WithNone(typeof(Hiding))]
public partial struct StartHiding : IJobEntity
{
    public EntityCommandBuffer ECB;

    public void Execute(Entity entity, in HiveLocationInfo info)
    {
        ECB.RemoveComponent<Foraging>(entity);
        ECB.RemoveComponent<Collecting>(entity);
        ECB.RemoveComponent<Delivering>(entity);
        ECB.RemoveComponent<Roaming>(entity);
        ECB.RemoveComponent<Searching>(entity);
        ECB.SetComponentEnabled<Moving>(entity, true);
        ECB.SetComponent(entity, new Target { Position = info.HivePosition });
        ECB.AddComponent<Hiding>(entity);
    }
}

The bees know what to do based on those states: Searching, Roaming, Delivering, Collecting, Foraging, Hiding. When night or winter comes, they all change states to hiding with the script above.
Currently I am trying to remove states that the entities do not have, and I think that is the cause for spike.
I couldn’t find any way to check if an entity has a component inside the IJobEntity.
I could split it into 5 different jobs that would target each state, but is it really the best implementation?

  1. Sounds like a candidate for a blob asset.
  2. There’s lots of ways to do spatial structures. Shared Components in a grid are one of them, but you have to be careful because you can fragment your ECS chunks pretty bad. I’d look for a different structure.
  3. Lots of things here. First, is there a reason you are using ECB to set Moving and Target rather than setting them directly in the job? Second, you can combine all those RemoveComponent calls into a single operation by creating a ComponentTypeSet and passing that in. And lastly, if this applies to all bees, you could use the EntityQuery overload to do your structural changes much faster.
3 Likes

I would have thought, leave the statuses rather than removing or adding, Hiding overules the other statuses. Instead enable and disable when not hiding. Adding/Removing equals structural change, while on/off does not which equals speed (not reassigning chunks/memory). You could probably do the Sets in a parallel job then using ParallelWriter

1 Like

Thanks for the reply
3) I’ve tried implementing everything you have mentioned ( except overloading EntityQuery ) and it wasn’t enough. FPS went from ~25 during spike to ~35. I definitely was overusing ECB, so after correcting it I took @Trindenberg advice and I turned every state into IEnableableComponent, and toggled them on/off with EnabledRefRW. After this change fps went to ~120+, that’s probably because my code was too fragmented, since they also had ISharedComponentData
.
But now I am left with this code below, and I’d like to eliminate ECB calls. Is there a way to grab IEnableableComponent regardless of whether it’s enabled or disabled

[BurstCompile]
[WithAll(typeof(BeeColonyStats))]
[WithDisabled(typeof(Hiding))]
public partial struct StartHiding : IJobEntity
{
    public EntityCommandBuffer ECB;

    public void Execute(ref Target target, in HiveLocationInfo info, Entity entity, EnabledRefRW<Hiding> hiding)
    {
        ECB.SetComponentEnabled<Foraging>(entity, false);
        ECB.SetComponentEnabled<Delivering>(entity, false);
        ECB.SetComponentEnabled<Searching>(entity, false);
        ECB.SetComponentEnabled<Collecting>(entity, false);
        ECB.SetComponentEnabled<Roaming>(entity, false);
        ECB.SetComponentEnabled<Moving>(entity, true);
        hiding.ValueRW = true;
        target.Position = info.HivePosition;
    }
}
  1. I was planning to update this HeightMap every few frames at least, I was under impression that blob asset is used for data that doesn’t change. I think that 2d array in singleton component will suffice.

WithPresent is what you want. There might be some issues with IJobEntity though, so you may need to manually pass in an EntityQuery when scheduling the job rather then use attributes.

1 Like

What if you make these many states a flaggable component with an enum, as long as they won’t ever have 2 of these states.

public enum BeeActivity
{
    Foraging,
    Delivering,
    Searching,
    Collecting,
    Roaming,
    Moving
}


public struct BeeActivityState : IComponentData
{
   public BeeActivity CurrentActivity;
}


[BurstCompile]
[WithAll(typeof(BeeColonyStats))]
[WithDisabled(typeof(Hiding))]
public partial struct StartHiding : IJobEntity
{
    [NativeDisableParallelForRestriction]
    public ComponentLookup<BeeActivityState> ActivityStateLookup;

    public void Execute(ref Target target, in HiveLocationInfo info, Entity entity)
    {
        ActivityStateLookup[entity] = new BeeActivityState
        {
            CurrentActivity = BeeActivity.Moving
        };

        target.Position = info.HivePosition;
    }
}

Although that would mean restructuring. Not sure what the best way to work with states is.

1 Like