Using the OnHit Event

A tutorial to for using the OnHit Event in Unreal.

Imagine you are creating a game that involves any kind of damage to a player, enemy, or object. In that case, chances are you will encounter a situation where you need to determine if those objects were hit by something and if so, what hit them, where the impact point was, or other information regarding the hit detected. The OnHit Event provides this information when the collision occurs, then you can use the data to drive changes in your game. Whether it affects health, destroys an object, or causes other gameplay-related actions. In this tutorial, you will use the OnComponentHit and Function Events to apply damage to an Actor that will be represented by changing the Actor's mesh material. The Events will also apply an impulse at the hit location to push the Actor, simulating the effects of being hit by a projectile and applying force at the hit location.

Choose your implementation method:

Blueprints

C++

Project Setup

  1. Begin by creating a new Games > First Person > Blueprint Project named OnHit.

image_0.png

Creating the Mesh Material

  1. Navigate to the Content Browser, and find the LevelPrototyping/Materials folder.

  2. Select MI_SolidBlue, then duplicate (CTRL+ D) and rename the newly duplicated asset MI_Solid_Red.

    image_1.png

  3. Double-click to open the asset, then select and edit the Base Color to the color red.

    image_2.png

  4. Save the asset.

Creating the Cube Actor

  1. Navigate to the Content Browser, and find the LevelPrototyping/Meshes folder.

  2. Right-click on SM_ChamferCube Static Mesh and select Asset Actions > Create Blueprint Using This...

    image_3.png

  3. Name your Blueprint BP_Cube.

    Be sure to choose a folder save location for the new Blueprint.

  4. In the Details panel, set Physics > Simulate Physics to True.

    image_4.png

  5. In the Details panel, click Events > On Component Hit.

    image_5.png

  6. This adds a new node to and opens the Event Graph.

image_6.png

  1. Left-click and drag off of the Other Actor pin, then search for and add the Cast To FirstPersonProjectile node.

image_7.png

Here you are casting to another Blueprint called the FirstPersonProjectile Blueprint and making sure that when you hit the BP_Cube, it is the FirstPersonProjectile Blueprint that hits it. If it is, you can apply an additional script to change the mesh's material, signifying that it has taken damage. If it isn't, you won't do anything.

  1. Drag off the Hit pin and add a Break Hit Result node.

image_8.png

  1. Drag off the Hit Actor pin from the Break Hit Result and add the Apply Point Damage node.

image_9.png

  1. On the Apply Point Damage node, set the Base Damage to 100 and set the Damage Type Class to Damage Type.

image_10.png

The Apply Point Damage node specifies the amount of damage, and the location where the damage is inflicted.

  1. On the Apply Point Damage node, connect the remaining wires.

    • Connect the As BP First Person Projectile pin to the Damage Causer pin.

    • Connect the Impact point to the Hit from Direction pin.

    • Connect the Hit pin to the Hit Info pin.

  2. Compile and Save.

Implementing the Damage functionality

Now that you created functionality to inflict damage on a cube when a projectile collides with it, you need to implement a function that sets the cube mesh's material when it receives damage, then after a delay resets the mesh back to its original material.

  1. Navigate to My Blueprint > Functions and click Add (+) to create a new function named OnTakeDamage.

image_12.png

  1. From the Components tab, Click and drag the StaticMesh Component onto the event graph.

image_13.png

  1. Click and drag off of the Static Mesh pin and from the actions menu select Set Material, then change the Material parameter to the MI_Solid_Red material asset that you created earlier.

image_14.png

  1. Compile and Save.

  2. Navigate to My Blueprint > Functions and click Add(+) to create a new function named Damage Reset.

image_15.png

  1. From the Components tab, click and drag the StaticMesh Component onto the event graph.

  2. Click and drag off of the Static Mesh pin and from the actions menu select Set Material, then change the Material parameter to the MI_Solid_Blue material asset.

image_16.png

  1. Click and drag off the execution pin of the Apply Point Damage node, then search for and select the On Take Damage function.

