Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How to make this kind of parabolic arc movement?

Discussion in 'Scripting' started by Paulx774, Nov 3, 2021.

  1. Paulx774

    Paulx774

    Joined:
    Mar 18, 2021
    Posts:
    103
    I'm trying to make a weapon like homing mortar. The behavior I want is shown in the picture.



    The black box represents the mortar and the red box represents the target.

    In this example, the number 1 shows the frame when the mortar is launched initially. The mortar only have upward velocity at that time. For example its velocity is 0,0,10. At the same time the target is moving away from the mortar. Basically, the mortar has to do parabolic arc movement to hit the target, however, since the target is moving, the mortar should do a movement like the number 2. The mortar will start to accelerate and when it gets close to the target it should slow down, so it can hit the target.

    The problem is a bit complex. The target can go around 3D world freely and there are some limitations:

    - The target's velocity may change while the mortar is flying. So, the mortar has to adjust its velocity.

    - The mortar shouldn't go backwards. For example; if the target stops and starts moving backwards the mortar shouldn't follow instead it should continue on its movement. Like it's shown in the image, the mortar should follow the blue line.



    So far, I tried to manipulate the X and Z resolutions (forward and right) of the mortar's velocity by calculating the distance and the direction between the target and the mortar, and I let the gravity to handle the Y resolution (up). By doing this, I got the number 2 movement perfectly. However, since I calculate the direction, the mortar always follows the target no matter the target starts moving backwards, which is not what I want.

    Any help is appreciated.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    As far as the "never goes back" thing, just make the mortar disengage its acceleration as soon as the X velocity becomes zero (or "too low," whatever that means to you).
     
  3. Paulx774

    Paulx774

    Joined:
    Mar 18, 2021
    Posts:
    103
    Good approach but implementing that logic is not possible, I guess because I rotate the mortar (black cube) depending on its velocity. So, its forward vector always changes
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Then what do you consider "backwards" motion?

    For my money it would be any motion that brings it closer to its original launch point.

    I imagine that could be detected by the dot product of the flattened current launch offset and the lateral velocity vector.
     
  5. Paulx774

    Paulx774

    Joined:
    Mar 18, 2021
    Posts:
    103
    That backward movement looks very weird. Imagine you launch a mortar and if target goes back towards you the mortar also turns back and coming back at you.

    First, I thought dot product as well but I don't have any forward vector since I rotate the mortar. Currently, I use the 2D and 3D distance between the mortar and the target. Depending on the mortar height and its 2D distance to the target, I stop changing its velocity. I'll see if it works in full game.
     
  6. ubbelito

    ubbelito

    Joined:
    Sep 24, 2018
    Posts:
    23
    Below uses bold for vectors and non-bold for scalars. I may have made a mistake somewhere, but it's an outline for a solution.

    So assuming that the projectile isn't seeking in the upward/downward and only changes velocity along the ground plane in order to hit the target... otherwise it could just stay afloat indefinitely and wait for the right time to strike.

    Compute the time T until it is again at ground level.
    0.5*a*sqr(t)+v0*t+s0 = s(t), now S(T) = S(0) = 0 (ground level at zero), s0 = 0 (starting at ground level)

    this gives
    0.5*a*sqr(T)+v0*T+s0 = 0, a*sqr(T)+2*v0*T=0, T=0 or T=-2*v0/a, here v0 is initial velocity when fired (upward direction, scalar, not a vector, as we're computing only the time until ground impact), a is ground acceleration, or 9.8m/sqr(s) on Earth.

    Knowing T, you can now compute the velocity it must have to hit the target.

    Let r = remaining time until impact, so r(t) = T - t, or r(t) = -2*v0/a - t

    Required velocity is simply the difference between the projectile current position Pp and position of the target Pt adjusted for its movement due to velocity Vt, divided by the remaining time, V(t) = (Pp - (Pt + Vt * r(t)) ) / r(t), here V(t) is the required velocity of the projectile.

    For each time frame, you accumulate this velocity by the frame time as usual.
    Code (csharp):
    1. transform.position += V(t) * Time.deltaTime;
    However, you also want to limit it so it does not move backwards. You can do this by clamping velocity V(t). Something along these lines.

    Code (csharp):
    1. Vector3 targetVector = Pt - Pp;
    2. Vector3 groundPlane = Vector3.forward; // you used z as up in your example, so I go with that here too.
    3. Vector3 requestVelocity = V(t);
    4.  
    5. Vector3 velocityInGroundPlane = Vector3.ProjectOnPlane(requestVelocity, groundPlane);
    6. if (Vector3.Dot(velocityInGroundPlane, targetVector) < 0) requestVelocity = Vector3.zero;
     
    alexeu and Paulx774 like this.
  7. Paulx774

    Paulx774

    Joined:
    Mar 18, 2021
    Posts:
    103
    Isn't the last line return true every time? (dot product). For example, the target is moving to the right and the mortar is getting close to the target. The target then changes its direction to the left. At some point, after the mortar passes the target, the direction (targetVector) will point left. You calculate the velocity depending on the direction (targetVector), which means when targetVector changes its direction, requestVelocity changes its direction as well. Since both vectors look at the same direction, the dot product will always be positive. Am I right or am I missing something?

    I kind of did something similar by only using the targetVector. If the distance is large, the mortar goes faster and when it gets close to the target, it slows down. (Basically, it's multiplying the targetVector with a float variable and setting the mortar's velocity). It works perfectly, but the mortar follows the target no matter the target changes its direction to the opposite. We're actually thinking the same thing, but since I rotate the mortar too, I don't have any reference vector to calculate the dot product. So, I can stop calculating the velocity, which leads mortar to do a normal parabolic arc.
     
  8. ubbelito

    ubbelito

    Joined:
    Sep 24, 2018
    Posts:
    23
    You are quite right. "targetVector" must be based on the original firing position and the target (velocity and position). Sorry about that.

    It may be tricky to implement something that looks good.

    I'm always hesitant to suggest alternatives, but one thing that could look cool and give you sort of what you're looking for is to fire the shell with an initial velocity given by the equations above (correcting my errors, ofc), but allow the shell to change velocity very slightly with each frame to move it closer to the request velocity.

    If the target maintains constant velocity these corrections should be zero, but if the target deviates a lot (either steers randomly, or accelerates/brakes), the small corrections may not be enough to compensate and the shell may miss.
     
    Paulx774 likes this.
  9. Paulx774

    Paulx774

    Joined:
    Mar 18, 2021
    Posts:
    103
    I can set the initial velocity like you do and lerp the current velocity to requestVelocity with a float variable, something called maneuver. As the mortar gets close to the target, its velocity change will ease out. Thanks.
     
    ubbelito likes this.