#Intended Transform workflow in 1.0.0pre65?

1 messages · Page 1 of 1 (latest)

wide orbit
#

I'm currently updating our project from pre8 to pre65 and implementing the changes to how transform data is handled on Entities. I'm trying to understand what the intended workflow is when it comes to determining world position data accurately.

It seems there's three different ways, all of which have serious issues:

  • Use LocalTransform values, which are updated in real time during simulation. This seems to be the intended way for setting position as well. Problem is this only works if the Entity has no Parent, so this is not viable when the Entities in our game can frequently be parented/unparented and we still need world position when they are parented.
  • LocalToWorld, which only updates after simulation, meaning it is not a viable method for getting positional data during the simulation phase. This is a major issue especially with newly spawned Entities as they'll have default LocalToWorld values unless we manually compute them on spawn. The official manual implies that this is only intended for rendering applications too.
  • ComputeWorldTransformMatrix() according to the manual this is the intended way to get lag-free, reliable positional data but the documentation on this is sparse to say the least. For one, the manual mistakenly lists this as part of LocalTransform when it is actually part of the Helpers class. There's also no usage examples I could find, and it's not very clear from the signature what exactly it expects for arguments. (If anyone can share a usage example of this method I'd appreciate that)

From what I can piece together, developers are intended to use this something like this?

  1. Use LocalTransform to read/write transform data while always making sure the Entities in question are not part of a hierarchy
  2. Use LocalToWorld for rendering only
  3. Use ComputeWorldTransformMatrix() to get transform data from anything within a hierarchy.

I have to say I find it baffling the API doc says getting accurate world-space transforms is "relatively uncommon". Almost all use cases for transforms in my experience require world, not local position. Certainly anything involving simulation does. Local space is only useful for positioning something in relation to its parent. So I find it strange that the Unity team decided to make local space the default and world space so cumbersome to access.

modest idol
bright tide
#

From what I can piece together, developers are intended to use this something like this?
Use LocalTransform to read/write transform data while always making sure the Entities in question are not part of a hierarchy
Use LocalToWorld for rendering only
Use ComputeWorldTransformMatrix() to get transform data from anything within a hierarchy.
Yes, that is the intended workflow.

#

Regarding the "relatively uncommon" expectation about accessing the world-space transform of child objects: this seems to be the sticking point most people have about using LocalTransform everywhere. In our experience, it hasn't been an issue (and we do periodically make games internally using DOTS). We've identified a handful of use cases where the world-space position of a child entity is needed, including:

  • casting rays into the world from a child entity, since the ray-cast origin/direction must be in world-space (e.g. from the tires of a car to the terrain)
  • spawning new entities at a specific point in world-space (e.g. bullets from the end of a rotating turret on a moving vehicle)
#

In these cases, you can either read the value of LocalToWorld (fast, but potentially inaccurate for a number of reasons), or use ComputeWorldTransformMatrix() (accurate at any given moment in time, but slower since it has to walk the hierarchy all the way up to the root and effectively compose the local-to-world matrix on demand). But either way, we don't expect this to be the common, thousands-of-times-every-single-frame use case.

#

@wide orbit it sounds like you disagree:

the Entities in our game can frequently be parented/unparented and we still need world position when they are parented.
What's the use case you have where the world-space position is frequently needed for child entities?

#

For one, the manual mistakenly lists this as part of LocalTransform when it is actually part of the Helpers class.
It was original a helper on LocalTransform, before we moved it to the transform helpers (which didn't exist at the time). Sorry about that. Where do you see the incorrect info in the manual? I see a changelog entry, but all other references in comments & manual content look correct in the DOTS repository.

#

There's also no usage examples I could find, and it's not very clear from the signature what exactly it expects for arguments. (If anyone can share a usage example of this method I'd appreciate that)
The Netcode racing sample should be using it, but I'm not sure if that's been published yet (yes, I agree that it's confusing when updated DOTS samples aren't published near-simultaneously with a new package release; I've already brought this up with the release team)

Failing that, the only usage example I'm aware of is the unit test for this method, in TransformHelperTests.cs in the Entities package.

wide orbit
# bright tide > From what I can piece together, developers are intended to use this something ...

Okay, thanks for confirming.

Regarding use cases, for example our game is a management sim featuring many autonomous AI agents. There's a storage system where individual items can be stored in containers, which can be either static (boxes, wardrobes, etc.) or moveable (backpacks, vehicles). The agents query the position of items of certain types to find the closest one to target, which means we need access to their world position. Now it's certainly possible to say, set up a separate system to sync the position of stored items with their container, but this seems like exactly the sort of thing the parent-child system is meant to handle, and duplicating the code is clunky.

I see a changelog entry, but all other references in comments & manual content look correct in the DOTS repository.
Yeah, when I went to update I looked through the changelog and that's where I saw the incorrect reference. I figured it might've gotten moved and the doc not updated, if so fair enough.

bright tide
#

We definitely need to put more effort into cleaning up the changelog before each release; it's kind of the wild west right now 🙂

wide orbit
#

Fair, writing docs is a grindy (though indispensable) part of development unfortunately

bright tide
#

We get to add a changelog entry for every change we make, but if a subsequent change invalidates an earlier one, there's no easy way to go remove/edit the previous changelog entry.
Or, if we make a change in our main branch (+ changelog entry) and then backport that change to an existing release branch, the release branch gets the changelog entry as well, but the original entry is still in main, and will be picked up by the next release branched off from main as well. So a changelog entry may appear twice?
Anyway, I digress. Solve Hard Problems 🙂

#

Back to transforms. So, items are stored in containers, and agents need to find the nearest object of a certain type. And I assume it's not sufficient to just look for the nearest container with the type of item they want; they need to find the location of the actual item within that container?

wide orbit
#

That'd be one way to do it, but then we'd have to iterate through a DynamicBuffer for each container to look for matches, which is slower and more complex of an operation than simply using an EntityQuery

#

The local position in this example doesn't actually matter, since items within a container are all placed at the zero position and have their rendering disabled. It's mainly a matter of finding that there's a matching item at the location of the container

#

Basically the ideal scenario for us would be to not even look at the containers, simply run an EntityQuery for matching items, get their transforms, then run a job to sort them by distance so we can pick the closest for our target

bright tide
#

Ah, so the child objects aren't even rendered. So, in terms of transforms, it really is the container's position that you care about in terms of the "find closest match" test; it's just that the container doesn't "know" what's in it, so you iterate over the individual items using a query to find the best match, and use their world-space position (= the container's position) to sort by distance. Is that more or less accurate?

wide orbit
#

Yes

bright tide
#

Okay. Are items always in a container when agents are searching for them, or would they ever just be out in the world somewhere?

wide orbit
#

Items can be either out in the world or in storage. For example an agent assigned to wood chopping could cut down a tree, spawning a pile of wood. That wood may be left where it spawned, or it may be carried off to a storage container by another agent assigned to haul resources.

bright tide
#

Got it. Are the containers themselves always in world-space, whether they're static or moving?

#

I guess a backpack might be parented to some other moving character, so I imagine not?

#

Also, are the objects of a given type within a container fungible? Like, does an agent particular care which piece of wood it pulls out of a container, or does it just need to find the nearest container containing one or more pieces of wood?

wide orbit
#

The containers can be nested, for example an agent can have their own inventory, then get into a vehicle which can have an inventory all of its own

#

does an agent particular care which piece of wood it pulls out of a container
No, it's any entity that's tagged as being wood basically

bright tide
#

Okay; that last point would lead me towards a solution where a container just keeps a count of how many objects it contains of each type. And when searching, you'd iterate over containers rather than individual items. But I suppose the pathological case for that approach would be thousands of empty containers & only one piece of wood, and you'd have to look in every container to find it.
Maybe it would make sense to keep a global list per-item-type of which containers contain at least one instance of that item? Adding/removing items to containers sounds relatively uncommon (there's that phrase again -- when I use it, I mean "not something that 90% of all relevant entities are doing every frame" or something like that). Adding the first / removing the last instance of an item to a container would be even less common, and that's the only case where you'd need to update the global container lists. That would give you a much smaller set of items to search in most cases, which further mitigates any cost of looking up / computing world-space transforms.

#

And I assume most containers are either totally stationary or relatively slow-moving -- they're not teleporting or constantly spawning/despawning, for the most part? In that case, using LocalToWorld for the container's world-space positions is probably just fine.
Or ComputeWorldTransformMatrix(); the hierarchies don't sound super deep. My concern with the latter approach would be that if multiple agents are searching for items in a given frame, you could end up computing the same object's world-space transform redundantly, and it might make sense to cache it somewhere.

#

In any case, I would definitely not advise implementing containers as transform hierarchies of individual unmoving, invisible entities for each resource. In that approach, you're possibly wasting a ton of time each frame recomputing LocalToWorld matrices that will never be read (on items in moving containers that nobody's searching for). Or computing an identical LocalToWorld for hundreds of piles of wood in the same container, and searching through all of them to figure out which is the closest.

wide orbit
#

Hhm, that's actually a good point, I hadn't considered the overhead of computing LocalToWorld on stored Entities. It may be better to implement some sort of GetWorldPosition function that takes into account whether an Entity is stored or not, then returns the container's position instead.

#

Something to look into on Monday, just about time for me to clock out. Thanks for the advice and have a nice weekend!

bright tide
#

My pleasure; bon weekend! Feel free to report back here if you try one of those approaches and run into issuesI haven't foreseen, after thinking about the problem for all of twenty minutes 🙂 (Though I'm about to leave for a few weeks of vacation, and may not get back to you until mid-April)

brazen dust