0
\$\begingroup\$

This is a follow-up question to How to reapply force applied to one object to another object

I want to make two objects move, react to collisions and other stuff as if they were one. For example, Objects A and B are entangled. When Object C rams into Object A and starts moving with it, Object B should start moving with it. When Object B hits a wall, Object A should act like it also hit a wall. I tried changing position/speed every frame, but this only works when done in one direction. Now, I want all forces applied to any of the objects to also apply to the other objects entangled to it.

Edit: This is the new code which only fails when multiple collisions happen at once

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

[RequireComponent(typeof(Rigidbody))]
public class EEScript : MonoBehaviour
{
    public List<EEScript> activeObjects;
    public new Rigidbody rigidbody;
    public Vector3 lastPosition;
    // Start is called before the first frame update
    void Start()
    {
        rigidbody = gameObject.GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {
        lastPosition = transform.position;
    }

    private void OnCollisionEnter(Collision collision)
    {
        foreach (EEScript entangled in activeObjects)
        {
            if (entangled == this) continue;
            Rigidbody rb = entangled.rigidbody;
            if (rb is null) continue;
            rb.velocity = rigidbody.velocity;
            rb.angularVelocity = rigidbody.angularVelocity;
            entangled.transform.position = entangled.lastPosition + transform.position - lastPosition;
        }
    }
}
\$\endgroup\$
0
\$\begingroup\$

Unfortunately, Unity's 3D collision structure doesn't contain all the information you need to compute angular effects from a collision unambiguously. We know the total linear impulse applied to the bodies but not the angular impulse.

But we can do our best to approximate this by keeping track of our previous angular velocity, and applying the difference to all our entangled partners.

[RequireComponent(typeof(Rigidbody))]
public class Entangled : MonoBehaviour {

    public Rigidbody[] entangledBodies;

    Rigidbody _body;
    Vector3 _lastKnownAngularVelocity;

    void Start() {
        _body = GetComponent<Rigidbody>();
        _lastKnownAngularVelocity = _body.angularVelocity;
    }

    void FixedUpdate() {
        _lastKnownAngularVelocity = _body.angularVelocity;
    }

    void ChangeAngularVelocity(Vector3 delta) {
        _body.angularVelocity += delta;
        _lastKnownAngularVelocity = _body.angularVelocity
    }

    void OnCollisionEnter(Collision collision) {
        // Unity sends the same impulse value to both participants in a collision,
        // even though one gets impulsed in the opposite direction. So which are we?
        Vector3 impulse = collision.impulse;
        if(Vector3.Dot(impulse, collision.GetContact(0).normal) < 0f)
            impulse *= -1f;

        Vector3 angularChange = _body.angularVelocity - _lastKnownAngularVelocity;

        // Transmit this impulse to all bodies entangled with this one.
        foreach(var entangled in entangledBodies) {
            entangled._body.AddForce(impulse, ForceMode.Impulse);
            entangled.ChangeAngularVelocity(angularChange);
        }
    }
}

Note that since we're only applying changes here, there are opportunities for differences to arise: from different initial conditions (if one body was already spinning at the time they became entangled), from different responses to changes (if one body has a different mass, pivot, moment of inertia, drag, or constraints), or from accumulation of small integration errors snowballing chaotically.

Instead of applying incremental pushes, you could instead set the linear & angular velocities of the entangled copies to those of the one most recently pushed by a collision (a role that could shift multiple times per frame when handling several collisions in a single physics tick). Or save an offset from a master object, and have all the entangled copies update their linear and angular velocities each frame to maintain that cached offset relative to the most recently pushed.

\$\endgroup\$
  • \$\begingroup\$ Setting linear and angular velocities as well as updating location after collision works well except for the case when one entangled object is pushed from one side and the other is blocked by something on the other side. \$\endgroup\$ – Dorijan Cirkveni Sep 13 at 15:04
  • \$\begingroup\$ Also, everything falls apart when there are multiple collisions at once. \$\endgroup\$ – Dorijan Cirkveni Sep 13 at 15:47
  • \$\begingroup\$ In real-world quantum physics, situations like that lead to decoherence. The objects that collide with some members of your entangled collection are effectively making observations of them, collapsing their wavefunctions (or themselves becoming entangled with particular outcomes, depending on your perspective). So if you want a different outcome there, you'll need to define in your game rules a policy for how these types of situations should be resolved. What's the expected outcome when an entangled object cannot follow its partner? \$\endgroup\$ – DMGregory Sep 13 at 16:11
  • \$\begingroup\$ The expected outcome is for the objects to act as if they were one object hit from both sides. \$\endgroup\$ – Dorijan Cirkveni Sep 13 at 16:16
  • \$\begingroup\$ Then it sounds like you might want a fixed joint as recommended to you on your previous Q&A post. \$\endgroup\$ – DMGregory Sep 13 at 16:27
0
\$\begingroup\$

physics engine that unity3d uses is non-deterministic.

it means you cant predict exactly how it will move. so you best approach for you is to use rigidBody for one object and make rigidBody of another, kinematic. and just set rotation of first object to another using Transform and move second one as first one moves with some offset.

\$\endgroup\$

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.