I created a custom SystemGroup for the background simulation of my game. I set the Rate manager to RateManager = new RateUtils.FixedRateCatchUpManager(0.2f); which as I understand it means the system should run every 0.2 seconds/5 times a second. But when I run the system with a single entity it seemingly running once per frame rather than based on a time interval. I’m not sure what’s going wrong as everything I’ve read online has said it should be as simple as making the custom system group, setting it’s rate manager and making sure than any system you want to run at that rate is put in that system group, which I double checked both in my code and in the systems window in the editor and it’s definitely in the right system group.
System Group:
[UpdateInGroup(typeof(SimulationSystemGroup), OrderFirst = true)]
public partial class BackgroundSimulationSystemGroup : ComponentSystemGroup {
public void OnCreate(ref SystemState state) {
RateManager = new RateUtils.FixedRateCatchUpManager(0.2f);
}
}
System:
[UpdateInGroup(typeof(SystemGroups.BackgroundSimulationSystemGroup))]
public partial struct UpdateNavPaths : ISystem {
[BurstCompile]
public void OnCreate(ref SystemState state) {
state.RequireForUpdate<Navigation.Components.Actor>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state) {
JobHandle handle = new Navigation.Jobs.UpdatePathsJob
{
TargetPosition = SystemAPI.GetComponentLookup<LocalToWorld>(),
}.ScheduleParallel(state.Dependency);
handle.Complete();
}
Job:
[BurstCompile]
public partial struct UpdatePathsJob : IJobEntity {
[ReadOnly] public ComponentLookup<LocalToWorld> TargetPosition;
private void Execute(Aspects.Actor actor) {
if (!TargetPosition.HasComponent(actor.Target))
return;
UnityEngine.Debug.Log($"{TargetPosition.GetRefRO(actor.Target).ValueRO.Position}");
}
}
Any input on what might be the issue would be apprecaited.
The signature “void OnXXX(ref SystemState)” is only for ISystem. ComponentSystemGroup is a managed system type that derives from ComponentSystemBase. You need to follow the semantics of implementing the system callbacks, which is to override the virtual methods, in this case:
[UpdateInGroup(typeof(SimulationSystemGroup), OrderFirst = true)]
public partial class BackgroundSimulationSystemGroup : ComponentSystemGroup
{
public void OnCreate(ref SystemState state)
{
// call base method as a best practice when overriding a non-abstract virtual method
// in the case of ComponentSystemGroup there's some necessary internal set up requiring you to call this
base.OnCreate();
RateManager = new RateUtils.FixedRateCatchUpManager(0.2f);
}
}
Thanks for the assistance. Can I assume that if I don’t have a custom constructor that calls SetRateManagerCreateAllocator that allocators don’t work? Or don’t work as intented? What sort of side effects would it create if I didn’t call that function?
| Just remember if you are overriding OnCreate in ComponentSystemGroup you call base.OnCreate()
I’ve made the change, although I find it odd that you have to manually call the base class function instead of it being architected to internall call all the required functions first and then call the overriden OnCreate
The choice won’t affect anything if you never use SystemState.WorldUpdateAllocator. If you just use the normal global allocators (Domain, Persistent, TempJob, Temp, etc.) you won’t see any problems. Of course, for JobTemp you still want to make sure the jobs complete within 4 frames for the lifespan management of those allocations.
Not setting the allocator will mean accessing SystemState.WorldUpdateAllocator will end up using the allocators that are available from within SimulationSystemGroup. I’m not sure if there are mechanisms to prevent / stall the allocator rollback and swap that happens for the pair of allocators that the group uses if there’s a job running that uses the allocator. If the mechanism exists, it would be safe to use the existing allocators, but the main thread would have to wait for those jobs to finish before the group can swap the allocators, and that would be a long wait for long jobs. If no such mechanism exists, the allocators could be invalidated during long job execution which would mean you have pointers to invalid memory in the job. Both results are pretty undesirable. If the jobs are short enough to last one real frame / end up getting completed one way or another (e.g. used as a dependency for something else in SimulationSystemGroup), there shouldn’t be a problem.
If you do create the allocators for your group, you’ll have general safety of allocations as long as the jobs each complete every group cycle. Technically there’s more time, until the group end point after the next time the system in question runs, but for systems that run every group update, you wouldn’t want average execution time greater than one group cycle. There also is a downside in that allocations would be held for the duration of two group update cycles, so if you make a bunch of allocations that are only necessary for some short jobs, TempJob would make more sense.
The OnCreate stuff is typical OOP stuff, you often see it being necessary to do this kind of base implementation calling. I wouldn’t call that out of the ordinary.