【问题标题】:Unity. Rigidbody constraints ignored with attached child game object统一。附加子游戏对象忽略刚体约束
【发布时间】:2021-01-10 18:11:01
【问题描述】:

我有一个非运动刚体的玩家角色,通过使用 RigidBody.AddRelativeForce() 将其沿着平坦的地面推动来移动。在刚体的约束部分,我冻结了 X 和 Z 旋转以防止它翻倒。这个动作似乎效果很好。

现在,地上有很多箱子。我为它们制作了(静态)对撞机(没有刚体),因此它们会阻止玩家的移动。角色应该能够拿起其中一个盒子,随身携带,然后将其放在其他地方。我希望携带的盒子与地面上的盒子发生碰撞,这就是问题的开始。我找到了两种不同的方法来做到这一点,但都没有成功。

方法 1. 对于拾取,我使用 SetParent() 将框变换动态链接到角色(当前测试将框设置为编辑器层次结构中角色的子级)。这给了我携带的盒子的运动和我想要的碰撞,因为盒子的碰撞器(它是静态的)现在充当刚体碰撞器。但是现在我注意到刚体的旋转约束不再起作用,在盒子成为角色游戏对象的孩子之后。当对角色的刚体施加力时(携带盒子时),它会倒下(X-rotation 和 Z-rotation 不再为 0)。我尝试为盒子提供自己的刚体组件,其约束与角色父级相同,但这会使盒子独立移动。我在这里做错了吗?为什么将框添加为子框时忽略约束?

角色根据玩家输入在脚本中旋转,代码如下

transform.RotateAround(transform.position, Vector3.up, deltaRot);

不确定是否相关,但角色是导入的 (Blender/.blend) 网格的预制件。因此,在编辑器层次结构中,角色游戏对象(具有刚体和碰撞器)具有许多子对象(具有网格渲染器,这些子对象是 Unity 在导入 .blend 文件时创建的)和一个子对象,即携带的盒子游戏对象.

方法 2. 当盒子被拾起时,我在盒子上创建(或激活)一个父约束组件,它以角色为源。这似乎是比第一个选项“更干净”的方式,因为我没有弄乱游戏对象层次结构,但又是问题。首先,盒子的碰撞器现在与角色的碰撞器交互,在角色应该静止时推动它。这似乎很奇怪,但我可以禁用与 Physics.IgnoreCollision() 的交互。其次,携带的盒子仍然是一个静态碰撞器,因此不会与地面(以及关卡边界)上的静态碰撞器交互,这意味着角色可以将盒子推过障碍物。我不知道如何处理这个。向盒子游戏对象添加刚体会使携带的盒子再次自行移动(Parent Constraint 似乎被忽略了)。我不希望地面上的盒子被移动,所以我不想给它们一个刚体。我可以尝试向角色游戏对象动态添加一个与携带盒的对撞机相匹配的对撞机,但这似乎很棘手(这些盒子后来应该具有不同的大小和形状,因此启用/禁用单个对撞机不是一个很好的解决方案)。

其中一种方法可行吗?有没有更好的我还没有考虑过?我的设计有问题吗?

【问题讨论】:

  • 您的第一个错误是您试图移动标记为静态的对象。这会产生意想不到的行为。
  • 好吧,这似乎是个好点子。我会记住这一点。谢谢!
  • 仅供参考,我发现刚体约束似乎被忽略的原始问题并非来自附加对象。当角色的对撞机不在地面上时,角色对象也可能翻倒(x 和/或 z 旋转不等于 0)。看来我错误地使用了这些约束。

标签: unity3d


【解决方案1】:

从不想要嵌套/创建不同的 Rigidbody 对象。这会导致很多意想不到的行为。

这就是Joints 的优点。

在您的用例中,您可能会使用FixedJoint

Fixed Joints 将对象的运动限制为依赖于另一个对象。这有点类似于 Parenting,但通过物理而不是 Transform 层次结构实现。使用它们的最佳场景是当您有想要轻松分开的对象时,或者连接两个对象的移动而不需要父项

所以这是我将在您的场景中使用的:

  • 因为您的播放器对象上已经有 Rigidbody
  • Rigidbody 在你所有的盒子上
  • 在运行时设置这些框的Rigidbody.constraints(尽管由于下一点可能不需要这样做)
  • 在盒子上使用提到的FixedJoint。如果没有设置锚体,这会将框也固定到场景中的当前位置

所以在盒子上,你会有例如

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(FixedJoint))]
public class Pickable : MonoBehaviour
{
    [SerializeField] private Rigidbody _rigidbody;
    public Rigidbody Rigidbody => _rigidbody;

    [SerializeField] private FixedJoint _fixedJoint;
    public FixedJoint Joint => _fixedJoint;

    private void Awake ()
    {
        if(!_fixedJoint && ! TryGetComponent<FixedJoint>(out _fixedJoint)) _fixedJoint = gameObject.AddComponent<FixedJoint>();

        if(!_rigidbody && ! TryGetComponent<Rigidbody>(out _rigidbody)) _rigidbody = gameObject.AddComponent<Rigidbody>();

        _rigidbody.constraints = RigidbodyConstraints.FreezeAll;
    }
}

你的皮卡可能看起来有点像

public class PickupController : MonoBehaviour
{
    // Rigidbody of the player
    [SerializeField] private Rigidbody _rigidbody;
    private Pickable currentPickable;

    // However exactly you would call these in your scenario
    public void PickUp(Pickable pickable)
    {
        if(currentPickable)
        {
            Debug.LogError($"Have already picked up {currentPickable.name}", this);
            return;
        }

        currentPickable = pickable;
        pickable.Rigidbody.constraint = RigidbodyConstraints.None;
        pickable.Joint.connectedBody = _rigidbody;
    }

    public void Release()
    {
        // If have not picked anything or object was destroyed -> nothing to do
        if(! currentPickable) return;

        currentPickable.Rigidbody.constraint = RigidbodyConstraint.FreezeAll;
        currentPickable.Joint.connectedBody = null;    
        currentPickable = null;   
    }
}

在智能手机上输入,但我希望思路清晰

【讨论】:

  • 感谢您的浏览。我试过你的建议,它似乎适用于固定关节,谢谢!我确实看过关于 Joints 的文档,但我被这句话推迟了“[...] 当你有想要轻松地相互分离的对象,或者连接两个对象的运动而不需要父母的时候。”我不希望链接断开,也没有看到育儿问题,所以我认为这不适合我的情况。不过应该试一下,抱歉。我不知道您可以将连接强度设置为“无穷大”。再次感谢代码 sn-ps!
猜你喜欢
  • 1970-01-01
  • 2018-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多