image_17.png

  1. Click and drag off the execution pin of the On Take Damage function, then search for and select Delay.

image_18.png

  1. Click and drag off the Completed execution pin from the Delay node, then search for and select Damage Reset.

image_19.png

  1. Compile and Save.

Finished Blueprint

image_20.png

Setting up the Level

  1. Drag the BP_Cube into the level from the Content Browser.

image_21.png

  1. Navigate to the Outliner > Simulated Cubes, select all SM_ChauferCubes then right-click and select Replace Selected Actors with > BP_Cube.

image_22.png

  1. Click Play to play in the editor and use the left-mouse button to fire a projectile at the cube.

image_23.gif

  1. When you play in the editor, you will see that when you hit the cube with the projectile you fire It causes the cube to take damage and change its mesh material, and applies an impulse at the location where the cube was hit causing it to move opposite the direction of the projectile.

The amount of force applied is defined inside the FirstPersonProjectile Blueprint which uses the Event Hit node to determine when the projectile actually hits something.

  1. In the Content/FirstPersonBP/Blueprints folder, open the FirstPersonProjectile Blueprint.

image_24.png

The script in this Blueprint checks to see if the object hit is simulating physics (which you set to true on your cube Blueprint). If it is, it then applies an Impulse at the location it hits (the amount is defined inside the green box). You can adjust this value to increase or decrease the amount of impulse applied when a hit occurs.

Project Setup

  1. Begin by creating a new Games > First Person > C++ Project named OnHit.

    image_25.png

Creating the Mesh Material

  1. Navigate to the Content Browser, and find the LevelPrototyping/Materials folder.

  2. Select MI_SolidBlue, then duplicate (CTRL+ D) and rename the newly duplicated asset MI_Solid_Red.

    image_26.png

  3. Double-click to open the asset, then select and edit the Base Color to the color red.

    image_27.png

  4. Save the Asset.

Creating the Cube Actor

  1. In the Editor, click Add(+) > New C++ Class, then choose Actor as your parent class and name your class Cube.

    image_28.png

  2. Declare the following class defaults in your cube.h file

        UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
        class UStaticMeshComponent* CubeMesh;
    
        UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
        UMaterialInstance* CubeMaterial;
    
        UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
        UMaterialInstance* DamagedCubeMaterial;
    
        FTimerHandle DamageTimer;
    
        UFUNCTION()
    
          void OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
  3. Next, in your cube.cpp file, declare the following class libraries.

    #include "Kismet/GameplayStatics.h"
    #include "OnHitProjectile.h"
  4. Navigate to the cube constructor and implement the following functionality.

    ACube::ACube()
    {
        CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));
        DamagedCubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("DamageMaterial"));
        CubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("CubeMaterial"));
    
        CubeMesh->SetSimulatePhysics(true);
    }

Implementing the Damage functionality

Now that you created a cube, you need to implement a function that sets the mesh's material when it receives damage, then after a delay resets the mesh back to its original material.

  1. Declare the following code in your cube.h file.

        void OnTakeDamage();
    
        void ResetDamage();
  2. Navigate to the cube.cpp file and implement the following for the ACube::BeginPlay function.

    void ACube::BeginPlay()
    {
       CubeMesh->OnComponentHit.AddDynamic(this, &ACube::OnComponentHit);
    }
  3. Implement the ACube::OnTakeDamage function.

    void ACube::OnTakeDamage()
    {
    
        CubeMesh->SetMaterial(0, DamagedCubeMaterial);
        GetWorld()->GetTimerManager().SetTimer(DamageTimer, this, &ACube::ResetDamage, 1.5f, false);
    }
  4. Next, implement the ACube::ResetDamage function.

    void ACube::ResetDamage()
    {
        CubeMesh->SetMaterial(0,CubeMaterial);
    }
  5. Finally, navigate to the ACube::OnComponentHit function and implement the following code.

    void ACube::OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
    {
    if (AOnHitProjectile* HitActor = Cast<AOnHitProjectile>(OtherActor))
    {
    
        UGameplayStatics::ApplyDamage(this, 20.0f, nullptr, OtherActor, UDamageType::StaticClass());
        OnTakeDamage();
    
    }
    }
  6. Compile your code.

  7. In the editor, navigate to C++ Classes > Cube then right-click on the Cube Actor and select Create Blueprint class based on Cube.

    image_29.png

  8. From the Components tab, select the Cube Mesh, then navigate to Details > Static Mesh and select the SM_ChamferCube asset.

    image_30.png

  9. In the class defaults of the BP_Cube, set the Cube Material to the MI_Solid_Blue asset, and the Damaged Cube Material to the MI_Solid_Red asset.

    image_31.png

  10. Compile and Save.

