1
\$\begingroup\$

I have a sprite which I need to move along a curve between two points in 2D space. Speed must be consistent all the way. No physics involved. How can I achieve this? enter image description here

The object is sun sprite.

enter image description here

\$\endgroup\$
6
\$\begingroup\$

You can use Bezier Curves.

Given 2 given points, you have to define one third point yourself that controls where the curve is made. In your example you can create the point between your two given points, plus some height.

// Assuming point[0] and point[2] are your starting point and destination
// and all points are Vector3
point[1] = point[0] +(point[2] -point[0])/2 +Vector3.up *5.0f; // Play with 5.0 to change the curve

Then to compute the curve in a script for an object to travel to it, do this:

float count = 0.0f;

void Update() {
    if (count < 1.0f) {
        count += 1.0f *Time.deltaTime;

        Vector3 m1 = Vector3.Lerp( point[0], point[1], count );
        Vector3 m2 = Vector3.Lerp( point[1], point[2], count );
        myObject.position = Vector3.Lerp(m1, m2, count);
    }
}

Basically, to compute a Quadratic Bezier Curve (one with three points) you have to find the interpolated points of it's two lines, construct a line with those two points (The green line in the gif), and interpolate again to find a final point.

enter image description here

As you can see, if you increase the Y value on your second point, the curve will be bigger.

Edit:

If you want to create moving objects like a sun and a moon, create 3 Vector3 points: starting point (where the sun begins travelling) an ending point (where the sun ends up) and a control point (that controls the curve). The control point should be located above the mountains, but the exact value depends on what kind of curve you are looking for.

Now substitute these points with the above example:

point[0] = startingPoint;
point[1] = controlPoint;
point[2] = endingPoint;

I assume that once the sun (or moon) reaches the ending point, they can just disappear from the scene, and re-appear on the next day on the starting point, so it's location until then is irrelevant.

\$\endgroup\$
  • \$\begingroup\$ What if I have two objects for moving. Like sun and moon. And I want them to take turns. What is it with counting then? \$\endgroup\$ – Random generalist Apr 17 '18 at 10:59
  • \$\begingroup\$ @NikitaDemidenko I edited my answer with a sun and moon example \$\endgroup\$ – TomTsagk Apr 17 '18 at 11:06
  • \$\begingroup\$ It is moving to fast. Just instantly sprite is at the finish point no mater how I tweak count value. \$\endgroup\$ – Random generalist Apr 17 '18 at 11:45
  • \$\begingroup\$ @NikitaDemidenko Can you post some code? I'm afraid there is nothing I can do, since this method I posted work, unless I see what you did wrong in the code. \$\endgroup\$ – TomTsagk Apr 17 '18 at 11:57
  • \$\begingroup\$ I have created more elaborated question gamedev.stackexchange.com/questions/157646/… I accept your answer here cause I think it is valid, it is just I do something wrong. \$\endgroup\$ – Random generalist Apr 17 '18 at 12:05
3
\$\begingroup\$

Myself, I'd use a circular arc for this - that way it's easy to get consistent speed.

IEnumerator FollowArc(
        Transform mover,
        Vector2 start,
        Vector2 end,
        float radius, // Set this to negative if you want to flip the arc.
        float duration) {

    Vector2 difference = end - start;
    float span = difference.magnitude;

    // Override the radius if it's too small to bridge the points.
    float absRadius = Mathf.Abs(radius);
    if(span > 2f * absRadius)
        radius = absRadius = span/2f;

    Vector2 perpendicular = new Vector2(difference.y, -difference.x)/span;
    perpendicular *= Mathf.Sign(radius) * Mathf.Sqrt(radius*radius - span*span/4f);

    Vector2 center = start + difference/2f + perpendicular;

    Vector2 toStart = start - center;
    float startAngle = Mathf.Atan2(toStart.y, toStart.x);

    Vector2 toEnd = end - center;
    float endAngle = Mathf.Atan2(toEnd.y, toEnd.x);

    // Choose the smaller of two angles separating the start & end
    float travel = (endAngle - startAngle + 5f * Mathf.PI) % (2f * Mathf.PI) - Mathf.PI;

    float progress = 0f;
    do {
        float angle = startAngle + progress * travel;
        mover.position = center + new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * absRadius;
        progress += Time.deltaTime/duration;
        yield return null;
    } while (progresss < 1f);

    mover.position = end;
}

Now you can start the object moving with:

StartCoroutine(FollowArc(sunSprite, sunrisePoint, sunsetPoint, radius, daylightDuration));
\$\endgroup\$
  • \$\begingroup\$ Seems to be a good solution for speed consistency. But does it provide trajectory adjustment? I mean is it only perfectly round or is it adjustable to stretch arcs height but preserve start and finish points coordinates? Sorry if it is obvious in the code. I need a day or two to digest it :) \$\endgroup\$ – Random generalist Apr 17 '18 at 12:50
  • \$\begingroup\$ This particular version is perfectly round, though you can shape how steep or shallow an arc you want by varying the radius parameter (bigger radii = shallower, straighter arc) \$\endgroup\$ – DMGregory Apr 17 '18 at 12:59
  • \$\begingroup\$ @DMGregory please take a look at this for me gamedev.stackexchange.com/questions/157685/… \$\endgroup\$ – Hilarious404 Apr 19 '18 at 3:34
  • \$\begingroup\$ Can you explain why you multiple the perp by a trig function plus the square root? I don't understand that. \$\endgroup\$ – WDUK Aug 13 '18 at 2:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.