Struggling to efficiently move 1000 assemblies

I am currently working on a zombie system with goal of achieving 1000 zombies at 144+ FPS. I have optimized their behavior, and replication, but got stuck with movement as I am sitting at 45 FPS.

I use BulkMoveTo for movement, it takes ~6 ms a frame, which is excessive, but the engine also has notifyVisuallyMovingPrimitives taking up ~3 ms of additional frame time. Rendering itself takes ~6 ms. To reach 144 FPS, I got ~7 ms to finish a single frame, I am currently way above that.



Movement code boils down to this snippet:

local parts: {BasePart} = {}
local cframes: {CFrame} = {}

local function onUpdate()
	table.clear(parts)
	table.clear(cframes)

	local index = 0
	for _, zombie in zombies do
		if shouldUpdate(zombie) then
			index += 1
			parts[index], cframes[index] = zombie.Root, zombie.CFrame
		end
	end

	workspace:BulkMoveTo(parts, cframes, Enum.BulkMoveMode.FireCFrameChanged)
end

The shouldUpdate function is used to throttle updates to reduce the number of zombies that are updated per frame, it takes distance and visibility into account.

Before you reply:

  1. What is notifyVisuallyMovingPrimitives, and what can be done to lower its frame time?
  2. What can be done to lower BulkMoveTo frame time?
  3. Are there any other optimizations I can employ without reducing the zombie count?
1 Like

What if you remove all joints except HumanoidRootPart and mesh with skinned bones? :thinking:

1 Like

I know nothing about using Blender, let alone creating a skinned mesh rig, but I did find S1 (single part skinned mesh R15 rig) online. BulkMoveTo now takes 2.5 ms on average. Overall, got decent FPS boost but with lots of stutter, and draw calls went up from 300 to over 1000.

1 Like

Consider moving them with Motor6D.Transform, which is said to defer its work to the same parallel thread that deals with character animations (source).
Unanchor the root, add a Motor6D between Terrain and the root, never set CFrame.

2 Likes

I had this in mind, but got carried away thinking about BulkMoveTo. Using Transform property resulted in ~6 ms dropping down to ~1 ms for movement code. Now the only major contributors are applyJointTransforms and notifyVisuallyMovingPrimitives, both which take up ~2 ms each; ~4.5 ms combined.

Timing:

  • Simulation: ~6 ms (~4.5 ms is coming from the engine)
  • Rendering: ~4.5 ms
  • Sleeping: ~2.5 ms

In total ~13 ms, with the goal being ~7 ms. I need to shave off 6 ms.

2 Likes

I came to conclusion that 3 things helped performance a lot:

  • Play testing using a local server (prevents server from affecting client performance, which caused stutters for me).
  • Using Motor6D to move the assemblies (more efficient than BulkMoveTo) as suggested by @Eestlane771 .
  • Using a single part skinned mesh rig instead of multi-part rig (lowers both simulation and render time) as suggested by @Yarik_superpro.

As of result, I was able to achieve 180 FPS in studio.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.