先看下最终要实现的效果:

当查找这个问题的资料时,发现多数的方案都是将物体设置为平台的子对象。
但是如果平台是非均匀缩放时,物体在移动或旋转时就会发生变形。
参考:Unity中父对象是非均匀缩放时出现倾斜或剪切现象
那有没有其他方法呢?
其实我们也可以不将物体设置为平台的子对象,而是通过管理刚体的实现。
先看代码:
using UnityEngine;
using System.Collections.Generic;
// CarryRigidbodies类负责让一系列Rigidbody对象跟随Unity GameObject移动。
public class CarryRigidbodies : MonoBehaviour
{
    // 存储所有应随本GameObject移动的Rigidbody对象的列表。
    public List<Rigidbody> rigidbodies = new List<Rigidbody>();
    // 存储上一帧物体的位置,用于计算移动距离。
    public Vector3 LastPosition;
    // 缓存当前GameObject的Transform组件。
    Transform _transform;
    // Start方法在脚本实例被激活时调用一次。
    void Start()
    {
        _transform = transform; // 初始化_transform为当前GameObject的Transform。
        LastPosition = _transform.position; // 初始化LastPosition为当前位置。
    }
    // LateUpdate在每一帧的Update函数调用之后调用。
    void LateUpdate()
    {
        // 如果列表中有Rigidbody对象,则处理它们的移动。
        if(rigidbodies.Count > 0)
        {
            // 遍历所有的Rigidbody。
            for(int i = 0; i < rigidbodies.Count; i++)
            {
                Rigidbody rb = rigidbodies[i];
                // 计算自上一帧以来的位置变化。
                Vector3 velocity = _transform.position - LastPosition;
                // 将位置变化应用到每个Rigidbody上。
                rb.transform.Translate(velocity, _transform);
            }
        }
        // 更新LastPosition为当前帧的位置。
        LastPosition = _transform.position;
    }
    // 当发生碰撞开始时调用。
    private void OnCollisionEnter(Collision other) 
    {
        // 尝试从碰撞对象中获取Rigidbody组件。
        Rigidbody rb = other.collider.GetComponent<Rigidbody>();
        if(rb != null)
        {
            // 如果存在Rigidbody,则添加到列表中。
            Add(rb);
        }    
    }
    // 当碰撞结束时调用。
    private void OnCollisionExit(Collision other) 
    {
        // 尝试从碰撞对象中获取Rigidbody组件。
        Rigidbody rb = other.collider.GetComponent<Rigidbody>();
        if(rb != null)
        {
            // 如果存在Rigidbody,则从列表中移除。
            Remove(rb);
        }    
    }
    // 添加一个Rigidbody到列表中,如果它尚未存在于列表中。
    void Add(Rigidbody rb)
    {
        if(!rigidbodies.Contains(rb))
        {
            rigidbodies.Add(rb);
        }
    }
    // 从列表中移除一个Rigidbody,如果它存在于列表中。
    void Remove(Rigidbody rb)
    {
        if(rigidbodies.Contains(rb))
        {
            rigidbodies.Remove(rb);
        }
    }
} 
上面脚本的核心实现原理是通过监听一个物体的位置变化,并将这个变化应用到与其相连的其他物体上,使得这些物体可以同步移动。这里面涉及的核心概念和方法包括:
位置追踪:
- 脚本通过记录物体的当前位置和上一帧的位置来计算位置的变化量(即位移向量)。这个位置变化量反映了物体在每一帧之间的移动。
 
同步位移:
- 使用Unity的Transform.Translate方法,将计算出的位移向量应用到所有附着的Rigidbody上。这个方法允许物体根据相对于其自身或另一个变换(在此案例中是脚本所依附的物体的变换)的位移向量进行移动。
 - 在LateUpdate方法中执行这个位移同步,是因为LateUpdate在所有Update方法之后执行,可以获取到所有物体的最终位置更新后,再统一进行位移操作,确保同步性和位置的准确性。
 
碰撞侦测与动态列表管理:
- 使用OnCollisionEnter和OnCollisionExit方法动态地管理附着的Rigidbody列表。这两个方法在Unity物理引擎处理碰撞的开始和结束时被调用。
 - 当一个物体开始与脚本附着的物体碰撞时,OnCollisionEnter被触发,脚本检查碰撞物体是否有Rigidbody组件,如果有,则加入到管理列表中。
 - 当碰撞结束时,OnCollisionExit触发,对应的Rigidbody从列表中移除。
 
性能考虑:
- 脚本中使用了变量_transform来缓存所附加GameObject的Transform组件的引用,这是为了减少在每次调用时重复获取这个组件的开销,优化性能。
 
那如何实现物体跟随平台同步旋转呢?
为了让附着的物体能够随着平台同步旋转,我们需要修改代码以跟踪平台的旋转变化,并将这些变化应用到所有附着的Rigidbody上。这可以通过计算每帧的旋转差异并应用这个差异来实现。下面是如何改进原有代码来支持旋转同步的详细步骤:
1. 添加跟踪旋转的变量: 在类中增加一个变量来存储上一帧的旋转:
public Quaternion LastRotation; 
2. 初始化旋转变量: 在Start方法中初始化这个变量:
void Start()
{
    _transform = transform;
    LastPosition = _transform.position;
    LastRotation = _transform.rotation;
} 
3. 计算旋转差异并应用: 在LateUpdate方法中,计算旋转的差异并应用到所有的Rigidbody上:
void LateUpdate()
{
    if (rigidbodies.Count > 0)
    {
        Vector3 velocity = _transform.position - LastPosition;
        Quaternion rotationDelta = _transform.rotation * Quaternion.Inverse(LastRotation);
        for (int i = 0; i < rigidbodies.Count; i++)
        {
            Rigidbody rb = rigidbodies[i];
            rb.transform.Translate(velocity, Space.World);
            rb.transform.rotation = rotationDelta * rb.transform.rotation;
        }
    }
    LastPosition = _transform.position;
    LastRotation = _transform.rotation;
} 
这段代码中,旋转差异rotationDelta是通过计算当前旋转与上一帧旋转的相对旋转得到的。这个旋转差异然后应用于所有附着的Rigidbody上,更新它们的旋转状态。
通过这种方式,任何附着的Rigidbody不仅会随着平台的移动而移动,也会随着平台的旋转而旋转,实现完全的同步。这对于制作需要物体随着移动平台旋转和平移的游戏场景(如旋转的平台或转盘等)非常有用。















![[极客大挑战 2019]BabySQL--详细解析](https://i-blog.csdnimg.cn/direct/0ecf9bfc1159481f85b128a889cfc9cf.png)



