Download

Adding experience when a target is destroyed.

What is the best approach to adding exp to a player/players who shot and hit a target? And would you add the logic to the target or player character?

What i would do is have an exp variable in the player class. Then in the enemy class on death you would get the player exp variable and add the exp you want to give the player.

how would you get the player instance that hit the object via ray cast? My current setup I have is the player shoots a ray which then gets the object it hit and casts it to “testtarget” is there a way to check to see if the target was hit by a ray cast?

I haven’t done a lot with ray-casting but one way i have done it is on my enemy i have a tag called “Enemy” and to check if you hit him i did

if(Hit.GetActor()->ActorHasTag(“Enemy”)) {
//Log
//Code
}

One problem i had is that i had to use ECC_Visibility in my ray cast but that might not be the case for you could of just been an issue on my end.

Replace Hit with your FHitResult name

What are you trying to get the hit object instance for? To take away health or something? or are you on about the player that cast the raycast?

Have you taken a look at delegates?

You misunderstood. The code I have now gets if the object is an enemy and applies damage. You are saying in the enemy class call a function in the player class to add experience. How would you give the correct player or players that hit the object the experience?

So you are already applying damage to the enemy. This means you have a direct line between player and enemy.

How are you applying damage? Are you manually lowering their health value?
Use an interface instead. Call it “apply damage” or something. It would have a damage value and a player reference as its input.
In the enemy object, you react to the interface by setting an internal player reference to the sent reference and subtracting the sent damage from its health.
Then you have the function that checks if the enemy is dead. If it is, send an “add XP” interface message to the player that’s saved in the reference (The reference is overwritten on every hit, so it references the last player to do damage) before destroying the enemy.

I hope my assumptions about your problem are correct.

If you “tag” or assign a unique player ID to the bullet in some fashion (to differentiate players), you can then pass the player ID or “tag” as a delegate parameter in the method once the bullet strikes. Inside the method on the player you can compare the ID passed back to the one that has been assigned on the player to verify whether or not that specific player hit the target. If you are using the TakeDamage function and a raytrace as the bullet from the player, you already have access to the EventInstigator which you should be able to find the respective player it is being fired from.

Epic provide a function you can override called take damage. If you override the function, and add your XP code there you will have

The amount of damage caused
The type of damage
The person/controller/AI that caused the event
The pawn that was being controlled by the pserson/controller/AI when the event happened

This should be everything you need to calculate and apply your XP.

Because epic wrote this into the base actor class, its callable on anything you’ve already written.

This means you can do the following in your code that applies damage (please excuse the spottiness of the code, its literally copied and pasted from my existing project - including my todo: add xp system reminder.)

You’ll have some code like this somewhere - you’ve figured out which actor needs to take damage and how much. Swap whatever damage call you’ve written for the one provided by epic. You can see my code as an example below.

FFWeapon.cpp



int32 DamageAmount = FMath::CeilToInt(InstantHitInfo[CurrentFireMode].Damage / NumTraces) * 1-(ImpactHit->BulletEnergy / InstantHitInfo[CurrentFireMode].TraceRange);
ImpactHit->HitInfo.Actor->TakeDamage(DamageAmount,
 FFFPointDamageEvent(InstantHitInfo[CurrentFireMode].Damage, ImpactHit->HitInfo, FireDir, InstantHitInfo[CurrentFireMode].DamageType, FireDir * InstantHitInfo[CurrentFireMode].Momentum), 
 FFOwner->Controller, 
 this);


And put this in the thing you want to receive damage

FFCreature.h



virtual float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, class AActor* DamageCauser) override;


Finally, choose what you want to do with the takedamage call. I’ve had the game modify the damage amount, drop a decal into the world and alert any AI near by that this creature made a pain sound.

FFCreature.cpp



float AFFCreature::TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, class AActor* DamageCauser)
{
	if (Health <= 0.f)
	{
		return 0.f;
	}
	// Modify based on game rules.
	AFFGameMode* const Game = GetWorld()->GetAuthGameMode<AFFGameMode>();
	Damage = Game ? Game->ModifyDamage(Damage, this, DamageEvent, EventInstigator, DamageCauser) : Damage;
	const float ActualDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
	if (ActualDamage > 0.f)
	{
		Health -= ActualDamage;
		if (Health <= 0)
		{
			Die(ActualDamage, DamageEvent, EventInstigator, DamageCauser);
		}
		else
		{
			PlayHit(ActualDamage, DamageEvent, EventInstigator ? EventInstigator->GetPawn() : NULL, DamageCauser);
		}
		MakeNoise(1.0f, EventInstigator ? EventInstigator->GetPawn() : this);
		CreateBloodDecal();

                // TODO: ADD XP CODE HERE - something like
                // Cast<AFFController>(EventInstigator)->AddXP(ActualDamage);
	}
	return ActualDamage;
}


NB. this is also how shootergame, unreal tournament and the survival game tutorial series do it, so it really is the recommended approach :wink: