I’m diving into AI, and I am running into trouble splitting different behaviors into different systems. I cannot use tagging; I’ve tried but I have too many units updating too frequently. Now I am just using a switch statement. But its ugly and I am left with a “God job” which processes all behaviors. Normally in OOP I would have different behaviors as classes, but we can’t do that in ECS.
I am working in an event system which helps alot with setting behaviors/reading player commands, but my problem is I don’t know how I can filter out different units with different behaviors when the time comes to process them. Using one job is an issue for me because as my list of behaviors grows, I am needing more and more component fields…
I also tried tagging “higher level” behaviors, and just polling sub-states within those, but many of these sub-states still use a large variety of components so it doesn’t really fix my problem. Any suggestions or threads welcome!
Here’s an example of what the job looks like right now.
[BurstCompile]
private struct MeleeUnitBehaviourJob : IJobEntityBatch
{
[ReadOnly] public float deltaTime;
[ReadOnly] public ComponentTypeHandle<Translation> translationHandle;
[ReadOnly] public ComponentTypeHandle<Velocity> velocityHandle;
[ReadOnly] public ComponentTypeHandle<TargetPosition> targetPositionHandle;
[ReadOnly] public ComponentTypeHandle<FormationForward> forwardHandle;
public ComponentTypeHandle<UnitSpeedData> speedHandle;
public ComponentTypeHandle<Acceleration> accelerationHandle;
public ComponentTypeHandle<Rotation> rotationHandle;
public ComponentTypeHandle<UnitAnimState> animationStateHandle; // dont need
public ComponentTypeHandle<UnitBehaviourState> behaviourStateHandle;
// target enemy
// enemy stats
// unit stats
// potentially many more
public void Execute( ArchetypeChunk batchInChunk , int batchIndex )
{
var batchTranslation = batchInChunk.GetNativeArray( translationHandle );
var batchVelocity = batchInChunk.GetNativeArray( velocityHandle );
var batchTargetPosition = batchInChunk.GetNativeArray( targetPositionHandle );
var batchForward = batchInChunk.GetNativeArray( forwardHandle );
var batchSpeed = batchInChunk.GetNativeArray( speedHandle );
var batchAcceleration = batchInChunk.GetNativeArray( accelerationHandle );
var batchRotation = batchInChunk.GetNativeArray( rotationHandle );
var batchAnimState = batchInChunk.GetNativeArray( animationStateHandle );
var batchBehaveState = batchInChunk.GetNativeArray( behaviourStateHandle );
for ( int i = 0; i < batchInChunk.Count; i++ )
{
var position = batchTranslation[ i ].Value;
var forward = batchForward[ i ].Value;
var target = batchTargetPosition[ i ].Value;
var velocity = batchVelocity[ i ].Value;
var rotation = batchRotation[ i ].Value;
var acceleration = batchAcceleration[ i ].Value;
var speed = batchSpeed[ i ];
var behaveState = batchBehaveState[ i ];
var animState = batchAnimState[ i ];
// many of these states rely on similar components in order to run, and I can have 100+ states, and there would be dozens of component type handles
switch ( behaveState.MainState )
{
case ( int ) UnitMainState.Form:
switch ( behaveState.SubState )
{
case ( int ) UnitFormState.Idle:
UnitBehaviourLogic.ProcessFormIdle( position , forward , target , ref rotation , ref speed , ref behaveState , ref animState );
break;
case ( int ) UnitFormState.Rotate:
UnitBehaviourLogic.ProcessFormRotate( deltaTime , position , forward , ref rotation , ref behaveState , ref animState );
break;
case ( int ) UnitFormState.Shift:
UnitBehaviourLogic.ProcessFormShift( position , target , ref acceleration , ref behaveState , ref animState );
break;
}
break;
case ( int ) UnitMainState.March:
switch ( behaveState.SubState )
{
case ( int ) UnitMarchState.Start:
UnitBehaviourLogic.ProcessMarchStart( position , target , velocity , ref acceleration , ref rotation , ref behaveState , ref animState );
break;
case ( int ) UnitMarchState.Rotate:
UnitBehaviourLogic.ProcessMarchRotate( deltaTime , position , target , ref rotation , ref behaveState , ref animState );
break;
case ( int ) UnitMarchState.SpeedUp:
UnitBehaviourLogic.ProcessMarchSpeedUp( deltaTime , position , target , speed , ref rotation , ref acceleration , ref behaveState , ref animState );
break;
case ( int ) UnitMarchState.March:
UnitBehaviourLogic.ProcessMarchMarch( deltaTime , position , target , speed , ref acceleration , ref rotation , ref behaveState , ref animState );
break;
case ( int ) UnitMarchState.SlowDown:
UnitBehaviourLogic.ProcessMarchSlowDown( velocity , position , target , ref acceleration , ref behaveState , ref animState );
break;
}
break;
}
batchRotation[ i ] = new Rotation { Value = rotation };
batchAcceleration[ i ] = new Acceleration { Value = acceleration };
batchSpeed[ i ] = speed;
batchBehaveState[ i ] = behaveState;
batchAnimState[ i ] = animState;
}
}
}