First of all, I found out that I have posted this issue in the wrong forum, and I’m re-posting it here because I don’t know how to move or delete threads (help needed). .
The original thread:
My understanding of entity queries is that they are the same as asking for Components in a job like:
var someQuery = GetEntityQuery(typeof(SomeComponent));
var componentData = someQuery.ToComponentDataArray<SomeComponent>(Allocator.TempJob);
would be enough to let me edit componentData as a component.
But when I try to this, I initially get the data I want when the system starts, but then, the initial data does not update to the current one, for example: a position data does not update since it’s initial query.
I have a system for rotating entities towards the player, my system looks something similar to:
public class RotationTowardsPlayerJobSystem : JobComponentSystem {
[BurstCompile]
[RequireComponentTag(typeof(TurnTowardsPlayer)]
private struct RotationTowardsPlayerJob : IJobForEach<Translation, Rotation> {
public Translation PlayerTranslation;
public void Execute([ReadOnly] ref Translation translation, ref Rotation rotation){
var entityToPlayer = (PlayerTranslation.Value - translation.Value);
entityToPlayer.y = 0;
entityToPlayer = math.normalize(entityToPlayer);
rotation.Value = quaternion.LookRotation(entityToPlayer, new float3(0, 1, 0));
}
}
private EntityQuery _playerQuery;
protected override void OnCreate() {
_playerQuery = GetEntityQuery(ComponentType.ReadOnly<PlayerTag>(), ComponentType.ReadOnly<Translation>());
base.OnCreate();
}
protected override JobHandle OnUpdate(JobHandle inputDependencies) {
var playerPositions = _playerQuery.ToComponentDataArray<Translation>(Allocator.TempJob);
// playerPositions array never updates, and only has one Translation component in it, the player's Translation, but it doesn't update when I move the player around!
// there's only one player in the scene
var jobHandle = new RotationTowardsPlayerJob {
PlayerTranslation = playerPositions[0]
}.Schedule(this, inputDependencies);
playerPositions.Dispose();
return jobHandle;
}
}
I don’t want to modify the player’s position. I just want to know where the player is.
There’s no method AsComponentDataArray in EntityQuery.
After research, I think my problem is that my player is converted to an entity by ConvertToEntity, with Convert and Inject GameObject selected. Which creates the entity in place, but never updates it’s data according to the actual player’s GameObject.
This presents another problem, as the movement of my player character is done through animation and blend trees, and setting the blend tree states (float, bool…etc) requires a reference to Animator, which is a behavior component, which also means I can’t access it from systems -yet- unless I create a static reference to the player character and its Animator, which is not ideal.
Is there any workaround to get animations working with ECS? or am I stuck with the static reference?
AsNativeArray (DynamicBuffer.AsNativeArray, NativeList.AsNativeArray)
Reinterprets the data to be a native array. Changes to the array will affect the original.
ToNativeArray (EntityQuery.ToNativeArray)
Makes a new array and copies the data, changes to the array will not affect the original
Sorry doesn’t really help your query, just wanted to make a note of it.
You can use classic Component types in ECS queries. You just can’t pass references to them to jobs. So you can create a ComponentSystem that accesses animator and collects the data, and then use JobComponentSystem(s) to process it, then use another ComponentSystem to write back to Animatior if needed.
My input framework talks to the InputManger and PlayerInput components from the new (but still OOP-based) Input system this way.
I have a ComponentSystem that loops through the active players MonoBehaviours, then flushes the action trace buffers into various struct buckets, which are then processed in Jobified threads.
There’s various components to write Transform data either from the classic transform → ECS transform or the other way around.
From my understanding, you would read input from player, then find the player character entity, add some tag to the player to fire up the animation system, then get the Animator component in the system, and after finishing the desired animation, remove the tag.
But isn’t that not performant? as it changes the archetype of the player character so often?..and how would you handle continuous input using this method? do you keep repeating the method until player stops input? or do you create several system for each case?
Adding/Removing tags should get more performant over the next few ECS releases. It’s definitely not something you want to do every frame if you can avoid it, but for playing an animation then removing the tag, it shouldn’t be too bad if the animations aren’t super-short and frequently alternating.
One alternative you could have is an ID component data for different animations, and map those to the different animations as well as a value like -1 for “not animating”. Then you don’t have archetype changes as frequently. You can also handle continuous or chains of animations simply by changing the ID, which can trigger the updates. You can throttle this with [ReadOnly] or checks in the ComponentSystem, whichever works best for your use case.
You could then map that ID to a converted table of values to apply to the animator when a change occurs.
So your settings data table entries would look like this (pseudo-code):
AnimationSettings:
{
AnimID: N
BoolValue[ ] boolValues[ ];
TriggerValues[ ] triggerValues;
FloatValue[ ] floatValues;
IntValue[ ] intValues;
}
BoolValue
{
int BoolParamHash;
bool Value;
}
TriggerValue
{
int TriggerParamHash;
}
// and so on and so forth.
Now the interesting thing is how you would want to store this, as there are several options.
Store it in the managed realm and have it exist on a GO component that’s also injected into the entity side.
Store it in an entity that has DynamicBuffers of the various settings, and is referred to by all converted entity instances that use the same animator controller.
Store it in AssetBlobs, which are relatively new and I’ve just been messing with them myself.
Now for settings you want to apply continously and extract from ECS code, you may want to do something similar to the above, where you have a DynamicBuffer of ParameterHash+ParameterValue structs your jobs update (scan for the value with the matching ParameterID of the correct type) and a ComponentSystem reads out and applies to the Animator. If the value doesn’t change, it’s not applied to the animator.
If you have no input event components, then the systems shouldn’t run. You can use RequireForUpdate() and RequireForUpdateSingleton() to make a system not run unless entities with a certain data type exist.
I’ve done this quite a bit in my game’s framework for config singletons and event response systems.
You will probably need to create a few systems for different purposes to update the animator with, as if you frequently update Animator ints and floats, this way everything’s a bit more granular.
There is a new pure ECS Animation system that’s supposed to preview later this year (closer to the existing workflow, not the baked one @Joachim_Ante_1 made), but I don’t know if/when that’s going to drop nor the state it’ll be in when it does drop.
The main thing you want to think about is the shape of the data you’re trying to send to the animator and what data access is most relevant. It’s very possible to use Hybrid game code at this point, and in some cases, we pretty much have to in order to work with the more mature systems Unity has to offer.