1
\$\begingroup\$

I'm pretty new to both programming and Unity, and I honestly have no idea how to accomplish what I want to do.

Scene: I have a Sphere which I can control with my keyboard. It rolls around on a plane using physics. Simple stuff.

What I want to do: Now, I want to be able to attach objects to this sphere which move along with the sphere, but should always stay upright. If it's possible to do this dynamically, with a bit of "sway" / smoothing to the correcting motion as the ball rolls around, that would be magical. :)

\$\endgroup\$
0

2 Answers 2

2
\$\begingroup\$

You can do this with a variation on this bobblehead script I wrote. Keep the spring stiffness high and the conservation low to make the object stay more upright and bounce less.

using UnityEngine;
 
public class Bobble : MonoBehaviour {    
    // We'll sproing about this transform's origin point.
    public Transform pivot;
 
    // Tunable parameters for how sharply the bobble spring pulls back,
    // and how long we keep bobbling after a shake.
    public float stiffness = 100f;
    [Range(0, 1)]
    public float conservation = 0.5f;
 
    // Used to constrain the bobble to a shell around the pivot.
    Vector3 _restOffset;
    float _radius;
 
    // Track physics state for Verlet integration.
    Vector3 _worldPosNew;
    Vector3 _worldPosOld;
    Vector3 _forward;
 
    // Used for interpolation between fixed steps.
    float _timeElapsed;
    
    private void Start() {
        // Cache our initial offset from the pivot as our resting position.
        // Remapped from pivot space to world space to stay upright.
        //_restOffset = pivot.InverseTransformDirection(
                        transform.position - pivot.position);
        _restOffset = transform.position - pivot.position;

        _radius = _restPosition.magnitude;
        _forward = transform.forward;
    }
        
    private void FixedUpdate() {
        // Compute a desired position our spring wants to push us to.

        // In the version in the tweet, this position would rotate
        // along with the pivot object.
        // Vector3 desired = pivot.TransformPoint(_restOffset);

        // But to stay upright above a rotating pivot, we'll want this
        // to be applied in world space.
        Vector3 desired = pivot.position + _restOffset;             
 
        // The further we are from this position, the more correcting force it applies.
        Vector3 acceleration = stiffness * (desired - _worldPosNew);
 
        // Step forward a new position using Verlet integration.
        Vector3 newPos = _worldPosNew + conservation * (_worldPosNew - _worldPosOld)
            + Time.deltaTime * Time.deltaTime * acceleration;
        _worldPosOld = _worldPosNew;
 
        // Constrain the bobble within our radius.
        _worldPosNew = ClampedOffset(newPos) + pivot.position;
 
        // Clear the accumulated time now that we have a new sample.
        _timeElapsed = 0f;
    }
 
    private void Update() {
        // Interpolate our position so we get nice smooth movement,
        // without stutters or beats with the FixedUpdate rate.
        _timeElapsed += Time.deltaTime;
        float t = (_timeElapsed / Time.fixedDeltaTime) % 1.0f;
        Vector3 blend = Vector3.Lerp(_worldPosOld, _worldPosNew, t);
 
        // Correct the interpolated position to one on the shell around our pivot.
        Vector3 offset = ClampedOffset(blend);
        transform.position = offset + pivot.position;
 
        // Orient so "up" points away from the pivot,
        // and "forward" aligns roughly to our cached forward direction.
        transform.rotation = Quaternion.LookRotation(offset, -_forward) 
            * Quaternion.Euler(90f, 0f, 0f);
    }
 
    Vector3 ClampedOffset(Vector3 position) {
        // Clamp our position onto a spherical shell surrounding the pivot
        // (as though we were swivelling on a rod of fixed length)
        Vector3 offset = position - pivot.position;
        return offset.normalized * _radius;
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Thank you so much for answering my question! I've tried your code now and I'm having some issues unfortunately; 1) the _restOffset was undefined, I defined it as Vector3, is that correct? 2) the object follows my ball but does not wobble at all 3) the object is tilted "forward", i.e. if a cylinder is the object following it will not stay horizontal but would flip. Again, thank you so much, I'm hoping we can fix this :) \$\endgroup\$
    – Mad Marvin
    Oct 8, 2021 at 7:08
  • \$\begingroup\$ 1) _restOffset is defined now. 2) Your conservation value might be too low 3) this code orients the object's "up" vector (its green one) away from the sphere. If you want to use a different vector, either wrap the object in a parent object or edit your question to show us how the local axes of your object should be arranged. Also note that it takes its rest position from the initial arrangement of the objects when the script starts up, so ensure your follower is directly above the sphere at start-up. \$\endgroup\$
    – DMGregory
    Oct 8, 2021 at 12:01
1
\$\begingroup\$

(I started to answer this before the post was closed soon after the question was posed and before I could finish it. DMGregory's answer is more complete and sophisticated, but I'm offering this anyway because I think it should be very easy for a beginner to understand.)

The first part of your question is pretty simple. For demo purposes, here's our set up:

enter image description here

We have a terrain that the sphere can roll around on. The sphere has a rigidbody and sphere collider.

enter image description here

For the "attachable" object we have a cube with no collider, scaled in one direction so we can see clearly its orientation. Attached to the cub(oid) is a custom script called AttachToSphere.

enter image description here

Here is that script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AttachToSphere : MonoBehaviour
{
    public Transform sphere; //the sphere we'll "attach" to 
    public Vector3 offset; // distance from the sphere we'll maintain
    float timer = 0; // used to time when we start and stop the attachment

    void Update()
    {
        timer += Time.deltaTime;

        if (timer > 2 && timer < 10)
        {
            transform.position = sphere.position + offset;
        }
    }
}

When the scene runs, the sphere drops and rolls around the terrain. The script's timer in the Update loop waits 2 seconds before coupling the cuboid to the sphere. the cuboid remains upright because it is simply tracing the position of the sphere, without being affected its rotation. After ten seconds, the cuboid detaches (stops follwing the sphere's position.) Very simplistic, but maybe useful as something to play with to begin to understand hown this stuff works. See DMGregory's answer for a much more in depth solution, that actually has the picked up object bobble slightly as the parent object moves around.

animated gif showing result

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for your thorough answer! It surely helped me in understanding coding more. :) The bobble answer above + your script to track the position (and not the rotation) actually works kinda well together. \$\endgroup\$
    – Mad Marvin
    Oct 8, 2021 at 8:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .