I should preface that I’m completely new to NetCode for ECS and Entities in general so if you notice any flaws in my logic then please let me know!
I’m wondering if there is an efficient way to check whether a child entity belongs to a parent entity that contains the GhostOwnerIsLocal tag without having to traverse up the heirarchy at runtime everytime it needs to be validated?
I’m aware that frequently adding/removing components especially to an entity that holds 100s of sub-entities (skeleton transforms) would result in a performance loss due to restructuring so that seems to be out of the equation. But since my system is already referencing a managed component (i.e., the Camera) it’s already slow from the fact that it’s running on the main thread (cannot use Burst).
What I’m trying to achieve (which works by traversing up until I’ve reached parent entity and checking if the GhostOwnerIsLocal component is present):
Client side system that updates the position of the MainCamera in the scene to the sub-entities’ position in world space. Rationale behind it is that if I need to update the skeleton for animations then the camera’s position/rotation would be driven by it. I’m not sure if this is the best approach or if there is a better way for this as well but that’s the solution I came up with for ECS.
From a child entity to reach the root there is no shortcut. You must traverse the parent hierarchy up using the Parent component.
If your need is only to know the root, you can cache this information at baking time or once at runtime (up to you) in another component of yours.
For calculating the world position there is no other way that traverse the hierarchy bottom up and the up to bottom and calculate for each parent the local to world matrix (that takes into account rotation etc etc).
There is an helper method in TransformHelpers for that.
Question: do you really need to calculate the world position by yourself ? If you need this only for the camera (as in your example), if you run the system after the TransformSystemGroup
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
[UpdateAfter(typeof(TransformSystemGroup))]
public partial class PlayerCameraSystem : SystemBase {
you don’t need to do any calculations. All child entities world position are already up to date and you should use the LocalToWorld matrix to get the current position in world space.
Thanks for the tip! After reading up on [UpdateAfter(typeof(TransformSystemGroup))] it seems like it’s exactly what I need. I did try to use LocalToWorld matrix to get the position in world space but it seems to be missing one of entities in the hierarchy? I wasn’t sure if this was correct hence calculating it myself manually…
My hierarchy consists of:
Player (LocalTransform.Position: 0, 1.135, 0) (Is dynamic based on user input but for the sake of example)
→ Body (mesh) LocalTransform.Position: 0, 0, 0)
----> Head (mesh) (LocalTransform.Position: 0, 0.5, 0.25)
------> DesiredCameraPosition (LocalTransform.Position: 0, 0, 0.5)
The LocalToWorld of DesiredCameraPosition would give me (x: 0, y: 1.635, z: 0.5) but based on the numbers above shouldn’t it be (x: 0, y: 1.635, z: 0.75)?
The LocalTransform is as the component say only the local position of entities in respect to the parent, not the world position.
In order to know the world position you should concatenate all the transform in the hierarchy (in this case it is a simple addition, with more transformation are matrices multiplication).
This is what systems in the TransformSystemGroup does. All entities that have a LocalToWorld component will get the matrix calculated.
Because you added a tag to the entity you are interested I suppose you add a baker to it and that you required the entity to either using TransformFlag.Dynamic or TransformFlag.Renderable or any flag combination that will add a LocalToWorld matrix.
In order to filter and set the camera only for the Entity that player is controlling (that is the root) the better way is to use a an idiomatic for-each that loop over the entity that has the GhostOwnerIsLocal flag active.
Then, for that entity, fetch the children with the camera tag. For this last task, the fastest is to bake that entity reference when you author the prefab in a component on the root.
If I modify the DesiredCameraPosition entity in the scene while in play mode it seems that the LocalToWorld values are only adding HALF of what the position values of the local transform are to the LocalToWorld transform, specifically for the DesiredCameraPosition entity. This doesn’t seem to be expected behaviour based on what your explanation of what LocalToWorld matrix is calculating. Am I missing something that would give me this outcome?
For ghosts, if the DesiredCameraPosition is not replicated (it should be not), changing the value of the entity before the TransformSystemGroup should recalculate all the world positions.
Notice that scale of objects affects the world position. So if you have any entity in your hierarchy that has a scale != 1, expect this entity position not have the exact difference that you expect.