User Tag List

Results 1 to 15 of 15

Thread: Calculating Projectile Velocity for Parabolic Arc

  1. #1
    0
    Infiltrator
    Join Date
    Mar 2016
    Posts
    10

    Calculating Projectile Velocity for Parabolic Arc

    I'm having trouble finding the correct formula to calculate the velocity needed to propel an actor with a Projectile Movement Component from its start position to a known target.

    The start angle is predetermined, and so is known.
    The Target Coordinates are known.
    The direction and distance can be established.

    What I need to find is the Velocity.

    I really need the projectile to travel in an arc towards the target.

    I've made several attempts, and my most successful approach is below, which was adapted from a coding example in another forum.

    Code:
    			AShell* Proj = World->SpawnActor<AShell>(ProjectileBP, startPos, FRotator(newrot.Pitch, newrot.Yaw, newrot.Roll), SpawnParams);
    
    			float distance = (startPos - TargetPosition).Size();
    			float angleToPoint = atan2(TargetPosition.Z - startPos.Z, (TargetPosition - startPos).Size());
    			float distanceFactor = 1 / 10000;
    			float angleCorrection = (PI*0.18) * (distance * distanceFactor);
    			float velocityX = cos(angleToPoint + angleCorrection) * 3000;
    			float velocityZ = sin(angleToPoint + angleCorrection) * -1 * 3000 + 500;
    
    			FVector Vel = FVector(velocityX, 0, velocityZ); 
    			Proj->ProjectileComponent->SetVelocityInLocalSpace(Vel);
    This gives a semi satisfactory result, but the problem is that this does not take the launch angle into account and almost always either falls short of the target or goes too long for distant and near targets respectively. It is not at all accurate.

    I have a few cheat approaches that I may have to employ (such as a UCurve and distance to dictate the height based on the distance to the target), but I know there is a relatively simple formulaic solution.
    Unfortunately, mathematics is not my strong point, so I turn to you for help.

  2. #2
    1
    You can separate launch speed (V) into horizontal and vertical components:
    Vx = V*cos(Theta)
    Vz = V*sin(Theta),
    where Theta is the launch angle from the horizontal.

    Then using s = ut + (1/2)at^2, with a = -g vertically and a = 0 horizontally, putting it together yields the following ugly result:

    V = (Sx / cos(Theta)) * Sqrt(g / (2 * (Sx * tan(Theta) - Sz)))

    where Sx and Sz are the horizontal and vertical displacements to the target (so Sz is negative if the target is below the firing point). For g, use World->GetGravityZ().

    Once you have V, then the velocity vector is just [ V*cos(Theta), 0, V*sin(Theta) ].

    Hopefully I got all that right, let me know if you have problems.
    I'm working on a marketplace pack to cover all sorts of aiming calculations for trajectories, prediction, AI & aim assist, etc. Won't be done for a while yet though.

  3. #3
    0
    I'm assuming you don't mind that your projectile can launch with a variable speed?

    If not that's fine - otherwise it's a Quadratic Equation.

  4. #4
    0
    Infiltrator
    Join Date
    Mar 2016
    Posts
    10
    It would appear a quadratic equation might well be what I need.

    I'm currently reading through this:
    https://blog.forrestthewoods.com/sol...s-b0165523348c

    I know this should be simple, but I'm afraid that I find it so slow to read algebraic formula not in code form.

    If I have any success, then I will be sure to post it here!

    Also; my previous reply doesn't seem to have appeared. Does it need moderating?

    Edit: Rewritten response to Kamrann.

    So, below is my current implementation of your maths, but I appear to be missing something as the projectiles always fall short.

    I'm logging the distance and height difference, and they appear to be correct.

    Code:
    			float Theta = 30.f; // launch angle
    
    			FVector dir = TargetPosition - startPos;
    			float Sz = dir.Z;// height difference
    			dir.Z = 0;
    			float Sx = dir.Size();// distance
    
    			float V = (Sx / cos(Theta)) * FMath::Sqrt(Gravity / (2 * (Sx * tan(Theta) - Sz)));
    
    			FVector VelocityOutput = FVector(V*cos(Theta), 0, V*sin(Theta));
    
    			UE_LOG(LogTemp, Warning, TEXT("Distance =  %f"), Sx);
    			UE_LOG(LogTemp, Warning, TEXT("Height  =  %f"), Sz);
    Am I misinterpreting what you're saying here?

    Then using s = ut + (1/2)at^2, with a = -g vertically and a = 0 horizontally,
    Last edited by Greywacke; 04-21-2017 at 06:17 AM.

  5. #5
    0
    Yeah I was assuming variable speed is expected, otherwise with fixed distance, launch angle and launch speed, there won't be any solutions.

    It's possible you post is awaiting moderation since you're new, but then I don't see why subsequent posts would get through.

    If code is simpler, this engine function is doing essentially the same thing as what I posted. Though it does more besides, so for your case you'd need to extract the relevant bits.

  6. #6
    0
    Infiltrator
    Join Date
    Mar 2016
    Posts
    10
    I understand. The velocity of the projectile is what I want to return from this calculation, in order to apply it to the projectile, so no, the speed is not fixed. The angle however is fixed; primarily for the sake of simplicity.
    I'm not sure how to adjust my above code to give me the correct velocity.

    Not sure if I can embed gifs, but the following links demonstrate the results I'm getting:

    Close Range:
    https://i.gyazo.com/b7c3d4561dbc12af...daff3e353e.gif

    Far Range:
    https://i.gyazo.com/195338553e2af816...3165d7cb17.gif

    Apologies for the poor quality!
    The Close Range shots appear to be on target, but the Far Range ones still fall short.

    In order to get this result, I've had to angle the projectile on spawn too. Without doing that , the projectiles were moved barely a few feet. Is this per chance a weight issue?

    I'm also getting a 404 error from that link.

    Thanks.
    Last edited by Greywacke; 04-21-2017 at 06:53 AM.

  7. #7
    1
    Didn't see your edit previously.

    Are you working in 2D only (X and Z)? I'd assumed so from the way you set Y to 0 in your first post, but the way you set dir.Z to 0 then take Size suggests maybe you're in 3D?

    Just to confirm, I believe your Gravity variable should be +9.8, not -9.8. And you should check that term isn't negative before passing to Sqrt (if it is, it means no solution, though that shouldn't happen for sensible inputs in this case).

    Finally, what are the trig functions you're using? C-runtime? In the code you pasted you've passed in the angle in degrees, nearly all trig functions expect input in radians (= degrees * PI / 180).

  8. #8
    0
    Infiltrator
    Join Date
    Mar 2016
    Posts
    10
    Yes, I am in 3D. I do indeed set Z to 0 in order to get the distance on the X, Y plane only.

    I'm also using World()->GetGravityZ();

    I've also just converted the launch angle to radians.
    Code:
    float Theta = (40 * PI / 180); // launch angle
    And below is how I'm actually spawning the projectile (note how I'm adding the pitch):
    Code:
     
    FRotator newrot = UKismetMathLibrary::FindLookAtRotation(startPos, TargetPosition);
    AShell* Proj = World->SpawnActor<AShell>(ProjectileBP, startPos, FRotator(newrot.Pitch + 40, newrot.Yaw, newrot.Roll), SpawnParams);
    Also, these are the sorts of values I'm getting from my debug logs:
    LogTemp:Warning: Distance = 5476.720703
    LogTemp:Warning: Distance = 2737.744141
    LogTemp:Warning: Height = -202.580521
    So, as you can see, the projectile spawn is higher than the target position.
    That shouldn't be a problem, should it?

    I'm getting a satisfactory arc now, but, for distant targets, the range seems too short to reach.


    Edit:Name:  17fbfbe35e7acee659da0a96c9dd98a3.png
