0
\$\begingroup\$

Okay, so I think I need to use a tween in order to move an object in my game. I have some little green dudes that need to move in an arc (bezier) to a location that is clicked by the user. My game functions by having the mouse point raycast to the screen giving my character a position to move to. They don't just run though, they fire off their jetpacks and should travel in an arc to the target position. I'm pretty new to Unity but all I can come up with is using a tween to do this. Am I missing something? That being said, I'm totally confused on what a tween actually is. I can't wrap my head around how the object can be moved outside of an update function. I'm using the "free from the asset store" LeanTween package. I feel like there should be some sort of easier way to achieve the desired behavior.

How can I have my character move from Vector3 A to Vector3 B in an arc?

\$\endgroup\$
  • \$\begingroup\$ You want them to move along a bezier curve or just a parabolic arc (like lofting a ball)? The latter is much simpler. \$\endgroup\$ – Fuzzy Logic Sep 23 '15 at 1:58
  • \$\begingroup\$ Parabolic arc. I guess I only mentioned bezier curves cause I think that's how LeanTween works. However, I'm open for whatever is easiest as long as it creates the desired effect. \$\endgroup\$ – GeoJohn Sep 23 '15 at 19:07
  • \$\begingroup\$ Daniel has provided the answer for traveling in a parabolic arc (moving on an elliptical path). Cheers \$\endgroup\$ – Fuzzy Logic Sep 23 '15 at 19:15
3
\$\begingroup\$

I went ahead and made it since I think it could be useful for me in the future. This implementation uses ellipses to determine the object location. All you have to provide is the speed, height of the ellipse, and start and end locations.

Edit: I modified it to support slowing down near the end. It uses new decayStart and decayAmount variables. Both of these variable should be between 0 and 1. If you ever want it to slow down near the beginning as well you could do something similiar to this.

using UnityEngine;
using System;

public class Jetpack : MonoBehaviour {

    private JetPackPath path;
    private double speed = 1, height = 5, decayStart = .75, decayAmount = .25;
    private bool jetPackOn;

    private void FixedUpdate() {
        MoveWithJetPack();
    }

    private void Update() {
        GetJetPackOn();
    }

    private void MoveWithJetPack() {
        if (jetPackOn) {
            transform.position = path.GetPosition();
            if(path.IsFinished()) {
                transform.position = path.GetEndPosition();
                jetPackOn = false;
            }
        }
    }

    private void GetJetPackOn() {
        if(!jetPackOn && Input.GetMouseButtonDown(0)) {
            InitializeJetPackSequence();
        }
    }

    private void InitializeJetPackSequence() {
        Vector3 startPosition = transform.position;
        Vector3 endPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        path = new JetPackPath(startPosition, endPosition, speed, height, decayStart, decayAmount);
        jetPackOn = true;
    }
}

public class JetPackPath {
    private Vector3 startPosition, endPosition, distance;
    private double height, width;
    private double position, speed;
    private double decayAmount, decayStart, decayWindow;

    public JetPackPath(Vector3 startPosition, Vector3 endPosition, double speed, double height, double decayStart, double decayAmount) {
        this.startPosition = startPosition;
        this.endPosition = endPosition;
        this.speed = speed;
        this.height = height;
        this.decayStart = decayStart;
        this.decayAmount = decayAmount;
        distance = endPosition - startPosition;
        width = .5;
        decayWindow = 1 - decayStart;
    }

    public Vector3 GetPosition() {
        position += GetSpeed() * Time.fixedDeltaTime;
        return startPosition + new Vector3((float)GetXPosition(), (float)GetYPosition(), (float)GetZPosition());
    }

    public double GetSpeed() {
        if(position >= decayStart) {
            double decay = position - decayWindow;
            double proportion = decay / decayWindow;
            return speed - (proportion * speed * decayAmount);
        }
        return speed;
    }

    public Vector3 GetEndPosition() {
        return endPosition;
    }

    public bool IsFinished() {
        return position >= 1.0;
    }

    private double GetXPosition() {
        return position * distance.x;
    }

    private double GetYPosition() {
        double yOffset = distance.y * position;
        double yPosition = GetSquareRoot();
        if (double.IsNaN(yPosition)) yPosition = 0;
        return yPosition + yOffset;
    }

    private double GetZPosition() {
        return position * distance.z;
    }

    private double GetSquareRoot() {
        double fraction = Squared(position - width) / Squared(width);
        double toBeSquareRooted = (1 - fraction) * Squared(height);
        return Math.Sqrt(toBeSquareRooted);
    }

    private double Squared(double value) {
        return Math.Pow(value, 2);
    }
}
\$\endgroup\$
  • \$\begingroup\$ Okay this is awesome! I've implemented it and it works great. If I may however... I'd like to pick your brain a little... Would it be possible to alter the speed based on the position of the flight? I would like the characters to slow down as they are coming in for their landing. If you have any thoughts on this, let me know. \$\endgroup\$ – GeoJohn Sep 23 '15 at 23:45
  • 1
    \$\begingroup\$ I modified it to support slowing down near the end. It uses new decayStart and decayAmount variables. Both of these variable should be between 0 and 1. If you ever want it to slow down near the beginning as well you could do something similiar to this. \$\endgroup\$ – Daniel Sep 24 '15 at 0:23

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.