The game object is deformed when the parent object is in non-uniform scaling.
先来看一下现象
有两个Cube, Cube1(Scale=2,1,1),Cube2(Scale=1,1,1)
将Cube2拖拽为Cube2的子对象。并且将position设置为(-0.6,1,0),也就是将Cube2置于Cube1上面并处于半悬空状态。为Cube2添加Rigidbody组件。点击"运行"。
这时,Cube2会在重力作用下向左倒下来,观察它的形状,会发现会拉长变形。
发现这个问题还是在研究如何将一个物体跟随平台进行移动时发现的。
网上给出的解决方法大多是将物体的父对象设置为平台。在测试时发现了物体变形的问题。
同样的,在Unity里编辑状态下,沿Y轴旋转Cube2,发现也会变形。
这是什么原因呢?
本质上还是父对象是非均匀缩放导致的。
首先来看下什么是非均匀缩放(non-uniform scaling)。
非均匀缩放是指 Transform 中的 Scale 具有不同的 x、y 和 z 值;例如 (2, 4, 2),也就是三轴上的缩放比例不一致。相反,均匀缩放具有相同的 x、y 和 z 值;例如 (3, 3, 3)。
那如何解决这个问题呢?
从Unity官方手册可以看到,还是提倡使用的3D模型保持均匀缩放的。当然如果我们在上面将Cube1的Scale设置为(1,1,1)时也不会出现这个问题。
-
不要在 Transform 组件中调整GameObject 的比例。如果您以真实比例创建模型,则无需更改 Transform 的比例。您还可以调整导入网格的比例,因为某些优化会根据导入大小进行。请在单个网格的导入设置中执行此操作。实例化具有调整后的比例值的游戏对象可能会降低性能。
-
如果子 GameObject 具有非均匀缩放的父 GameObject,并且相对于父 GameObject 旋转,则它可能会出现倾斜或"剪切"。有些组件支持简单的非均匀缩放,但在像这样倾斜时无法正常工作。例如,倾斜的盒子碰撞器与渲染网格的形状不准确匹配。
-
非均匀缩放的父游戏对象的子游戏对象在旋转时不会自动更新其比例。因此,当您最终更新比例时,子游戏对象的形状可能会突然改变,例如,如果子游戏对象与父游戏对象分离。
但是有时候难免要调整对象的形状,尤其是使用Unity里的原生对象时。这时我们有什么方法呢?
方法一 :比较简单,就是将子对象外部再加一个均匀缩放(Scale=1,1,1)的空对象。这样物体在移动或旋转时就不会变形。这种方法本质上还是避免了将物体作为非均匀缩放的子对象。
方法二:就是通过动态管理物体上的刚体实现。代码如下:
详细的解释请参考:如何实现物体跟随平台移动或旋转。
cs
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);
}
}
}
参考资料: