Disabling tick on actors with SkeletalMeshComponents also disables LOD transitions on the mesh

In my game I aggressively disable ticking on actors that are not in the immediate player radius.

However I’ve just noticed that LODs on SkelMeshes don’t transition when the tick is disabled on it’s actor. This is not the case for static mesh components, they transition fine with no ticking.

Does anyone know if it is possible to have the LODs still transition when the owning actor is not ticking?

I disable tick too, but when the pawns are far and hidden, so I haven’t noticed the lack of LOD.

You can use Pawn.Mesh.ForcedLodModel = 4; before disable tick to force the transition (and for the components too).

Unfortunately that won’t help my case. I disable ticking on actors even when they are very close to the player. For instance for building pieces of player bases. Then i wake them up for certain events like taking damage or animations (like doors opening etc) before putting them back to sleep.

So what can occur is the tick gets disabled when a player is far (so lowest detail LOD is enabled), then they can walk right up on the mesh with it still in low detail, and it won’t swap to the high-detail unless one of the the “waking” events happen.

This can also happen the other way, whereby high-detail LODs get pause by the tick disabling, and then when the player moves far away, it’s stuck rendering the high detail mesh.

I guess i can run regular radius checks from the player view and force no-ticking actors to update their LOD, but that just seems gross…

Yes, I want to try to disable ticks and hide pawns (and even force lod3) when they are doing nothing and they are near but at a certain distance and behind the camera. That implies doing checks at every tick and maybe it’s not a good deal.

You’re probably going to have to check distance manually. If you want to speed that up, then instead of iterating through AllActors, and doing lots of costly square roots, keep a separate array of just the affected actors and check them with a VSizeSq. And you don’t have to do it every tick.

Do you know if VSizeSq() is any more efficient than VSize()?

You definitely don’t want to be doing distance checks on every tick if you can avoid it (at least on actors where you have 10’s of thousands spawned on the map, like in my game).

One thing do a lot of for my non-ticking actors is use a single global ticking actor (which I keep a ref to in my extended MapInfo, so is available easily via WorldInfo.GetMapInfo()). Then use this to create timers on my non ticking actors, as you can pass an object to the SetTimer/ClearTimer/etc methods for the object/actor that it should callback to. Also means you can use timers on objects too (rather than just actors).

Well, I was going to say that yes, VSizeSq is more efficient than VSize, because VSize does a square root, which I thought was relatively costly. But I decided to test it:

exec function TestSqrt()
{
	local int i, Reps;
	local vector RandVect;
	local int Year, Month, DayOfWeek, Day, Hour, Min, Sec, MSec, StartTime, EndTime, ElapsedTime;
	local float VectSize;

	Reps = 100000;
	GetSystemTime(Year, Month, DayOfWeek, Day, Hour, Min, Sec, MSec);
	StartTime = (Min * 60000) + (Sec * 1000) + MSec;
	for(i=0; i<Reps; i++)
	{
		RandVect.X = FRand() * 100;
		RandVect.Y = FRand() * 100;
		RandVect.Z = FRand() * 100;
		VectSize = VSize(RandVect);
	}
	GetSystemTime(Year, Month, DayOfWeek, Day, Hour, Min, Sec, MSec);
	EndTime = (Min * 60000) + (Sec * 1000) + MSec;
	ElapsedTime = EndTime - StartTime;
	Log(self $ ".TestSqrt(): Elapsed time for " $ i $ "x VSize(): " $ ElapsedTime $ " msec");

	GetSystemTime(Year, Month, DayOfWeek, Day, Hour, Min, Sec, MSec);
	StartTime = (Min * 60000) + (Sec * 1000) + MSec;
	for(i=0; i<Reps; i++)
	{
		RandVect.X = FRand() * 100;
		RandVect.Y = FRand() * 100;
		RandVect.Z = FRand() * 100;
		VectSize = VSizeSq(RandVect);
	}
	GetSystemTime(Year, Month, DayOfWeek, Day, Hour, Min, Sec, MSec);
	EndTime = (Min * 60000) + (Sec * 1000) + MSec;
	ElapsedTime = EndTime - StartTime;
	Log(self $ ".TestSqrt(): Elapsed time for " $ i $ "x VSizeSq(): " $ ElapsedTime $ " msec");
}

And on an i7-12700k, neither loop takes more than about 8 or 9 milliseconds, and they take about the same amount of time. So on a new processor, VSize isn’t a problem and there doesn’t seem to be any difference in performance. Maybe there’s a difference on older processors though.

Anyone got some old hardware they want to try that on?

Interesting, thanks for sharing. I had assumed the difference would be minimal if at all.

Btw, for testing execution time, there are some native functions in Unrealscript for this:

function TestSomething()
{
    local float execT;

    Clock(execT);
    //Do something
    UnClock(execT);

    `log("Execution time:"@execT);
}
3 Likes