碰撞后正确附加到GameObject?

时间:2015-11-21 16:49:21

标签: c# unity3d collision-detection collision unity5

如何正确在碰撞后使GameObject附加(或“粘贴”)到另一个GameObject?问题:我希望GameObject在碰撞后附加,即使它正在改变比例。

“碰撞时附加”代码:

protected Transform stuckTo = null;
protected Vector3 offset = Vector3.zero;

public void LateUpdate()
{
    if (stuckTo != null)
        transform.position = stuckTo.position - offset;
}  

void OnCollisionEnter(Collision col)
{
    rb = GetComponent<Rigidbody>();
    rb.isKinematic = true;

    if(stuckTo == null 
       || stuckTo != col.gameObject.transform)
        offset = col.gameObject.transform.position - transform.position;

    stuckTo = col.gameObject.transform;
}

此代码使GameObject在碰撞后完美连接。但是当GameObject改变比例(当它附着时)时,它在视觉上不再看起来与它碰撞的任何东西相关联。基本上,这段代码使GameObject在碰撞时仅保留原始比例。如何使GameObject始终坚持与碰撞的任何东西?它在过程中的规模是多少?我想避免养育子女:“虽然有点不安全,但是养育对手会造成奇怪的结果,比如随机传送或物体开始疯狂地移动和旋转等等。” - SamedTarıkÇETİN:comment

缩放脚本:

public Transform object1; //this is the object that my future-scaling GameObject collided with.
public Transform object2; //another object, the same scale as object1, somewhere else 
//(or vice versa)

void Update () 
{
    float distance = Vector3.Distance (object1.position, object2.position);
    float original_width = 10;
        if (distance <= 10) 
    {
        float scale_x = distance / original_width;
        scale_x = Mathf.Min (scale_x, 3.0f);
        transform.localScale = new Vector3 (scale_x * 3.0f, 3.0f / scale_x, 3.0f);
    }
}

4 个答案:

答案 0 :(得分:1)

您的基本想法是正确的,您的代码可以稍微修改以支持此功能。

这是诀窍:相反,不是将对象粘贴到它碰撞的对象上,而是创建一个虚拟游戏对象,让它在碰撞点称为“胶水”,并将对象粘贴到胶水上。胶水对象然后是我们碰撞的对象的父级。

由于胶水只是一个虚拟对象,只有组件转换和一些脚本,所以育儿没有问题。

此外,请注意我们创建胶水的接触点并不重要,以防我们有多个接触点,并且也很容易将其扩展以支持旋转,请参阅下文。

所以在碰撞时,我们现在唯一要做的就是创造一个胶水。这是代码:

void CreateGlue(Vector3 position, GameObject other) {
    // Here we create a glue object programatically, but you can make a prefab if you want.
    // Glue object is a simple transform with Glue.cs script attached.
    var glue = (new GameObject("glue")).AddComponent<Glue>();

    // We set glue position at the contact point
    glue.transform.position = position;

    // This also enables us to support object rotation. We initially set glue rotation to the same value
    // as our game object rotation. If you don't want rotation - simply remove this.
    glue.transform.rotation = transform.rotation;

    // We make the object we collided with a parent of glue object
    glue.transform.SetParent(other.transform);

    // And now we call glue initialization
    glue.AttachObject(gameObject);
}

void OnCollisionEnter(Collision col)
{
    // On collision we simply create a glue object at any contact point.
    CreateGlue(col.contacts[0].point, col.gameObject);
}

以下是Glue.cs脚本的外观,它将处理LateUpdate并修改转换。

public class Glue : MonoBehaviour {

    protected Transform stuckTo = null;
    protected Vector3 offset = Vector3.zero;

    public void AttachObject(GameObject other)
    {
        // Basically - same code as yours with slight modifications

        // Make rigidbody Kinematic
        var rb = other.GetComponent<Rigidbody>();
        rb.isKinematic = true;

        // Calculate offset - pay attention the direction of the offset is now reverse
        // since we attach glue to object and not object to glue. It can be modified to work
        // the other way, it just seems more reasonable to set all "glueing" functionality
        // at Glue object
        offset = transform.position - other.transform.position;

        stuckTo = other.transform;
    }

    public void LateUpdate()
    {
        if (stuckTo != null) {
            // If you don't want to support rotation remove this line
            stuckTo.rotation = transform.rotation;

            stuckTo.position = transform.position - transform.rotation * offset;
        }
    }

    // Just visualizing the glue point, remove if not needed
    void OnDrawGizmos() {
        Gizmos.color = Color.cyan;
        Gizmos.DrawSphere(transform.position, 0.2f);
    }
}

另外,请注意简单地按照此处建议的方式为对象添加内容会让您遇到一些额外的麻烦,因为缩放父级也会缩放子级,因此您必须将子级重新缩放回原始大小。问题是这些缩放操作是相对于不同的锚点,因此您还必须对对象位置进行额外调整。可以这样做。

我还创建了一个小样本项目,请看这里(Unity v5.2.f3): https://www.dropbox.com/s/whr85cmdp1tv7tv/GlueObjects.zip?dl=0

P.S。我看到你混合了变换和刚体的语义,因为它是在Kinematic刚体上完成的,这不是什么大问题,而只是一个建议:想想你是否真的需要对已经“卡住”其他人的对象有刚性体,如果不是 - 也许只是删除或禁用刚体,而不是使其成为运动学。

答案 1 :(得分:1)

您要做的是缩放碰撞点。您可以通过将枢轴点设置为碰撞点来实现此目的,当您缩放对象时,它将根据枢轴进行缩放。模拟这个的最好方法是在Unity中使用精灵。

Scaling About Pivot Point

在上图中,左上角的板条箱在中心有一个枢轴点,所以当你沿着x缩放它时,它会围绕该点进行缩放,增加它在枢轴点两侧的宽度。但是当枢轴点设置在侧面时,就像左下角图像一样,当你缩放它时,它只能向左延伸(除非你当然是负面缩放)。

现在问题是你不能轻易改变网格的枢轴点,所以有很多不同的解决办法。一种这样的方法是将网格附加到空的新gameObject并相对于父网格调整网格的局部变换,模拟移动网格轴点。

以下代码的作用是确定碰撞点。由于可能存在多个碰撞点,因此我得到它们的平均碰撞点。然后我将网格的父级移动到碰撞点并调整网格局部位置,使得立方体的侧面位于该点,这用于将网格的枢轴点设置为碰撞点。

现在缩放时,它将围绕碰撞点进行缩放,就像上图中一样。

public MeshRenderer _meshRenderer;
public float _moveXDirection;
public Rigidbody _rigidBody;
public Transform _meshTransform;
public bool _sticksToObjects;
public ScalingScript _scalingScript;

protected Transform _stuckTo = null;
protected Vector3 _offset = Vector3.zero;

void LateUpdate() 
{
    if (_stuckTo != null)
    {
        transform.position = _stuckTo.position - _offset;
    }
}

void OnCollisionEnter(Collision collision)
{
    if (!_sticksToObjects) {
        return;
    }

    _rigidBody.isKinematic = true;

    // Get the approximate collision point and normal, as there
    // may be multipled collision points
    Vector3 contactPoint = Vector3.zero;
    Vector3 contactNormal = Vector3.zero;
    for (int i = 0; i < collision.contacts.Length; i++) 
    {
        contactPoint += collision.contacts[i].point;
        contactNormal += collision.contacts[i].normal;
    }

    // Get the final, approximate, point and normal of collision
    contactPoint /= collision.contacts.Length;
    contactNormal /= collision.contacts.Length;

    // Move object to the collision point
    // This acts as setting the pivot point of the cube mesh to the collision point
    transform.position = contactPoint;

    // Adjust the local position of the cube so it is flush with the pivot point
    Vector3 meshLocalPosition = Vector3.zero;

    // Move the child so the side is at the collision point.
    // A x local position of 0 means the child is centered on the parent,
    // a value of 0.5 means it's to the right, and a value of -0.5 means it to the left
    meshLocalPosition.x = (0.5f * contactNormal.x);
    _meshTransform.localPosition = meshLocalPosition;

    if (_stuckTo == null || _stuckTo != collision.gameObject.transform) 
    {
        _offset = collision.gameObject.transform.position - transform.position;
    }

    _stuckTo = collision.gameObject.transform;

    // Enable the scaling script
    if (_scalingScript != null)
    {
        _scalingScript.enabled = true;
    }
}