CubeActor.h

    #pragma once

    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "Cube.generated.h"

    UCLASS()
    class ONHIT_API ACube : public AActor
    {
        GENERATED_BODY()

    public:
        // Sets default values for this actor's properties
        ACube();

    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;

        UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
        class UStaticMeshComponent* CubeMesh;

        UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
        UMaterialInstance* CubeMaterial;

        UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
        UMaterialInstance* DamagedCubeMaterial;

        FTimerHandle DamageTimer;

        void OnTakeDamage();

        void ResetDamage();

        UFUNCTION()
        void OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);

    public:
        // Called every frame
        virtual void Tick(float DeltaTime) override;

    };

CubeActor.cpp

    #include "Cube.h"
    #include "Kismet/GameplayStatics.h"
    #include "OnHitProjectile.h"

    // Sets default values
    ACube::ACube()
    {
        // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;

        CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));
        DamagedCubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("DamageMaterial"));
        CubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("CubeMaterial"));

        CubeMesh->SetSimulatePhysics(true);

    }

    // Called when the game starts or when spawned
    void ACube::BeginPlay()
    {
        Super::BeginPlay();
        CubeMesh->OnComponentHit.AddDynamic(this, &ACube::OnComponentHit);
    }

    void ACube::OnTakeDamage()
    {
        CubeMesh->SetMaterial(0, DamagedCubeMaterial);
        GetWorld()->GetTimerManager().SetTimer(DamageTimer, this, &ACube::ResetDamage, 1.5f, false);
    }

    void ACube::ResetDamage()
    {
        CubeMesh->SetMaterial(0,CubeMaterial);
    }

    void ACube::OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
    {

        if (AOnHitProjectile* HitActor = Cast<AOnHitProjectile>(OtherActor))
        {
            UGameplayStatics::ApplyDamage(this, 20.0f, nullptr, OtherActor, UDamageType::StaticClass());
            OnTakeDamage();
        }
    }

    // Called every frame
    void ACube::Tick(float DeltaTime)
    {
        Super::Tick(DeltaTime);

    }

Setting up the Level

  1. Drag the BP_Cube into the level from the Content Browser.

    image_32.png

  2. Navigate to the Outliner > Simulated Cubes, select all SM_ChauferCubes then right-click and select Replace Selected Actors with > BP_Cube.

    image_33.png

  3. Click Play to play in the editor and use the left-mouse button to fire a projectile at the cube.

  4. When you play in the editor, you will see that when you hit the cube with the projectile fired it causes the cube to take damage and change its mesh material, and applies an impulse at the location where it was hit causing it to move opposite the direction of the projectile.

    image_34.gif

  5. The amount of force applied is defined inside the OnHitProjectile.cpp file which uses the OnHit function to determine when the projectile actually hits something.

     void AOnHitCPPProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
    {
    
        // Only add impulse and destroy projectile if we hit a physics
        if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr) && OtherComp->IsSimulatingPhysics())
        {
            OtherComp->AddImpulseAtLocation(GetVelocity() * 100.0f, GetActorLocation());
    
            Destroy();
        }
    
    }
Help shape the future of Unreal Engine documentation! Tell us how we're doing so we can serve you better.
Take our survey
Cancel