How to make an object seek and decelerate to a stop exactly on a specified position?

c#, logic, math, unity-game-engine

Solution

Adapt the first script:

Introduce a variable `velocity`, as in the second script. Set this equal to `movementSpeed` in `Start()`, and don't use `movementSpeed` after that. Do not proceed until this works perfectly.

Now introduce acceleration:

if (distanceToEndPoint < slowingDistance)
{
   velocity *= distanceToEndPoint/slowingDistance;
}
else
{
   velocity += direction * 2.0f * Time.deltaTime;
}

Problem

Introduction I am currently trying to create a script (C# script in Unity) which controls point and click behaviour for a GameObject. In essence, the player will - in the end - control a hot air balloon by simply clicking on the screen, whereby the object will start to accelerate and move toward the position of the click. When at a specific distance of the click position, the hot air balloon should then start to accelerate in the opposite direction (thus decelerating) and come to a complete halt exactly at the position of the click. I have already created a fully functional and commented script (found at the bottom of this post), which simply moves a GameObject towards the position of a click at a constant speed, and then, when within a specific distance, starts to slow down until stopping at the point. I have also created a separately and fully functional script which moves toward the position of a click while accelerating (getting that kind of asteroids thrusting behaviour). This can also be found at the bottom of the post. The Problem Now the problem that I'm facing is that I've been working quite a while now to find a solution where you can take these two behaviours and implement them as one working script, that is, the hot air balloon have the accelerating behaviour while also slowing down and stopping at a target position, exactly like this visual example found here: Visual Arrival Behaviour Demonstration The Question My question then becomes: how can you make the arrival behaviour not only using constant speeds, but with acceleration included in the equation as well? I've tried to research this problem as well as experimenting with my own solutions, but nothing I do seems to work quite the way it should. Script attachments Point and click movement at constant speeds with arrival behaviour ``` using UnityEngine; using System.Collections; public class PlayerControl : MonoBehaviour { // Fields Transform cachedTransform; Vector3 currentMovementVector; Vector3 lastMovementVector; Vector3 currentPointToMoveTo; Vector3 velocity; int movementSpeed; Vector3 clickScreenPosition; Vector3 clickWorldPosition; float slowingDistance = 8.0f; Vector3 desiredVelocity; float maxSpeed = 1000.0f; // Use this for initialization void Start () { cachedTransform = transform; currentMovementVector = new Vector3(0, 0); movementSpeed = 50; currentPointToMoveTo = new Vector3(0, 0); velocity = new Vector3(0, 0, 0); } // Update is called once per frame void Update () { // Retrive left click input if (Input.GetMouseButtonDown(0)) { // Retrive the click of the mouse in the game world clickScreenPosition = Input.mousePosition; clickWorldPosition = Camera.main.ScreenToWorldPoint(new Vector3(clickScreenPosition.x, clickScreenPosition.y, 0)); currentPointToMoveTo = clickWorldPosition; currentPointToMoveTo.z = 0; // Calculate the current vector between the player position and the click Vector3 currentPlayerPosition = cachedTransform.position; // Find the angle (in radians) between the two positions (player position and click position) float angle = Mathf.Atan2(clickWorldPosition.y - currentPlayerPosition.y, clickWorldPosition.x - currentPlayerPosition.x); // Find the distance between the two points float distance = Vector3.Distance(currentPlayerPosition, clickWorldPosition); // Calculate the components of the new movemevent vector float xComponent = Mathf.Cos(angle) * distance; float yComponent = Mathf.Sin(angle) * distance; // Create the new movement vector Vector3 newMovementVector = new Vector3(xComponent, yComponent, 0); newMovementVector.Normalize(); currentMovementVector = newMovementVector; } float distanceToEndPoint = Vector3.Distance(cachedTransform.position, currentPointToMoveTo); Vector3 desiredVelocity = currentPointToMoveTo - cachedTransform.position; desiredVelocity.Normalize(); if (distanceToEndPoint < slowingDistance) { desiredVelocity *= movementSpeed * distanceToEndPoint/slowingDistance; } else { desiredVelocity *= movementSpeed; } Vector3 force = (desiredVelocity - currentMovementVector); currentMovementVector += force; cachedTransform.position += currentMovementVector * Time.deltaTime; } } ``` Point and click movement using acceleration but no arrival behaviour ``` using UnityEngine; using System.Collections; public class SimpleAcceleration : MonoBehaviour { Vector3 velocity; Vector3 currentMovementVector; Vector3 clickScreenPosition; Vector3 clickWorldPosition; Vector3 currentPointToMoveTo; Transform cachedTransform; float maxSpeed; // Use this for initialization void Start () { velocity = Vector3.zero; currentMovementVector = Vector3.zero; cachedTransform = transform; maxSpeed = 100.0f; } // Update is called once per frame void Update () { // Retrive left click input if (Input.GetMouseButtonDown(0)) { // Retrive the click of the mouse in the game world clickScreenPosition = Input.mousePosition; clickWorldPosition = Camera.main.ScreenToWorldPoint(new Vector3(clickScreenPosition.x, clickScreenPosition.y, 0)); currentPointToMoveTo = clickWorldPosition; // Reset the z position of the clicking point to 0 currentPointToMoveTo.z = 0; // Calculate the current vector between the player position and the click Vector3 currentPlayerPosition = cachedTransform.position; // Find the angle (in radians) between the two positions (player position and click position) float angle = Mathf.Atan2(clickWorldPosition.y - currentPlayerPosition.y, clickWorldPosition.x - currentPlayerPosition.x); // Find the distance between the two points float distance = Vector3.Distance(currentPlayerPosition, clickWorldPosition); // Calculate the components of the new movemevent vector float xComponent = Mathf.Cos(angle) * distance; float yComponent = Mathf.Sin(angle) * distance; // Create the new movement vector Vector3 newMovementVector = new Vector3(xComponent, yComponent, 0); newMovementVector.Normalize(); currentMovementVector = newMovementVector; } // Calculate velocity velocity += currentMovementVector * 2.0f * Time.deltaTime; // If the velocity is above the allowed limit, normalize it and keep it at a constant max speed when moving (instead of uniformly accelerating) if (velocity.magnitude >= (maxSpeed * Time.deltaTime)) { velocity.Normalize(); velocity *= maxSpeed * Time.deltaTime; } // Apply velocity to gameobject position cachedTransform.position += velocity; } } ```

Original source