The Firing Solution [WIP] : Need Some Help Tweaking

This has been solved. Scroll to my 3rd post below.
[HR][/HR]

I wanted to know if I am implementing correctly. And is there a way to obtain the square root of a vector?

I implemented an excerpt pseudo-code from Ian Millington’s Artificial Intelligence for Games’s Firing Solution on Chapter 3.5.3. According to the pseudo-code, it goes like this:



function firingSolution (start, target, muzzle_v, gravity) {
   // Calculate vector back from target to start
   delta = target - start

   // Real-valued coefficents of quadratic equation
   a = gravity * gravity
   b = -4 * (gravity * d + muzzle_v * muzzle_v)
   c = 4 * delta * delta

   // Check for no real solutions
   if ( 4 * a * c > b * b ) return null

   // Find short and long times to target
   disc = sqrt (b * b - 4 * a *c)
   t1 = sqrt ( ( -b + disc ) / (2 * a ))
   t2 = sqrt ( ( -b - disc ) / (2 * a ))

   // Pick shortest valid time to target (ttt)
   if ( t1 < 0 ) && ( t2 < 0) return null  // No valid times
   if ( t1 < 0 ) ttt = t2 else
   if ( t2 < 0 ) ttt = t1 else
   ttt = min ( t1, t2 )
   
   // Return firing vector
   return ( 2 * delta - gravity * ttt * ttt ) / ( 2 * muzzle_v * ttt )
}


Where gravity, delta, and muzzle_v are vectors. At the end of the excerpt, the book mentioned that ***** may represent dot product.

Here’s my code written in UE4:



FVector MuzzleVelocity = FVector(1024.0f);
FVector Gravity = FVector(0.0f, 0.0f, -9.8f);
FVector Delta = CannonLocation - CurrentLocation;
float gravity = -9.8f;
float distance = FVector::Dist(CannonLocation, CurrentLocation);
float velocity = 215.0f;

float a = Gravity | Gravity;
float b = -4.0f * ((Delta | Gravity) + (velocity * velocity));
float c = 4.0f * (Delta | Delta);

//No solutions
if (FMath::Sqrt(((Gravity | Delta) + 1024.0f * 1024.0f)) < ((Gravity | Gravity) * (Delta | Delta))){
	LOG("No solutions.");
	return;
}

float disc = FMath::Sqrt((b*b) - (4.0f * a*c));
float time0 = FMath::Sqrt((-b + disc / (2 * a)));
float time1 = FMath::Sqrt((-b - disc / (2 * a)));

float PlusMinus = FMath::Sqrt(FMath::Square<float>((Gravity | Delta) + (velocity * velocity)) - ((Gravity | Gravity)*(Delta | Delta)));
float time2 = 2.0f * FMath::Square<float>((((Gravity | Delta) + (velocity * velocity) + PlusMinus) / (2.0f * (Gravity | Gravity))));
float time3 = 2.0f * FMath::Square<float>((((Gravity | Delta) + (velocity * velocity) - PlusMinus) / (2.0f * (Gravity | Gravity))));

float ttt = 0.0f;
if (time2 < 0.0f && time3 < 0.0f) { LOG("No valid times."); return; }
if (time2 < 0.0f && time3 >= 0.0f) { ttt = time2; }
if (time2 > 0.0f && time3 < 0.0f) { ttt = time3; }
if (time2 > 0.0f && time3 >= 0.0f) { ttt = FMath::Min<float>(time2, time3); }

FVector Result = ((2.0f * Delta) - (Gravity * FMath::Square<float>(ttt))) / (2.0f * velocity * ttt);


I feel like I did the right thing. The end result is that I can fire at an angle, but the angle is too sensitive. One slight bump, and the trajectory goes off by a lot. Does anyone know what other ways to tweak the values in order to get the trajectory correct?

A quick update:

Someone suggested that I switch the value inside the Sqrt() for the FVectors to be Size(). It could be that the sqrt() is looking for the magnitude of the vectors. I tried it, but gotten wonky angles. Any other suggestions?

Posting the solution here. Life is complete!



void ATargetPawn::Fire(){
	FVector Delta = this->GetActorLocation() - this->MainCannon->GetActorLocation();
	FVector Gravity(0.0f, 0.0f, this->GetWorld()->GetGravityZ());
	float MuzzleVelocity = 1024.0f;

	float a = FVector::DotProduct(Gravity, Gravity);
	float b = -4.0f * (FVector::DotProduct(Gravity, Delta) + MuzzleVelocity * MuzzleVelocity);
	float c = 4.0f * FVector::DotProduct(Delta, Delta);

	if (4.0f * a * c > b * b) {
		LOG("Out of Bounds.");
		return;
	}

	float Disc = FMath::Sqrt(b * b - 4.0f * a * c);
	float MinTime = FMath::Sqrt((-b - Disc) / (2.0f * a));

	FVector Result = (2.0f * Delta - Gravity * (MinTime * MinTime)) / (2.0f * MuzzleVelocity * MinTime);
	
	//Spawn projectiles
	FActorSpawnParameters Params;
	Params.Owner = this->MainCannon;
	Params.Instigator = this->Instigator;
	AProjectile* const Projectile = this->GetWorld()->SpawnActor<AProjectile>(AProjectile::StaticClass(), this->MainCannon->GetActorLocation(), FRotator::ZeroRotator, Params);
	if (Projectile){
		//FVector LaunchDirection = RotatedResult.RotateVector(One);
		Projectile->SetCutOffBoundary(this->GetActorLocation().Z);
		Projectile->InitialVelocity(Result, 1.0f);
		FRotator OppositeRoll = FRotator(0.0f, 0.0f, Result.Rotation().Pitch - 180.0f);
		this->MainCannon->SetActorRotation(OppositeRoll);
	}
}


Source code is provided below. Only the code is provided, no other contents/models/meshes, etc. File too large to attach.