I currently have an entity in the following fashion: “Data” entity → CenterBottom component → “CenterBottom” entity. The “Data” entity holds all game logic components while components in the style of CenterBottom serve as locatable markers in the game world. I am trying to update the CenterBottom entity’s LocalToWorld in one of my jobs. I structured the code so that the system with the job runs during the FixedUpdate loop. Some debugging proves that the LocalToWorld is indeed being updated. However, once the Update event loop takes place, where the Simulation system group runs, a job runs that causes the LocalToWorld to revert to its previous value. An example of debugging output is:
Before: (1,1)
After(2,2)
Before: (2,2)
After(3,3)
Before: (3,3)
After(4,4)
New frame
Before: (1,1)
After(2,2)
New frame
Before: (1,1)
After: (2,2)
Before: (2,2)
After: (3,3)
I suspect the offending job is CopyTransformFromGameObject.
As I understand it, this is intentional, and documented. The transform systems take the translation, rotation and scale components, or any combination that are present, and convert the values in a LocalToWorld value. They don’t care what value was already there. If you update the Transform, Rotation and/or Scale components, then the change will picked up and reflected in LocalToWorld next time the transform system/s run.
Thank you for the answer. How can I update the Transform from ECS? Updating Translation makes it worse since then the LocalToWorld is not updated at all even between FixedUpdates
So you have your simulation group running in Fixed Update. This is where you want to run your ECS logic in most cases. The you have your presentation group running in Update. (I think p31 moved simulation to update temporarily while they sort something out).
For most use cases, updating Translation, Rotation and Scale components in the simulation group is probably fine. Access the components just like you access the LocalToWorld component. Try this first and see if you’re happy. My understanding is the intent is for the presentation group to run smoothly at 60fps (update) while the sim group runs less often at your fixed update time step. There may or may not be LocalToWorld smoothing involved, I’m not sure on this myself.
Here are the docs on the transform system which should detail most of this.
https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/transform_system.html
Also, the system update order to see where things are happening.
https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/system_update_order.html
You need to set your own component to [WriteGroup(typeof(LocalToWorld))] if you want to override CopyTransformFromGameObjectSystem.
https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/ecs_write_groups.html
I followed your advice of updating the relevant Translation component, and using the Entity Debugger I see that the Translation component was updated to the correct value. However, its value is not being copied over to LocalToWorld, whose value remained what it was before the job was run. Here is the job:
private struct CalculateNextMoveJob : IJobForEachWithEntity<CenterBottom, Grounded>
{
//[ReadOnly]
[NativeDisableParallelForRestriction]
public BufferFromEntity<TraverseWaypointElement> waypointsFromEntity;
[NativeDisableParallelForRestriction]
public ComponentDataFromEntity<Translation> translationFromEntity;
public EntityCommandBuffer.Concurrent ecb;
[ReadOnly]
public CollisionWorld world;
public int topOfWorld;
public int bottomOfWorld;
public LayerMask surfaceMask;
//[WriteOnly]
//public NativeArray<int3> newPositions;
public void Execute(
Entity entity,
int index,
[ReadOnly] ref CenterBottom centerBottom,
[ReadOnly] ref Grounded grounded
)
{
var waypoints = waypointsFromEntity[entity];
var waypointsLength = waypoints.Length;
if(waypointsLength == 0)
{
ecb.RemoveComponent<GroundedStatus>(index, entity);
ecb.AddComponent(index, entity, new TraverseSuccessMessage());
return;
}
var speed = grounded.speed;
var centerBottomEntity = centerBottom.bottom;
var currentPosition = translationFromEntity[centerBottomEntity].Value;
Debug.Log("start: " + translationFromEntity[centerBottomEntity].Value);
var latestWaypoint = waypoints[waypointsLength - 1].waypoint;
var wayPointDirection = latestWaypoint - currentPosition.xz;
var waypointAngle = math.atan(wayPointDirection.y / wayPointDirection.x);
var newX = (int)((math.cos(waypointAngle) * speed) + currentPosition.x);
var newZ = (int)((math.sin(waypointAngle) * speed) + currentPosition.z);
var newLocation = new float2(newX, newZ);
var newY = SurfaceAt(newLocation, topOfWorld,bottomOfWorld,surfaceMask, world).y;
var newPosition = new float3(newX, newY, newZ);
var twoDDistance = (int) math.sqrt(
math.pow(math.abs(newPosition.x - currentPosition.x), 2) +
math.pow(math.abs(newPosition.y - currentPosition.y), 2)
);
var hypotenuse = newPosition - currentPosition;
var unitHypotenuse = hypotenuse.Normalize();
var scaledUnitHypotenuse = unitHypotenuse * twoDDistance;
var finalPosition = (scaledUnitHypotenuse + currentPosition);
var finalPositionGrounded =
new float3(
finalPosition.x,
SurfaceAt(finalPosition.xz, topOfWorld, bottomOfWorld, surfaceMask, world).y,
finalPosition.z
);
// check if the mover stepped over the waypoint
var overWaypoint =
CrossedWaypoint(currentPosition.x, finalPosition.x, latestWaypoint.x)
|| CrossedWaypoint(currentPosition.z, finalPosition.z, latestWaypoint.y);
if (overWaypoint)
{
waypoints.RemoveAt(waypoints.Length - 1);
finalPositionGrounded = SurfaceAt(latestWaypoint, topOfWorld, bottomOfWorld, surfaceMask, world);
}
var translation = translationFromEntity[centerBottomEntity];
translation.Value = finalPositionGrounded;
translationFromEntity[centerBottomEntity] = translation;
Debug.Log("end: " + translationFromEntity[centerBottomEntity].Value);
}
}
It basically calculates finalPositionGrounded and sets Translation to that
Bump? I’ve searched through the forums and tried the following:
- Updating after TransformSystemGroup
- Updating inside TransformSystemGroup and before CopyTransformToGameObject and after CopyTransformFromGameObject
- Writing only to Translation Component
- Moving everything from FixedUpdate to SimulationGroup
None of these worked. Any suggestions on what may be wrong?
Whats the archetype of the entity
The relevant components are:
- CopyTransform[To/From]GameObject
- LocalToWorld
- MeshFilter
- MeshRenderer
- Rotation
- Transform
- Translation
Have you tried without CopyTransformFromGameObject beacuse you’re probably overriding your transform
That fixed it. I had a component called “DraggableComponent” that adds a CopyTransformFromGameObject so that dragging the GO would update the ECS side, so I removed it. Now the LocalToWorld is updating when Translation updates, but the mesh rendered in MonoBehaviour land isn’t moving with LocalToWorld. May I ask how to fix that?
Do you have a CopyTransformToGameObject component on it?
Yes. Here’s the list of components:
- RenderMesh
- CopyTransformToGameObject
- WorldRenderBounds
- RenderBounds
- Rotation
- Translation
- LocalToWorld
I would also like to add that the entity is not spawned; rather, it’s already in the scene
Sorry for wasting y’all’s time. The issue was that I had this:
using UnityEngine;
using System.Collections;
using Unity.Entities;
using Unity.Transforms;
[RequiresEntityConversion]
public class CopyTransformToGameObjectComponent : MonoBehaviour , IConvertGameObjectToEntity
{
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
dstManager.AddComponentData(entity, new CopyTransformToGameObject());
}
}
// This is the result of copy, pasting, and modifying boilerplate code
public struct CopyTransformToGameObject : IComponentData
{
}
I removed the struct and now the Authoring adds the correct CopyTransformToGameObject