How to set tick order when using FTickableGameObject ?

I have a custom UObject class as such:

UCLASS()
class TESTING_API UMyObject : public UObject, public FTickableGameObject {
    GENERATED_BODY()

   public:
    UMyObject() { bIsCreateOnRunning = GIsRunning; }

   private:
    UPROPERTY()
    bool bIsCreateOnRunning = false;

    UPROPERTY()
    uint32 LastFrameNumberWeTicked = INDEX_NONE;

    virtual void Tick(float DeltaTime) override {
        if (LastFrameNumberWeTicked == GFrameCounter) {
            return;
        }

        LastFrameNumberWeTicked = GFrameCounter;

        UE_LOG(LogTemp, Warning, TEXT("UMyObject::Tick()"));
    }
    virtual bool IsTickable() const override { return bIsCreateOnRunning; }
    virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(UMyObject, STATGROUP_Tickables); }
};

and a non-UObject struct as such:

struct FMyStruct : public FTickableGameObject {
   public:
    FMyStruct() { bIsCreateOnRunning = GIsRunning; }

   private:
    bool bIsCreateOnRunning = false;
    uint32 LastFrameNumberWeTicked = INDEX_NONE;

    virtual void Tick(float DeltaTime) override {
        if (LastFrameNumberWeTicked == GFrameCounter) {
            return;
        }

        LastFrameNumberWeTicked = GFrameCounter;

        UE_LOG(LogTemp, Warning, TEXT("FMyStruct::Tick()"));
    }
    virtual bool IsTickable() const override { return bIsCreateOnRunning; }
    virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(FMyStruct, STATGROUP_Tickables); }
};

And I’m creating them both in an actor as such:

UCLASS()
class TESTING_API AMyActor : public AActor {
    GENERATED_BODY()

   protected:
    AMyActor() { MyObj = CreateDefaultSubobject<UMyObject>(TEXT("MyObj")); }

    UPROPERTY(Instanced, EditAnywhere, BlueprintReadOnly)
    UMyObject* MyObj;

    FMyStruct MyStruct;

    virtual void BeginPlay() override {
        Super::BeginPlay();

        MyStruct = FMyStruct();
    }
};

And the order in which the UMyObject::Tick() & FMyStruct::Tick() seems to be inconsistent. Is there any way I can make sure FMyStruct always ticks first?

Also when I create and place a BP_MyActor in the map it ticks perfectly but when I delete it from the map it still seems to be ticking, what could be causing this?




Edit:
I’ve managed to use FTickFunction instead of FTickableGameObject and leveraged FTickFunction::bHighPriority to ensure FMyStruct always ticks first, but the issue of ticking even after deleting/destroying BP_MyActor persists

Code below:

Code Dropdown
DECLARE_DELEGATE_OneParam(FOnTick, float);

USTRUCT(BlueprintType)
struct FTicker : public FTickFunction {

    GENERATED_BODY()

   public:
    FOnTick OnTick;

   private:
    virtual void ExecuteTick(float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) override {
        OnTick.ExecuteIfBound(DeltaTime);
    }
};

template <>
struct TStructOpsTypeTraits<FTicker> : public TStructOpsTypeTraitsBase2<FTicker> {
    enum { WithCopy = false };
};


UCLASS()
class TESTING_API UMyObject : public UObject {
    GENERATED_BODY()

   public:
    void Setup(UObject* Owner, bool bToStartTick = false) {
        if (!Ticker.IsTickFunctionRegistered()) {
            Ticker.bCanEverTick = true;
            Ticker.bHighPriority = true;
            Ticker.RegisterTickFunction(Owner->GetWorld()->PersistentLevel);
            Ticker.OnTick.BindUObject(this, &UMyObject::Tick);
        }

        Ticker.SetTickFunctionEnable(bToStartTick);
    }
    void Cleanup() {
        if (Ticker.IsTickFunctionRegistered()) {
            Ticker.UnRegisterTickFunction();
        }

        Ticker.SetTickFunctionEnable(false);
    }
    void Tick(float DeltaTime) {
        UE_LOG(LogTemp, Warning, TEXT("UMyObject::Tick()"));
    }


   private:
    FTicker Ticker;
};


struct FMyStruct {

   public:
    void Setup(UObject* Owner, bool bToStartTick = false) {
        if (!Ticker.IsTickFunctionRegistered()) {
            Ticker.bCanEverTick = true;
            // Ticker.bHighPriority = true;
            Ticker.RegisterTickFunction(Owner->GetWorld()->PersistentLevel);
            Ticker.OnTick.BindRaw(this, &FMyStruct::Tick);
        }

        Ticker.SetTickFunctionEnable(bToStartTick);
    }
    void Cleanup() {
        if (Ticker.IsTickFunctionRegistered()) {
            Ticker.UnRegisterTickFunction();
        }

        Ticker.SetTickFunctionEnable(false);
    }

    void Tick(float DeltaTime) {
        UE_LOG(LogTemp, Warning, TEXT("FMyStruct::Tick()"));
    }


   private:
    FTicker Ticker;
};

You are basically wanting to be executing the tick function from with the owning actor. Why not just add a tick function inside of the owning actor and manually triggering the function that “ticks” in the needed order. (it might need to just be a function)

The FTickableGameObject seems a bit redundant if you are not allowing the objects to tick at their own rate. The tick will also stop once the object gets garbage collected (you could stop the tick right before destroy)

In actors and components you can add TickPrerequisite’s in the form of

  • AddTickPrerequisiteActor(TargetingActor);
  • AddTickPrerequisiteComponent

But at a UObject and FStruct you probably lack the tools to controls them outside of manually firing the functions in order inside of the actors tick (varied tick rate would need extra calculations).

External link:

1 Like

The owning actor was just for example :slight_smile:

In actuality the UMyObject & FMyStruct can belong to anyone and therefore tick from anywhere like UObject, AActor, ACharacter, etc

Also I found FTickFunction which has bHighPriority which I can set to always make sure FMyStruct ticks first but it seems to have it’s own issues & I’m not completely sure if I’m using it properly so could you take a look at the edit I’ve made in the question?