Can I have two ticks for an actor, one before and one after physics?

This would be the same as Unity’s ‘Update’ and ‘LateUpdate’ functions.

Basically if I can’t do this I’m forced to split functionality across multiple objects which adds to clutter, difficulty to follow code, and break the tenets of Code Complete.

For example, let’s say I have a baton with a ribbon on it which are affected by physics. In the regular Tick I apply the user inputs. In the post physics tick, I would check where the ribbon ended up to trigger some stuff. In this particular case, some of you might be tempted to answer, “well make the ribbon its own object with a post physics tick”, but let’s just say that’s would not work for me. I want to know if it’s possible to have a single Actor have one tick before physics and another tick after physics. If there were a way to have any other kind of delegate fire off post physics, that would work as well.

That’s still just one tick for one actor, right? I want two different ticks for one actor. One that happens before physics, and one that happens after.

I just recently experienced this. The way I handled it was to make another object that is aware of the object that needs two ticks. This object has TG_PostPhysics grouping, and acts as a ‘tick controller’ of the object. This calls each registered object’s ‘Tick_Post(float DeltaSeconds)’ function which, in my case, did kind of a ‘reduce step’ to my ‘map-reduce’.

Hope that was helpful!

Thanks, it does help. I was hoping to avoid having two objects that do one thing but there may be no other way. It’s poor system design on the part of Epic IMO. You want objects to be able to handle all of their needs.

You’re welcome. However I wouldn’t be so quick to judge Epic for this.

I think the design rationale was to not clog up AActor with too many unnecessary virtual functions. Firstly, that would explode the code memory space. Secondly, remember that almost everything inherits from AActor. Adding in even one more Tick()-like function would require that almost every object used in the game or editor will have another function that is called every frame interval, which can add up significantly since function calls are expensive compared to inline code (push, pop w/in ASM, but that’s another topic entirely).

Abstract/theoretical coding notions and practices are generally helpful, but they have their exception cases too.

By default ticks are not called. I imagine that internally they aren’t even instantiated unless ‘can ever tick’ is set. Basically, they don’t want to deal with the messiness of setting it up on their end, so they put the burden of it on our end. Letting the user base overcomplicate their work (not to mention make it more error prone) to simplify theirs is bad design IMO. If you’ve used Unity, you know how convenient those functions are.

Follow-up question: Can I create a blueprint derived off of ActorComponent and set the tick group to post physics without creating a c++ class?

As far as I can see this IS possible without having multiple objects or additional components (I’m using 4.9). Adding an additional TickFunction member (deriving from FTickFunction) did the trick for me. I merged .h and .cpp to make this code sample shorter, but you probably want to split these to solve circular dependencies.

USTRUCT()
struct FMySecondaryTickFunction : public FTickFunction
{
	GENERATED_USTRUCT_BODY()

	class AMyActor*	Target;

	YOUR_APP_API virtual void ExecuteTick(
		float DeltaTime, 
		ELevelTick TickType, 
		ENamedThreads::Type CurrentThread, 
		const FGraphEventRef& MyCompletionGraphEvent) override
	{
		if (Target && !Target->HasAnyFlags(RF_PendingKill | RF_Unreachable))
		{
		  FScopeCycleCounterUObject ActorScope(Target);
		  Target->TickActor2(DeltaTime*Target->CustomTimeDilation, TickType, *this);
		}
	}

	MY_APP_API virtual FString DiagnosticMessage() override 
	{ 
		return Target->GetFullName() + TEXT("[TickActor2]"); 
	}
};

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

public:

	UPROPERTY(EditDefaultsOnly, Category = "Tick")
	FMyActorSecondaryTickFunction SecondaryActorTick;

	AMyActor::AMyActor()
	{
		PrimaryActorTick.TickGroup = TG_PrePhysics;
		PrimaryActorTick.bCanEverTick = true;
		PrimaryActorTick.bStartWithTickEnabled = true;

		SecondaryActorTick.TickGroup = TG_PostPhysics;
		SecondaryActorTick.bCanEverTick = true;
		SecondaryActorTick.bStartWithTickEnabled = true;
	}

	virtual void PostInitProperties() override
	{
	  Super::PostInitProperties();
	  if (!IsTemplate() && SecondaryActorTick.bCanEverTick)
	  {
	    SecondaryActorTick.Target = this;
	    SecondaryActorTick.SetTickFunctionEnable(
	    		SecondaryActorTick.bStartWithTickEnabled );
	    SecondaryActorTick.RegisterTickFunction(GetLevel());
	  }
	}
	void TickActor2(
		float DeltaSeconds, 
		ELevelTick TickType, 
		FMyActorSecondaryTickFunction& ThisTickFunction
	)
	{
		// Non-player update.
		const bool bShouldTick = 
			((TickType != LEVELTICK_ViewportsOnly) || ShouldTickIfViewportsOnly());
		if (bShouldTick)
		{
		  if (!IsPendingKill() && GetWorld())
		  {
		   if (GetWorldSettings()!=NULL &&
		       (bAllowReceiveTickEventOnDedicatedServer||!IsRunningDedicatedServer()))
		   {
			   //My cool post physics tick stuff
		   }
		  }
		}
	}
}
2 Likes

I’m having some trouble due to the order of declaration. If I declare AMyActor first, I cannot reference it in FMySecondaryTickFunction, and if I declare FMySecondaryTickFunction first, I cannot reference it in AMyActor. Am I doing something wrong here, has the UE api changed (4.13), or is this just impossible?

Have a closer look at line 6.
The “class” keyword makes this a forward declaration, so AMyActor does not have to be declared at this point. You should be able to move all other occurrences of AMyActor within the TickFunction class into the .cpp file (while leaving the class declaration in the .h), where you sohuldn’t have any problems with circular dependencies (also edited initial answer to clarify that).

After resolving the circular dependencies I got a “reference to deleted function” trying to compile. After some research turns out FTickFunctions are unsafe to copy and its copy assignment operator has been deleted (which I didn’t even know was a thing in C++ till now :slight_smile:

I needed this right after the declaration of FMySecondaryTickFunction to compile.

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

If below 4.16 I think you just need TStructOpsTypeTraitsBase rather than TStructOpsTypeTraitsBase2