Views: 38
Size:  15.8 KB

    Is it possible that the problem lies with the Movement Component (and my misunderstanding of its functionality) rather than the code?

    Adjusting the speeds here yields different results, but I would have thought that the velocity would dictate the speed?
    Last edited by Greywacke; 04-21-2017 at 07:34 AM.

  9. #9
    0
    The formula assumes launch angle to be relative to the horizontal. So you shouldn't be adding pitch to the lookat rotation. That would indeed cause the projectile to fall short if the target was below the launch position. Just use
    Code:
    FRotator(40, lookat.Yaw, 0)
    Also, not sure what you're doing with VelocityOutput, since the way that was coded assumed 2D. I think you need to be passing FVector(V, 0, 0) to the SetVelocityInLocalSpace method on the UProjectileMovementComponent, immediately after spawning your actor.

  10. #10
    0
    Infiltrator
    Join Date
    Mar 2016
    Posts
    10
    I hadn't just though about the pitch I was using.
    Made those two changes, but the results stills seem incorrect. The current lead seems to suggest that Initial speed dictates how far the projectiles go, and not the velocity set after spawning.

    After adding the below line of code, setting velocity to 0, the projectile now simply falls to the ground, which suggest the results of the calculation was never being applied at all!
    Going to have to do some investigating...

    Code:
    AShell* Proj = World->SpawnActor<AShell>(ProjectileBP, startPos, FRotator(40, newrot.Yaw, 0), SpawnParams);
    Proj->ProjectileComponent->SetVelocityInLocalSpace(FVector(0,0,0));
    Edit:
    So I'm getting this from printing part of the output velocity vector
    LogTemp:Warning: VelocityOutput = -nan(ind)
    Last edited by Greywacke; 04-21-2017 at 08:11 AM.

  11. #11
    0
    VelocityOutput shouldn't exist anymore, at least not in the form it was when you last posted the code.

    Maybe just post everything as you have it now.

  12. #12
    0
    Infiltrator
    Join Date
    Mar 2016
    Posts
    10
    Thanks for your patience btw

    I've narrowed down where that log is coming from. Bear with me...

    So, printing V produces "-nan(ind)"
    Code:
     float V = (Sx / cos(Theta)) * FMath::Sqrt(Gravity / (2 * (Sx * tan(Theta) - Sz)));
    I attempted to break down and log sections of the equation and these are the results:

    Code:
    UE_LOG(LogTemp, Warning, TEXT("V =  %f"), (Gravity / (2 * (Sx * tan(Theta) - Sz))) );
    This produces
    LogTemp:Warning: Gravity / (2 * (Sx * tan(Theta)- Sz)) = -0.171647
    Code:
    UE_LOG(LogTemp, Warning, TEXT("V =  %f"), FMath::Sqrt(Gravity / (2 * (Sx * tan(Theta) - Sz))));
    So, when finding the Square root, I get the -nan(ind) message.



    This suggests that something is wrong with the calculation of V, or one of the variables which are fed into it.

    As for the current code block, it is as follows:
    Code:
    			FVector TargetPosition = currentTargetRegion->GetRandomPointInVolume();
    
    			FRotator newrot = UKismetMathLibrary::FindLookAtRotation(startPos, TargetPosition);
    			AShell* Proj = World->SpawnActor<AShell>(ProjectileBP, startPos, FRotator(40, newrot.Yaw, 0), SpawnParams);
    			Proj->ProjectileComponent->SetVelocityInLocalSpace(FVector(0,0,0));
    
    			const float Gravity = World->GetGravityZ();
    			float Theta = (40 * PI / 180); // launch angle
    
    			FVector dir = TargetPosition - startPos; //direction
    			float Sz = dir.Z;// height difference
    			dir.Z = 0; // remove hight from direction
    			float Sx = dir.Size();// distance
    
    			float V = (Sx / cos(Theta)) * FMath::Sqrt(Gravity / (2 * (Sx * tan(Theta) - Sz)));
    
    			FVector VelocityOutput = FVector(V*cos(Theta), 0, V*sin(Theta));
    
    			Proj->ProjectileComponent->SetVelocityInLocalSpace(VelocityOutput);
    Last edited by Greywacke; 04-21-2017 at 08:55 AM. Reason: Added log quote

  13. #13
    1
    Try this:
    Code:
    FVector TargetPosition = currentTargetRegion->GetRandomPointInVolume();
    FRotator newrot = UKismetMathLibrary::FindLookAtRotation(startPos, TargetPosition);
    
    const float Gravity = World->GetGravityZ();
    float Theta = (40 * PI / 180); // launch angle
    
    FVector dir = TargetPosition - startPos; //direction
    float Sz = dir.Z;// height difference
    dir.Z = 0; // remove hight from direction
    float Sx = dir.Size();// distance
    
    float Discriminant = Gravity / (2 * (Sx * tan(Theta) - Sz));
    if(Discriminant > 0)
    {
      float V = (Sx / cos(Theta)) * FMath::Sqrt(Discriminant);
    
      FVector LocalVelocity = FVector(V, 0, 0);
    
      AShell* Proj = World->SpawnActor<AShell>(ProjectileBP, startPos, FRotator(40, newrot.Yaw, 0), SpawnParams);
      Proj->ProjectileComponent->SetVelocityInLocalSpace(LocalVelocity);
    }
    If the discriminant term is negative, it means there are no possible solutions for the given inputs, so you just shouldn't fire. For example, if you tried to fire at something that is at 60 degrees up with a launch angle of only 40 degrees.
    If you're finding the discriminant is negative when you think your inputs are valid, then log the value of Gravity to check it's not negative. If it is, negate it.

  14. #14
    0
    Infiltrator
    Join Date
    Mar 2016
    Posts
    10
    You were correct about the gravity, and i had spotted that problem after rereading an earlier post of yours.

    So, I only ever fire at targets that are lower than the origin's Z axis.

    I have to leave the computer for a while now, but I will take a look at your code sample when I return.

    Currently, the velocity does appear to be applied (and since fixing the gravity, now prints), but seems to be far lower than it should be.

    I really thought this would have been far simpler that I'm making it. =P

    Edit: RESULT!

    I need to run a few more checks when return later before I confirm, but it now appears to be working! For your sake, and Mine I hope this is true.
    Last edited by Greywacke; 04-21-2017 at 09:15 AM.

  15. #15
    0
    Infiltrator
    Join Date
    Mar 2016
    Posts
    10
    Here is my final, working code - for the benefit of anyone else whom it may help.

    Code:
    	FActorSpawnParameters SpawnParams;
    	SpawnParams.Owner = this;
    	SpawnParams.Instigator = Instigator;
    
    	FVector TargetPosition = currentTargetRegion->GetRandomPointInVolume();
    
    	const FRotator newrot = UKismetMathLibrary::FindLookAtRotation(startPos, TargetPosition);
    	AShell* Proj = World->SpawnActor<AShell>(ProjectileBP, startPos, FRotator(0, newrot.Yaw, 0), SpawnParams);
    	Proj->ProjectileComponent->SetVelocityInLocalSpace(FVector(0, 0, 0));
    
    	//  == Fixed Angle Projectile Velocity Calculation == 
    
    	const float Gravity = World->GetGravityZ() * -1; // Gravity. (Must be a positive value)
    	//const float Gravity = 980.f; // This is the same as the line above (unless your project settings have been changed)
    	const float Theta = (40 * PI / 180); // Launch angle in radians (40 being the launch angle in degrees)
    
    	FVector dir = TargetPosition - startPos; //direction
    	float Sz = dir.Z; // Height difference
    	dir.Z = 0; // Remove hight from direction
    	float Sx = dir.Size(); // Distance
    
    	const float V = (Sx / cos(Theta)) * FMath::Sqrt((Gravity * 1) / (2 * (Sx * tan(Theta) - Sz)));
    	FVector VelocityOutput = FVector(V*cos(Theta), 0, V*sin(Theta));
    			
    	Proj->ProjectileComponent->SetVelocityInLocalSpace(VelocityOutput);
    So, all along it was a series of red herrings, and the original code/ algorithm was largely correct.

    One thing you may notice, however, that the value of gravity in UE4 is -980. I found I had to move the decimal point as originally I was using 9.81f as gravity, and you will note that the value also must be positive.

    Again, thank you for all of the help!

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •