This looks like basic spring physics.
Each frame, you compute an acceleration vector, by taking the offset from your current position to your target position (the other end of the spring), and scaling it by the spring stiffness factor k
. The higher the k
value, the more sharply the spring pulls.
acceleration_x = k * (target_x - position_x)
acceleration_y = k * (target_y - position_y)
On frames where the mouse isn't clicking to attract the object, you can set acceleration_x/y
to zero to "release" the object and let it drift using its own momentum without outside interference.
Then you can integrate that acceleration over time to get a changing position, using your favourite numerical integration method. Here's a basic Euler method:
velocity_x = velocity_x * inertia + acceleration_x * deltaTime
velocity_y = velocity_y * inertia + acceleration_y * deltaTime
position_x = position_x + velocity_x * deltaTime
position_y = position_y + velocity_y * deltaTime
Here velocity_x/y
form an extra pair of state variables that you maintain about your object from one frame to the next. That gives it some memory of how it was moving, allowing it to retain some momentum in the old direction when the target is moved, creating that effect you described where the object can overshoot the target sideways and orbit around it, rather than only ever approaching/receding along the line joining the two points.
deltaTime
is the duration of one step of your physics simulation, in the appropriate units. (eg. if position
is measured in pixels and velocity
in pixels per second, then deltaTime
would be measured in seconds). Small timesteps of consistent duration help you get smooth, plausible physics.
inertia
is a constant you choose between 0 and 1, that controls how much of the speed is retained from one simulation step to the next. At high values, the object will overshoot the target and travel almost as far as its original displacement before turning around, making big, long-lasting oscillations. Lower values act like a dampened spring, or an object with friction, where the excess velocity bleeds off quickly and the object can settle down near the target without huge overshoots / long oscillations.
To keep the expression simple, I didn't adjust inertia
for the duration of your time step - basically assuming that you'll fix your time step first, then tune inertia to your liking. But if you want to dynamically adjust the inertia value to get similar behaviour under different time step values, you can use inertia = power(speedRemainingAfter1Second, deltaTime)
where speedRemainingAfter1Second
is a ratio between 0 and 1.