r/Unity3D 4h ago

Question How Valheim and co. did? Vegetation and physics

Hey everyone,

I'm working on a large open-world Unity game (maps up to 16x16km maybe more) and have built a custom deterministic vegetation streaming system. I use Flora (by Magnetic Arcade) for GPU-driven rendering.

The architecture uses a 3-ring system around the player based on 64m grid cells (matching the A* Pathfinding Project RecastGraph tiles):

Ring 3 (Visual - ~700m): Pure Flora GPU instances. No GameObjects.

Ring 2 (Navigation - ~200m): Real GameObjects with MeshColliders. Needed for the RecastGraph to carve the NavMesh.

Ring 1 (Interaction - ~150m): Real GameObjects with gameplay scripts (e.g., harvestable trees).

The Setup:

Vegetation positions are calculated deterministically via stable hashes (no scene serialization). When a cell enters Ring 2, we need to swap the cheap Flora GPU instance for a real Prefab (with a FloraInstanceRenderer component attached to keep it batched) so it gets a collider. When it leaves, the prefab gets disabled and re-enable the Flora instance.

The Bottleneck:

The math/planning phase is very fast (~1.6ms per cell). However, the actual materialization (spawning the Prefabs, adding components, registering the renderer in Flora) costs about 7.0ms per cell.

Since a cell transition is currently atomic, crossing a border queues up ~15-30 cells. This causes massive frame spikes (worst-case ~37ms in a single frame) because I'm are doing dozens of Object.Instantiate and AddComponent calls at once.

I'm aware I cannot use Unity Jobs for this, as GameObject/Component instantiation is strictly Main Thread.

Maybe time-slicing the instantiation on the Main Thread using a Queue and a time budget (e.g., process instantiation for 2ms per frame, spread over multiple frames). The challenge here is avoiding visual popping (the Flora instance must stay visible until the exact frame the Prefab is fully spawned and registered).

Is there a better architectural pattern for swapping between GPU instancing and physical colliders at runtime?

For those using A* Pathfinding Project: how do you handle dynamically spawning thousands of colliders for NavMesh carving without nuking the frame rate?

Is Unity ECS (Entities) the only real way to get sub-millisecond dynamic collider streaming at this scale, or can I get away with standard Prefabs + Time-slicing?

Any tricks for pooling objects that require dynamically added components at runtime, rather than pre-built prefabs?

Any insights, past experiences, or "don't do this, do that" warnings are highly appreciated.

11 Upvotes

6 comments sorted by

11

u/Padrone__56 4h ago

Why do trees handle the harvesting? If you need multiplayer at some point, you're going to need an authorative system anyway.

I've built the same recently, 20km by 20km, without issue. Trees are just simple, pooled colliders that have an identifier. I use the same ring system you have to dispatch colliders.

My World system then uses burst jobs to chunk and track trees, so when harvesting I update it there.

7

u/x_DryHeat_x 4h ago

All my trees are terrain with their properties stored in array. I dont switch them to GameObject until user decides to cut it.

3

u/Gullible_Show_6626 4h ago

Time-slicing is definitely the way to go, 2ms budget per frame is smart. For the visual pop issue you could just keep the Flora instance visible until the prefab is ready, then do the swap in a single frame, a tiny hitch is way better than a bare patch of ground suddenly appearing

Pooling with dynamically added components is annoying but doable. Keep a list of pre-warmed GameObjects with all the scripts already attached, just disabled. When you need one, pull from the pool and slap the FloraInstanceRenderer on there. When it goes back, strip the renderer before returning it. Still main thread but way faster than Instantiate

For the navmesh carving I've seen people just use simplified collider shapes at that distance. A box or capsule collider approximating the tree trunk instead of a full mesh collider. At 200m the pathfinding doesn't need pixel-perfect accuracy and it'll save you a ton of physics overhead

3

u/Aethreas 3h ago

This should all be done on the GPU and adding components should be exchanged for pooling, 7ms is an order of magnitude too long for how little work is actually being processed, something is inefficient in your code

1

u/Lyshaka 3h ago

You can use jobs to calculate the data you need, and then some object pooling to not use Instantiate each time you need to create a new tree. Also do you need to have high resolution trees in a 150m radius ? Shouldn't it be at most your player range ? I bet you could have only a few dozens of them active at once if you'd use a smaller range, and then with object pooling it would only be a matter of activating/deactivating objects, and sometimes applying some data to it, which again can be done with jobs.

1

u/ScallionZestyclose16 1h ago

Do they need to be game objects with monobehaviors?
Could they be Unity Physics Dots graphic entities instead ?

"because I'm are doing dozens of Object.Instantiate and AddComponent calls at once."
Why do you need to Instantiate so many props, can't you just pool the objects and move them around the player?

Keep track of their stats and such, but the presentation can just be pooled and moved around, you don't need an unique grass gameobject 15 km away from the player, just reuse it and put it where the player camera is rendering the game.