How to charge an Arrow

First, you need to set a way to change the value of the bHold variable. Either by the player clicking again causing
ABow_ArrowCharacter::Draw() to be called again, which sould be something like:

void ABow_ArrowCharacter::Draw(){ 
    bHold = !bHold;
}

Then, on the tick function, you need to calculate the speed of the projectile based on how much time the key was held down. You should clamp the held time though because we dont want our arrows to be able to fly to the moon as soon as the player has charged enough.

Super::Tick(DeltaTime);
 {

     if (!bHold && ChargeTime!=0) //shoot the arrow
     {
             float ChargePercent = FMath::Clamp(ChargeTime, 0.0f, MaxChargeTime)/MaxChargeTime;
             float Speed = ChargePercent*MaxArrowSpeed;
             //spawn the projectile here, with the appropriate direction and speed
             ChargeTime = 0;//reset the charge time timer
             bHold = false;
     }else{
             ChargeTime+=DeltaTime;//keep adding charge time, the player does not want to shoot yet
     }
 }

Notice the 2 extra variables: MaxArrowSpeed and MaxChargeTime: Max Charge time is the max time the user can charge his bow in order to achieve a speed of MaxArrowSpeed (this is why we clamp).

If you have got issues regarding projectile spawning, may I redirect you to this question as well: https://answers.unrealengine.com/questions/306257/spawning-a-projectile.html

Hi,

I have been to charge a projectile (StaticMesh) based on how long we hold the LMB.
But I am literally stuck, I am trying to achieve this using C++.

Below are my codes under

Character class

void ABow_ArrowCharacter::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);
{

	if (bHold)
	{
		ChargeTime += DeltaTime;
		if (ChargeTime <= ArrowSpeed)
		{
			GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, TEXT("Draw"));
			ChargeTime = 0;
		}
	}
}

}

void ABow_ArrowCharacter::Release()

{

bHold = false;
USkeletalMeshComponent* UseMesh = GetMesh();
if (ProjectileClass != NULL)
{
	FVector CameraLoc;
	FRotator CameraRot;
	GetActorEyesViewPoint(CameraLoc, CameraRot);
	//FVector const MuzzleLocation = CameraLoc + FTransform(CameraRot).TransformVector(GunOffset);
	const FVector SocketLocation = UseMesh->GetSocketLocation("ArrowSocket");
	/*const FRotator SocketRotation = UseMesh->GetSocketRotation("ArrowSocket");*/
	FRotator MuzzleRotation = CameraRot;
	MuzzleRotation.Pitch += 10.0f;
	UWorld* const World = GetWorld();
	if (World)
	{
		FActorSpawnParameters SpawnParams;
		SpawnParams.Owner = this;
		SpawnParams.Instigator = Instigator;
		AProjectile_Arrow* const Projectile = World->SpawnActor<AProjectile_Arrow>(ProjectileClass, SocketLocation, MuzzleRotation, SpawnParams);
		if (Projectile)
		{
			FVector const LaunchDir = MuzzleRotation.Vector();
			Projectile->InitVelocity(LaunchDir);
		}
	}
}

}

void ABow_ArrowCharacter::Draw()

{

bHold = true;

}

Projectile Class

AProjectile_Arrow::AProjectile_Arrow()

{

// 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;

CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("Collision_comp"));
CollisionComp->InitSphereRadius(5.0f);
CollisionComp->BodyInstance.SetCollisionProfileName("Arrow");
//CollisionComp->BodyInstance.SetCollisionProfileName("Projectile");
CollisionComp->OnComponentHit.AddDynamic(this, &AProjectile_Arrow::OnHit);
RootComponent = CollisionComp;

ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp"));
ProjectileMovement->UpdatedComponent = CollisionComp;
//ProjectileMovement->InitialSpeed = 2000.0f;
//ProjectileMovement->MaxSpeed = 2000.0f;
ProjectileMovement->bRotationFollowsVelocity = true;
ProjectileMovement->ProjectileGravityScale = 0.5f;
ProjectileMovement->bShouldBounce = false;

ArrowMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Arrow"));
ArrowMesh->AttachTo(RootComponent);

InitialLifeSpan = 3.0f;

}

Would be great If anyone can shed light on how I can get this done. Thanks a lot in advance.

Cheers!

Hi Zark,

Thanks a lot. It makes a lot more sense. But one small doubt, spawning a projectile in a tick will spam the arrows right?
and I have already got the arrows to travel in the direction I aim in the release function. So should I copy and paste it in the tick function?

I am going to try this tonight.

Thanks,
Spark

It is not going to spam the arrow because of these 2 lines :
ChargeTime = 0;//reset the charge time timer
bHold = false;

It essentially resets the whole mechanism.

Best Regards,
Zark

HI Zark,

I have been trying this out for the past two days and sorry but as i suspected before the arrows are being spammed not sure if I am doing something wrong.

This what i have so far

void ABow_ArrowCharacter::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);

{

	if (!bHold && ChargeTime != 0)
	{
		float ChargePercent = FMath::Clamp(ChargeTime, 0.0f, MaxChargeTime)/MaxChargeTime;
		float Speed = ChargePercent*MaxArrowSpeed;
		GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, TEXT("Draw"));
		if (ProjectileClass != NULL)
		{
			FVector CameraLoc;
			FRotator CameraRot;
			GetActorEyesViewPoint(CameraLoc, CameraRot);
			const FVector SocketLocation = GetMesh()->GetSocketLocation("ArrowSocket");
			FRotator MuzzleRotation = CameraRot;
			MuzzleRotation.Pitch += 10.0f;
			UWorld* const World = GetWorld();
			if (World)
			{
				FActorSpawnParameters SpawnParams;
				SpawnParams.Owner = this;
				SpawnParams.Instigator = Instigator;
				AProjectile_Arrow* const Projectile = World->SpawnActor<AProjectile_Arrow>(ProjectileClass, SocketLocation, MuzzleRotation, SpawnParams);
				if (Projectile)
				{
					FVector const LaunchDir = MuzzleRotation.Vector();
					Projectile->InitVelocity(LaunchDir);
				}
			}
		}
		ChargeTime = 0;
		bHold = false;
	}
	else
	{
		ChargeTime += DeltaTime;

	}

}

}

void ABow_ArrowCharacter::Draw()

{

bHold = !bHold;

}

void ABow_ArrowCharacter::Release()

{

bHold = false;

}

Thanks,
Spark

Since you implemented a Release function, you could revert to having Draw just setting bHold to true. Then, move these parts to the functions accordingly:

this should solve all of your problems. You might also want to add a delay before the player is able to re-draw an arrow.

Hi Zark,

I have been trying to do this myself but seems like I am stuck big time. Sorry for being such a pain. I did what you told and the arrow spawns only for the first time and after that it never spawns. So i changed it like this

void ABow_ArrowCharacter::Release()

{

USkeletalMeshComponent* UseMesh = GetMesh();
float DeltaTime = 100;
if (ChargeTime != 0)

{

	float ChargePercent = FMath::Clamp(ChargeTime, 0.0f, MaxChargeTime) / MaxChargeTime;

	float Speed = ChargePercent*MaxArrowSpeed;

	GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, TEXT("Draw"));

}

if (ProjectileClass != NULL)

{

		FVector CameraLoc;
		FRotator CameraRot;
		GetActorEyesViewPoint(CameraLoc, CameraRot);
		/*FVector const MuzzleLocation = CameraLoc + FTransform(CameraRot).TransformVector(GunOffset);*/
		const FVector SocketLocation = UseMesh->GetSocketLocation("ArrowSocket");
		//const FRotator SocketRotation = UseMesh->GetSocketRotation("ArrowSocket");
		FRotator MuzzleRotation = CameraRot;
		MuzzleRotation.Pitch += 10.0f;
		UWorld* const World = GetWorld();
	if (World)

	{

			FActorSpawnParameters SpawnParams;
			SpawnParams.Owner = this;
			SpawnParams.Instigator = Instigator;
			AProjectile_Arrow* const Projectile = World->SpawnActor<AProjectile_Arrow>(ProjectileClass, SocketLocation, MuzzleRotation, SpawnParams);

		if (Projectile)

		{

				FVector const LaunchDir = MuzzleRotation.Vector();
				Projectile->InitVelocity(LaunchDir);
				Projectile->GetProjectileMovementComponent();

		}
	}
}

ChargeTime = 0;
bHold = false;
if (bHold)
{
	ChargeTime += DeltaTime;
}

}

I have added debug message in the IF statement but seems like it is not working. Am I missing something?

thanks,
Spark

This is how Release should be: `USkeletalMeshComponent* UseMesh = GetMesh();
if (ChargeTime != 0 && bHold)
{

 float ChargePercent = FMath::Clamp(ChargeTime, 0.0f, MaxChargeTime) / MaxChargeTime;

 float Speed = ChargePercent*MaxArrowSpeed;

 GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, TEXT("Shoot Arrow"));

 if (ProjectileClass != NULL)

{

     FVector CameraLoc;
     FRotator CameraRot;
     GetActorEyesViewPoint(CameraLoc, CameraRot);
     /*FVector const MuzzleLocation = CameraLoc + FTransform(CameraRot).TransformVector(GunOffset);*/
     const FVector SocketLocation = UseMesh->GetSocketLocation("ArrowSocket");
     //const FRotator SocketRotation = UseMesh->GetSocketRotation("ArrowSocket");
     FRotator MuzzleRotation = CameraRot;
     MuzzleRotation.Pitch += 10.0f;
     UWorld* const World = GetWorld();
 if (World)

 {

         FActorSpawnParameters SpawnParams;
         SpawnParams.Owner = this;
         SpawnParams.Instigator = Instigator;
         AProjectile_Arrow* const Projectile = World->SpawnActor<AProjectile_Arrow>(ProjectileClass, SocketLocation, MuzzleRotation, SpawnParams);

     if (Projectile)

     {

             FVector const LaunchDir = MuzzleRotation.Vector();
             Projectile->InitVelocity(LaunchDir);
             Projectile->GetProjectileMovementComponent();

     }
 }
}

 ChargeTime = 0;

bHold = false;

}

And, on tick, if bHold is true, append current delta seconds to chargeTime

Hi Zark,

Thanks a lot.Sorry I was on vacation. I am getting back to what i was doing. Will let you know how it goes.