Ballistic Movement Algorithm

Hi there. I have an actor in my project, which is supposed to imitate the ballistic movement without overcomplicating it. So it has InitVelocity variable, which represents velocity in meters per second at the start of motion, StartLocation and StartRotation, and some other variables, that are shown here:

FVector ATarget3D::BallisticMovement()
{
	/*FVector drag = -CurrentVelocity.GetSafeNormal() * K * CurrentVelocity.SizeSquared() * p;
	FVector gravity = FVector(0.0, 0.0, -G * M);
	FVector force = drag + gravity;
	FVector acc = force / M;*/
	return (-CurrentVelocity.GetSafeNormal() * K * CurrentVelocity.SizeSquared() * p + FVector(0.0, 0.0, -G * M)) / M;
}

where

float M = 100.0;
float G = 9.8;
float K = 5; //change this later
float p = 1.2255;
FVector CurrentVelocity;

So basically what I do is I implement this formula


in code. Then, in BeginPlay, I run this code:

SetActorLocation(StartLocation);
SetActorRotation(StartRotation);
CurrentVelocity = StartRotation.Vector() * Velocity;

And to make this whole code work properly here’s what my Tick function has in it:

FVector old_pos = GetActorLocation();
CurrentVelocity += BallisticMovement() * DeltaTime;
SetActorLocation(old_pos + CurrentVelocity);
SetActorRotation(UKismetMathLibrary::FindLookAtRotation(old_pos, GetActorLocation()));

So here’s the problem. This code does actually send my actor by ballistical(parabolical) trajectory, but every time I start the game over and over again, the result trajectory faces different direction, has different height and lands on different location. So I wanted to ask how to fix that problem. However, it would be even better if somebody could provide me with different formula, which can be counted by time since launch, as I need to implement some sort of algorithm, which counts when and in which exact direction should missile be sent with set InitVelocity, so that the formula of ballistic trajectory also has to be very accurate. I’d be very thankful to see any possible suggestions or questions under this questions as I really need to implement this feature as soon as possible.

Formula looks familiar and is probably the same basic one we use.

Instead of making a custom actor, create a class based on UProjectileMovementComponent and override ComputeAcceleration() and/or ComputeVelocity().

One of the nice things UProjectileMovementComponent does natively is to subdivide long ticks specifically to make movement more closely follow as parabolic trajectory.

I’ve looked up what UProjectileMovementComponent is (sorry for stupid questions, I’m new into Unreal) and what ComputeAcceleration() does. However I still don’t really understand how am I supposed to make it work. So, from what I understood, I need to override ComputeAcceleration() with the code of my own BallisticMovement() function, but what do I do next? Could you please provide me with some information on how to make this whole thing work properly? And also I don’t really understand what does subdividing ticks has to do with this topic? Again, I’m sorry for probably being annoyingly unexperienced, but could you explain a bit more?

Just a small update. I still do not understand how using UProjectileMovementComponent can help me, rather just implement the same thing in a bit different-looking way. However, I’ve tried to log five different sessions, and here’s what I got:


These are states of FVector CurrentVelocity in every Tick execution. You don’t really need the numbers lower than you can see, because they just slowly decrease. The actual interesting part are the first few calls(different number of them in different sessions), as those are the ones where numbers somehow become extraweird, as they can suddenly become negative, multiply a few times, or get decreased by a huge number. This is where problem definitily comes from, but I can’t really understand why does it appear only in the first few calls, and why does it appear at all. Maybe any suggestions? Does it help?

What you have in BallisticMovement() would essentially be ComputeAcceleration() in the projectile movement component.

Check out the C++ first person example game to see how to use the project movement component.

You can also download the Unreal Tournament 4 source code and take a look at what it does with it.

There is probably tutorials floating around as well :slight_smile:

Yes, I understand, but what’s the difference between those two implementations? I think from what Logs are saying the problem is in BallisticMovement()’s code itself, not in how I execute this function.

I don’t know the exact formula, but if you want identical movement always for fast moving physics objects, consider using a fixed curve formula and then interp the object location at time T.

Any tick-based computations involving “newLocation = oldLocation + something” will give different results at different Tick() rates, different machines, etc. Over short distances the error isn’t much but it can compound pretty badly over time and distance (as you’ve seen).

Whereas, if your ballistic arc is a fixed formula, then any game session will interp the same location at time T given the same initial rotation, direction, and time of the shot.

That said I’ve never personally done this for ballistic arcs but I’ve done it with other curves to synchronize projectile locations in multiplayer.

1 Like

Yes, I tried using different code for this movement behavior:

FVector AProjectile2D::BallisticMovement()
{
	float t = GetWorld()->GetTimeSeconds() - TrajectoryStartMoment;
	//t *= 10;

	float Pitch = StartRotation.Pitch * PI / 180.0f;
	float Yaw = StartRotation.Yaw * PI / 180.0f;

	float V0x = Velocity * FMath::Cos(Pitch) * FMath::Cos(Yaw);
	float V0y = Velocity * FMath::Cos(Pitch) * FMath::Sin(Yaw);
	float V0z = Velocity * FMath::Sin(Pitch);

	float X = V0x * t + StartLocation.X;
	float Y = V0y * t + StartLocation.Y;
	float Z = V0z * t - 0.5f * t * G * t + StartLocation.Z;

	return FVector(X, Y, Z);
}

and set new location in Tick:

SetActorLocation(BallisticMovement());

And this one always works fine. But isn’t it the same thing? Doesn’t given Time/DeltaTime just change a point, which is part of final trajectory based on time since movement had started, without changing the result trajectory? If not then how do I fix my first formula to do so?

1 Like