Subsystem Tick always called twice per frame, what am I doing wrong?

I’m pulling my hair out all weekend trying to figure out what I am doing wrong so I made the simplest possible test case and I am still seeing double ticks. Here is what I am doing.

I am trying to make a subsystem derived from UGameInstanceSubsystem that ticks. So Here is what I do.

  1. Create new empty C++ project (Unreal Engine 5.0.3)
  2. Create a new Class of type UGameInstanceSubsystem
  3. Add FTickableGameObject as additional inherited class.
  4. Implement overrides for Tick() and GetStatId()
  5. Run the project in PIE and witness double ticks in the logs.

Here is the code I am using for the tickable subsystem

MyGameInstanceSubsystem.h

type or paste#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyGameInstanceSubsystem.generated.h"
UCLASS()
class MYPROJECT_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem, public FTickableGameObject
{
	GENERATED_BODY()
	public:
	virtual void Tick(float DeltaTime) override;
	virtual TStatId GetStatId() const override { return UObject::GetStatID(); };
};

MyGameInstanceSubsystem.cpp

#include "MyGameInstanceSubsystem.h"
 void UMyGameInstanceSubsystem::Tick(float DeltaTime)
{
 	double T = FApp::GetCurrentTime();
 	UE_LOG(LogTemp, Warning, TEXT("Tick running with current time: %f, delta time: %f, frame: %d"),
 		T, DeltaTime, GFrameCounter);
}

Log output

Warning      LogTemp                   Tick running with current time: 16875601.640324, delta time: 0.027800, frame: 374
Warning      LogTemp                   Tick running with current time: 16875601.640324, delta time: 0.027800, frame: 374
Warning      LogTemp                   Tick running with current time: 16875601.665518, delta time: 0.025194, frame: 375
Warning      LogTemp                   Tick running with current time: 16875601.665518, delta time: 0.025194, frame: 375
Warning      LogTemp                   Tick running with current time: 16875601.759149, delta time: 0.016667, frame: 380
Warning      LogTemp                   Tick running with current time: 16875601.759149, delta time: 0.016667, frame: 380

I must be doing something wrong but I have no idea what it is.

1 Like

I finally found a solution, I am not sure if it is correct or where it is documented but the IsTickable() method needs to be overridden and it needs to return false until after the call to Initialize().

For some reason if IsTickable() always returns true which is the default in the FTickableGameObject class then the tick registration gets run twice.

I can provide some additional details for you because I’ve run into the same issue. What this turns out to be is that with a naïve implementation (it’s what I did the first time too) is that the ClassDefaultObject ends up getting registered to tick. The “real” instance of the subsystem is not actually ticking twice.

You can see the Epic solution to this in the UTickableWorldSubsystem (WorldSubsystem.h/cpp). It’s partly similar to your solution of not allowing ticking until the instance is initialized. The other part is that IsTickable always returns false when IsTemplate is true (meaning the instance is the CDO). It also checks IsTemplate as part of GetTickableTickType so that CDO’s return ::Never as the tick type.

2 Likes

For the record, it’s also possible to forget that you are also launching a dedicated server which will, obviously, also tick. Happened to me :sweat_smile: