Explanation for Dynamic Delegates and OnHit was confusing, so I wrote notes

I followed along on this section but I’ve had to revisit it, do a lot of Googling, and slowly explain it all to myself as code comments because I wasn’t really getting what we were doing. This caused me to write a lot of documentation to myself in my cpp and header file.

Now that I get it, I thought I should probably share these notes for people coming after that also get confused and click on Lecture Discussions looking for more info.


So, first thing is we want to be able to access that OnComponentHit Event that fires off automatically in UE4 every time our ProjectileMesh is hit (or hits something) in-game. That’s easy to do with Blueprints since OnComponentHit is just a big red box we can add with lots of outputs already there, but with C++ we have to take a few steps as we see in this lecture.

What we want to be able to do, is call our OnHit() method that we are creating, every time the OnComponentHit Event is fired off automatically for us behind the scenes.

Listening for Events like that I imagine makes things a lot simpler than trying to write clever code in Tick functions to watch and “listen” for hits. It’s already been figured out and written for us, we just gotta somehow be able to access that Event when it fires off.

So to understand how we did that, in the end I felt better working sort of backwards. The first line I look at is in our AProjectileBase constructor in the cpp file. I’m only gonna include the relevant lines.

// Constructor. Sets default values.
AProjectileBase::AProjectileBase()
{
    // Here's where we create our StaticMesh.
	ProjectileMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Projectile Mesh"));

	// This binds "OnComponentHit" event to "OnHit()" function, so OnHit is called any time this component is hit.
	// AddDynamic() is a helper macro that binds the event to the object and method we want to call.
	ProjectileMesh->OnComponentHit.AddDynamic(this, &AProjectileBase::OnHit);
}

Okay, we used AddDynamic() (a UE4 macro) to add a Dynamic Delegate to the OnComponentHit event. We’re gonna add our own stuff as the delegate. OnHit() will delegate what to do with the data we get from the OnComponentHit event firing off behind the scenes.

The OnComponentHit event is going to give us hit data as parameters we can work with, that we can pass directly into our OnHit method to play around with. Unfortunately, the way to find what those parameters are that we need to add to OnHit, is buried deep in the source code itself. I don’t understand why it’s not cleanly laid out in the docs.

That’s exactly why we have to ctrl-click OnComponentHit and scroll up to the first time it’s mentioned, to see where these delegate parameters are declared so that we know which ones to grab if we’re making our own delegate for OnComponentHit (which we are: Our object and its method called OnHit()).

So you search upwards in PrimitiveComponent.h (because that’s where OnComponentHit is declared in the source code) and you find this mess with no explanations. So here’s some:

	/**
	* Delegate for notification of blocking collision against a specific component.
	* NormalImpulse will be filled in for physics-simulating bodies, but will be zero for swept-component blocking collisions.
	*/
	DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams(
		FComponentHitSignature,					// (Don't need) The type this event fires off from behind the scenes (I think).
		UPrimitiveComponent,					// (Don't need) The class this event is attached to behind the scenes (I think), which is why we can access it from a StaticMesh component.
		OnComponentHit,							// (Don't need) The event name! This is how we know what it is.
		UPrimitiveComponent*, HitComponent,		// First parameter we'll actually get from the event. Our component that was hit (our ProjectileMesh in this case).
		AActor*, OtherActor,					// Second parameter. The other actor hit by our ProjectileMesh.
		UPrimitiveComponent*, OtherComp,		// Third. The other specific component hit on that actor, if needed (like if we could specifically damage an arm, rather than a player).
		FVector, NormalImpulse,					// Fourth. The impulse of the hit.
		const FHitResult&, Hit					// Fifth. The info in the hit result of a trace, like point of impact, etc.
		);

From that, we finally know what parameters will be automatically filled by the OnComponentHit event, when it calls our OnHit() method that we assigned to it as a delegate. I highlight all of this over and over to beat it into my own head.

The last step needed is that to declare a method as a dynamic delegate like we want, you just have to mark it as a UFUNCTION():

/// Our method. Will be a Dynamic Delegate. Used to handle our OnComponentHit Event info for damage, destruction, etc. \n
/// Parameters determined by parameters accessible from OnComponentHit event.
UFUNCTION() void OnHit(
	UPrimitiveComponent* HitComponent,    // First parameter.
	AActor* OtherActor,                   // Second, etc.
	UPrimitiveComponent* OtherComp,
	FVector NormalImpulse,
	const FHitResult& Hit
	);

All of this was essentially so we could write:

ProjectileMesh->OnComponentHit.AddDynamic(this, &AProjectileBase::OnHit);

So that’s about it.


What a mess. So if all Dynamic Delegates need to be created as UFUNCTIONs, what the hell are UFUNCTIONs??

A UFUNCTION as I understand it is the same as regular C++ functions with a few differences.

  • Can be called or overridden from within Blueprints visual scripting (very very useful), if you include parameters like:

    • BlueprintCallable,
    • BlueprintImplementableEvent, and
    • BlueprintPure.
  • Can be assigned as delegates within the default properties of a class (like what we just did).

  • Replication callbacks: Will be called when the variable changes that it’s associated with, and is replicated across the network. (I don’t know what this means)

  • The only type of function that can be declared as an exec function, allowing them to be called by the player from the in-game console (pretty handy).

7 Likes

Thanks for sharing. I’m sure this will be very useful to others.

2 Likes

Thanks so much dude fr I tried to research it further and was about to watch the lecture for like the 3rd time but this was super useful :smiling_face_with_three_hearts:

Thank you this helped me a ton!

Thank you so much for taking the time to do this. I was getting some serious whiplash from how many places this section was going and these notes really helped me understand what was happening.