Trying to trace for an attached component with weapon

Hello,

I am fairly new with programming and am working on trying to get my weapon to return an enemies attached component. I am using a bounding box for the collision, which is definitely returning a hit result, however, I am unable to get the component that I have made that sets the character’s stats such as HP and Combat. I would appreciate any help on fixing this, as I mentioned, I am quite new to programming. I would prefer not to use BP to solve this. I have two functions handling this, the trace, and then the function that breaks the hit result to get the component and deal damage to the component. I am also using an Enum to set the type of weapon as well as a struct to set the weapon stats. My trace is as follows:

*// The trace:*
     // FArmoryStats is a struct set in UWeaponComponent that sets weapon type, damage, mesh, ID, etc.
**void UWeaponComponent::OnAttack(FArmoryStats WeaponType, UWeaponComponent* AttackingCharacter)**

{
	AFirst_RPGCharacter* Character = Cast<AFirst_RPGCharacter>(AttackingCharacter->GetOwner());
	
	if (bIsAttacking && (Character != nullptr)) // if the Character is attacking with this weapon component
	{
		switch (WeaponType.Type)
		{
			case EArmoryType::AT_Melee: // Weapon is a melee weapon
			{
                            // MeleeHitBox is a UBoxComponent to give me the dimensions for collision that I want
				FVector MeleeTraceStart = MeleeHitBox->GetComponentLocation();
				FVector MeleeTraceEnd = (MeleeTraceStart * MeleeHitBox->GetScaledBoxExtent());
				FCollisionShape MeleeBox = FCollisionShape::MakeBox(MeleeHitBox->GetScaledBoxExtent());
				ECollisionChannel MeleeTrace = ECC_GameTraceChannel1;
				FCollisionQueryParams Params;
				Params.AddIgnoredActor(GetOwner());

				bIsHitMelee = GetWorld()->SweepMultiByChannel(MeleeHits, MeleeTraceStart, MeleeTraceEnd, FQuat::Identity, MeleeTrace, MeleeBox, Params);
			}



*// The break hit result and deal damage:*

**void UWeaponComponent::GetHitMeleeTarget()**

{
	if (bIsHitMelee) // If there is a hit from the trace, this currently will return a hit, can even get the name of the actor
	{
		for (auto & Hit : MeleeHits)
		{
			UE_LOG(LogTemp, Warning, TEXT("Hit Found"))
			UCombat_Component* TargetCombatComp = Cast<UCombat_Component>(Hit.GetActor()->GetComponentByClass(TSubclassOf<UActorComponent>()));
                    // Have also used in cast (Hit.GetComponent()) as well as (Hit.Component.Get())
			if (TargetCombatComp != nullptr) // If there is a hit to the Combat_Component
			{
				TargetCombatComp->TakeDamage(Stats.Damage); //Deal Damage
				float DamagedHealth = TargetCombatComp->GetCurrentHealth();                           // Get Target's Health
				UE_LOG(LogTemp, Warning, TEXT("Target Health: %s"), &DamagedHealth);                      // Log Target's Health
				return;
			}
			else if (TargetCombatComp == nullptr)
			{
				UE_LOG(LogTemp, Warning, TEXT("No Combat Component"));
				return;
			}
		}
	}
	else if (!bIsHitMelee) { return; } // If there was no hit return
}

Currently, I am getting the log that there is no combat component and can return the name of the target hit so I know that I am definitely getting a hit. If anyone could please help with this, or has any tips on my coding in general, I would be most grateful.

Thanks,

If I understand correctly:

You have a character who is holding a weapon. When the weapon hits an opponent, you want to change HP of this character. Also, you want to add some exp, points to the character that deals damage.

First, we have Soldier class.

Soldier.h

class MYPROJECT_API ASoldier : public ACharacter
{
	/** The weapon currently held in the hand by a soldier */
	class AWeapon * CurrentWeapon;
	
	void EquipWeapon(
		AWeapon * Weapon
	);
};

Soldier.cpp

void ASoldier::EquipWeapon(
	AWeapon * Weapon
)
{
	if (Weapon)
	{
		Weapon->SetOwner(this); // now the weapon will remember who holds it
	}
}

In Soldier we have a pointer to the currently held weapon. In EquipWeapon() we set the owner of the weapon. Now, if you want to get the weapon owner, use Weapon->GetOwner().

Next is Weapon class:

Weapon.h

class MYPROJECT_API AWeapon : public AActor
{
	/** Static Mesh */
	class UStaticMeshComponent * WeaponMesh;
	
	/** Event called when this Actor hit something
	UFUNCTION()
	void OnHit(
		UPrimitiveComponent * HitComponent,
		AActor * OtherActor,
		UPrimitiveComponent * OtherComp,
		FVector NormalImpulse,
		const FHitResult& Hit
	);
};

Weapon.cpp

AWeapon::AWeapon()
{
	// Here create WeaponMesh 
	
	// Set event when component hit something
	WeaponMesh->OnComponentHit.AddDynamic(this, &AWeapon::OnHit);
	
}


void AWeapon::OnHit(
	UPrimitiveComponent * HitComponent,
	AActor * OtherActor, // this is the Actor that weapon hit (enemy)
	UPrimitiveComponent * OtherComp,
	FVector NormalImpulse,
	const FHitResult& Hit
)
{
	if (OtherActor) //check if exist
	{
		if (OtherActor->GetClass()->IsChildOf<ASoldier>()) //check if the hit Actor is ASoldier
		{
			ASoldier * OtherSoldier = Cast<ASoldier>(OtherActor);
			
			// Now you can reduce HP of the enemy (for example 10 hp).
			OtherSoldier->ReceivedDamage(10.0f);
		}
	}
	
	if(GetOwner()) // check if owner of this actor exist. Owner is set in EquipWeapon() in ASoldier class.
	{
		ASoldier * MySoldier = Cast<ASoldier>(GetOwner());
		// Now you can add combo, points etc.
		MySoldier->AddCombo();
	}
}

In the constructor, we add our event. This event will be called every time when WeaponMesh hit in something. In OnHit() we have OtherActor representing the an actor who was hit by WeaponMesh. In GetOwner (), we have an actor who keeps this weapon.

To set the collision profile:

WeaponMesh->SetCollisionProfileName(FName("Weapon"));

You can create your own collision profile in Project Settings->Engine->Collision expand Present (sometimes you have to hide and expand, because the scrollbar does not appear).

Thanks for your reply! So, what I have is a weapon that has a shape trace for collision, is this OnHit function better? I tried using it, but couldn’t get any return from it. Anyways, so I have the shape trace and am getting a hit. I also have a component that I made that handles stats like HP and sets whether the character is in combat. I am trying to get this combat component, in order to deal the damage. The actor itself has no Health stored on him. I will definately try OnHit again, but will I be able to return a component that is attached to the OtherActor? It’s base is UActorComponent.

[1] Shape trace seems cool, but if you want using OnHit() your mesh need have collision

and set in the constructor

WeaponMesh->SetNotifyRigidBodyCollision(true);

to your Mesh. [Here you can read more about collision][2]

[2] Here:

UCombat_Component* TargetCombatComp = Cast<UCombat_Component>(Hit.GetActor()->GetComponentByClass(TSubclassOf<UActorComponent>()));

I see you using GetComponentByClass() so in your AMyActor (Hit.GetActor()) you should add your UCombat_Component using AddOwnedComponent().

[3] This is my idea for a solution.

Every Actor has attached his own UCombat_Component where you store HP. So you can create a pointer to handle CombatComponent.

// MyActor.h
UCombat_Component * CombatHandle;

UCombat_Component * GetCombat();

// MyActor.cpp    
UCombat_Component * GetCombat()
{
    return CombatHandle;
}

In your AMyActor class in the constructor, BeginPlay() or somewhere you need to set CombatHandle (I don’t know where you create CombatComponent).

Next in UWeaponComponent::GetHitMeleeTarget() instead:

UCombat_Component* TargetCombatComp = Cast<UCombat_Component>(Hit.GetActor()->GetComponentByClass(TSubclassOf<UActorComponent>()));

use this:

// Cast to AMyActor to have access to GetCombat() function
AMyActor * HitActor = Cast<AMyActor>(Hit.GetActor()); 
UCombat_Component * TargetCombatComp = HitActor->GetCombat();

Now you should have access to CombatComponent.

[4] Your UWeaponComponent inherits from UActorComponent, but you have also class AWeapon inherits form AActor to spawn in the world. Here I read

UActorComponent

This is the base Component. It can be
included as part of an Actor. It can
Tick if you want it to.
ActorComponents are associated with a
specific Actor, but do not exist at
any specific place in the world
. They
are generally used for conceptual
functionality, like AI or interpreting
player input.

The bool returned does not indicate a hit. It indicates whether something is blocking. IF it starts out blocking then there is no hit.

You should be iterating over the HitResults, and for any ‘valid’ hits processing the result. API’s like OnHIt() dont fire when they start out blocking. Its impossible to calculate an impact vector, or a hit normal. It is similarly impossible to depenetrate since this typically uses that information.

Anything with a physics body is part of the physics scene, and can be tested against. This includes Skeletal Meshes, but does not have to. If you turn on Physics for the Skeletal mesh actor I believe you get a default capsule. But if you want to do something like cut an arm off or a leg, then you need more granularity than a capsule provides. You need a physics asset. Typically the physics bodies are authored in maya or which ever tool, at the same time as the mesh. Then its imported at the same time.

Thank you again for your help. So the way I have set up my components that are attached (including the UCombat_Component) is using an initialize function, ie. So I would just need to add a getter to return the Initialized component. Also, does OnHit work with Skeletal Mesh Components? My WeaponComponent is a USkeletalMeshComponent. I based it off of Ruben Ward’s Inventory Tutorial if you are familiar with that. As I said, lol, I’m new to programming. Thanks!

I see, yeah I am using the free weapon assets from the Infinity Blade Packs, which don’t come with physics assets. Would that be why OnHit did not work? I did turn on overlap and hit events as well as enable physics. And OMG THANK YOU!!! While OnHit still is not working, making a getter function in my character that returns the initialized combat component and then retrieving it with your method worked!

Thank you for your reply!

I am sorry, I am new to programming. Is my for (auto & Hit : MeleeHits) { //Do Stuff } not iterating?

Thanks again for your help! If you are interested, here’s a link to my video playlist showing what I have done on this game.