这是一个带有上述代码的示例项目: https://www.dropbox.com/s/i6pdlw8mjs2sxcf/CubesAttached.zip?dl=0

答案 2 :(得分:0)

改变全球

protected Collider stuckTo = null;    

/////使用Collider而不是变换对象。你可能会得到更好的解决方案。告诉我它是否有效或给出任何错误,因为我没有尝试,如果它有效,我想知道。

void OnCollisionEnter(Collision col)
{
    rb = GetComponent<Rigidbody>();
    rb.isKinematic = true;

    if(stuckTo == null  || stuckTo != col.gameObject.transform)
       offset = col.collider.bounds.center - transform.position;

    stuckTo = col.collider;
}
    public void LateUpdate()
    {
        if (stuckTo != null)
          { 

         Vector3 distance=stuckTo.bounds.extents + GetComponent<Collider>().bounds.extents;
         transform.position = stuckTo.bounds.center + distance;

            }
}

答案 3 :(得分:0)

确保您正在缩放 stuckTo 转换(连接了碰撞器的转换),而不是它的任何父级,或者这不起作用。

如果 stuckTo 的比例是统一的:

protected Transform stuckTo = null;
protected Vector3 originalPositionOffset = Vector3.zero;
protected Vector3 positionOffset = Vector3.zero;
protected Vector3 originalScaleOfTheTarget = Vector3.zero;

public void LateUpdate()
{
    if (stuckTo != null){
        positionOffset *= stuckTo.localScale.x;
        transform.position = stuckTo.position - positionOffset;
    }
}  

void OnCollisionEnter(Collision col)
{
    rb = GetComponent<Rigidbody>();
    rb.isKinematic = true;

    if(stuckTo == null 
       || stuckTo != col.gameObject.transform){
        originalScaleOfTheTarget = col.gameObject.transform.localScale;

        originalPositionOffset = col.gameObject.transform.position - transform.position;
        originalPositionOffset /= originalScaleOfTheTarget.x;
    }

    stuckTo = col.gameObject.transform;
}

但如果 stuckTo 的比例不均匀:

protected Transform stuckTo = null;
protected Vector3 originalPositionOffset = Vector3.zero;
protected Vector3 positionOffset = Vector3.zero;
protected Vector3 originalScaleOfTheTarget = Vector3.zero;

public void LateUpdate()
{
    if (stuckTo != null){
        positionOffset.x = originalPositionOffset.x * stuckTo.localScale.x;
        positionOffset.y = originalPositionOffset.y * stuckTo.localScale.y;
        positionOffset.z = originalPositionOffset.z * stuckTo.localScale.z;

        transform.position = stuckTo.position - positionOffset;
    }
}  

void OnCollisionEnter(Collision col)
{
    rb = GetComponent<Rigidbody>();
    rb.isKinematic = true;

    if(stuckTo == null 
       || stuckTo != col.gameObject.transform){
        originalScaleOfTheTarget = col.gameObject.transform.localScale;

        originalPositionOffset = col.gameObject.transform.position - transform.position;
        originalPositionOffset.x /= originalScaleOfTheTarget.x;
        originalPositionOffset.y /= originalScaleOfTheTarget.y;
        originalPositionOffset.z /= originalScaleOfTheTarget.z;
    }

    stuckTo = col.gameObject.transform;
}

但是仍然 - 你为什么要关注ÇETİN的建议人?只要你知道自己在做什么,对父母的对手和刚体以及任何事情都是完全安全的。只需将你的粘性变换置于目标和bam之下!如果出现问题,只需删除刚体组件或禁用对撞机组件。