如何创建一个二维的“曲线”碰撞器。

10

我正在尝试寻找一种智能的方法来创建曲线,如下图所示(使用Unity3d 2D部分(不使用网格碰撞器)),但是我没有找到合适的方法。

输入图像描述

任何帮助将不胜感激。


2
你可以尝试使用边缘碰撞器,虽然结果可能不如那条曲线完美。 - Jay Kazama
那就是我正在努力解决的问题类型。我正在尝试用 N 个点做贝塞尔曲线,同时等待答案。如果我解决了我的问题,我会自己回答的。 - A.Quiroga
我已经按照你说的 @JayKazama 使用了边缘碰撞器。 - A.Quiroga
2个回答

29

考虑到之前的答案(已删除)并不符合我的需求,我自己使用了贝塞尔曲线和EdgeCollider2D来创建BezierCollider2D

版本1

BezierCollider2D.cs

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

[RequireComponent (typeof (EdgeCollider2D))]
public class BezierCollider2D : MonoBehaviour 
{
    public Vector2 firstPoint;
    public Vector2 secondPoint;

    public Vector2 handlerFirstPoint;
    public Vector2 handlerSecondPoint;

    public int pointsQuantity;

   Vector3 CalculateBezierPoint(float t,Vector3 p0,Vector3 handlerP0,Vector3 handlerP1,Vector3 p1)
   {
       float u = 1.0f - t;
       float tt = t * t;
       float uu = u * u;
       float uuu = uu * u;
       float ttt = tt * t;

       Vector3 p = uuu * p0; //first term
       p += 3f * uu * t * handlerP0; //second term
       p += 3f * u * tt * handlerP1; //third term
       p += ttt * p1; //fourth term

       return p;
    }

    public Vector2[] calculate2DPoints()
    {
        List<Vector2> points = new List<Vector2>();

        points.Add(firstPoint);
        for(int i=1;i<pointsQuantity;i++)
        {
            points.Add(CalculateBezierPoint((1f/pointsQuantity)*i,firstPoint,handlerFirstPoint,handlerSecondPoint,secondPoint));
        }
        points.Add(secondPoint);

        return points.ToArray();
   }

}

BezierCollider2DEditor.cs

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(BezierCollider2D))] 
public class BezierCollider2DEditor : Editor 
{
    BezierCollider2D bezierCollider;
    EdgeCollider2D edgeCollider;

    int lastPointsQuantity = 0;
    Vector2 lastFirstPoint = Vector2.zero;
    Vector2 lastHandlerFirstPoint = Vector2.zero;
    Vector2 lastSecondPoint = Vector2.zero;
    Vector2 lastHandlerSecondPoint = Vector2.zero;

    public override void OnInspectorGUI() 
    {
        bezierCollider = (BezierCollider2D) target;

        edgeCollider = bezierCollider.GetComponent<EdgeCollider2D>();

        if (edgeCollider != null)
        {
            bezierCollider.pointsQuantity = EditorGUILayout.IntField ("curve points",bezierCollider.pointsQuantity, GUILayout.MinWidth(100));
            bezierCollider.firstPoint = EditorGUILayout.Vector2Field ("first point",bezierCollider.firstPoint, GUILayout.MinWidth(100));
            bezierCollider.handlerFirstPoint = EditorGUILayout.Vector2Field ("handler first Point",bezierCollider.handlerFirstPoint, GUILayout.MinWidth(100));
            bezierCollider.secondPoint = EditorGUILayout.Vector2Field ("second point",bezierCollider.secondPoint, GUILayout.MinWidth(100));
            bezierCollider.handlerSecondPoint = EditorGUILayout.Vector2Field ("handler secondPoint",bezierCollider.handlerSecondPoint, GUILayout.MinWidth(100));

            EditorUtility.SetDirty(bezierCollider);

            if (bezierCollider.pointsQuantity > 0  && !bezierCollider.firstPoint.Equals(bezierCollider.secondPoint) &&
                (
                    lastPointsQuantity != bezierCollider.pointsQuantity ||
                    lastFirstPoint != bezierCollider.firstPoint ||
                    lastHandlerFirstPoint != bezierCollider.handlerFirstPoint ||
                    lastSecondPoint != bezierCollider.secondPoint ||
                    lastHandlerSecondPoint != bezierCollider.handlerSecondPoint
                ))
            {
                edgeCollider.points = bezierCollider.calculate2DPoints();
            }

        }
    }
}

示例:

在此输入图片描述

版本2(带有可视化处理程序和隐藏的边缘碰撞器在检查器中)

BezierCollider2D.cs

using UnityEngine;
using System.Collections.Generic;

[RequireComponent (typeof (EdgeCollider2D))]
public class BezierCollider2D : MonoBehaviour 
{
    public Vector2 firstPoint;
    public Vector2 secondPoint;

    public Vector2 handlerFirstPoint;
    public Vector2 handlerSecondPoint;

    public int pointsQuantity;

    Vector3 CalculateBezierPoint(float t,Vector3 p0,Vector3 handlerP0,Vector3 handlerP1,Vector3 p1)
    {
        float u = 1.0f - t;
        float tt = t * t;
        float uu = u * u;
        float uuu = uu * u;
        float ttt = tt * t;

        Vector3 p = uuu * p0; //first term
        p += 3f * uu * t * handlerP0; //second term
        p += 3f * u * tt * handlerP1; //third term
        p += ttt * p1; //fourth term

        return p;
    }

    public Vector2[] calculate2DPoints()
    {
        List<Vector2> points = new List<Vector2>();

        points.Add(firstPoint);
        for(int i=1;i<pointsQuantity;i++)
        {
            points.Add(CalculateBezierPoint((1f/pointsQuantity)*i,firstPoint,handlerFirstPoint,handlerSecondPoint,secondPoint));
        }
        points.Add(secondPoint);

        return points.ToArray();
    }

}

BezierCollider2DEditor.cs

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(BezierCollider2D))] 
public class BezierCollider2DEditor : Editor 
{
    BezierCollider2D bezierCollider;
    EdgeCollider2D edgeCollider;

    int lastPointsQuantity = 0;
    Vector2 lastFirstPoint = Vector2.zero;
    Vector2 lastHandlerFirstPoint = Vector2.zero;
    Vector2 lastSecondPoint = Vector2.zero;
    Vector2 lastHandlerSecondPoint = Vector2.zero;

    public override void OnInspectorGUI() 
    {
        bezierCollider = (BezierCollider2D) target;

        edgeCollider = bezierCollider.GetComponent<EdgeCollider2D>();

        if (edgeCollider.hideFlags != HideFlags.HideInInspector)
        {
            edgeCollider.hideFlags = HideFlags.HideInInspector;
        }

        if (edgeCollider != null)
        {
            bezierCollider.pointsQuantity = EditorGUILayout.IntField ("curve points",bezierCollider.pointsQuantity, GUILayout.MinWidth(100));
            bezierCollider.firstPoint = EditorGUILayout.Vector2Field ("first point",bezierCollider.firstPoint, GUILayout.MinWidth(100));
            bezierCollider.handlerFirstPoint = EditorGUILayout.Vector2Field ("handler first Point",bezierCollider.handlerFirstPoint, GUILayout.MinWidth(100));
            bezierCollider.secondPoint = EditorGUILayout.Vector2Field ("second point",bezierCollider.secondPoint, GUILayout.MinWidth(100));
            bezierCollider.handlerSecondPoint = EditorGUILayout.Vector2Field ("handler secondPoint",bezierCollider.handlerSecondPoint, GUILayout.MinWidth(100));

            EditorUtility.SetDirty(bezierCollider);

            if (bezierCollider.pointsQuantity > 0  && !bezierCollider.firstPoint.Equals(bezierCollider.secondPoint) &&
                (
                    lastPointsQuantity != bezierCollider.pointsQuantity ||
                    lastFirstPoint != bezierCollider.firstPoint ||
                    lastHandlerFirstPoint != bezierCollider.handlerFirstPoint ||
                    lastSecondPoint != bezierCollider.secondPoint ||
                    lastHandlerSecondPoint != bezierCollider.handlerSecondPoint
                ))
            {
                lastPointsQuantity = bezierCollider.pointsQuantity;
                lastFirstPoint = bezierCollider.firstPoint;
                lastHandlerFirstPoint = bezierCollider.handlerFirstPoint;
                lastSecondPoint = bezierCollider.secondPoint;
                lastHandlerSecondPoint = bezierCollider.handlerSecondPoint;
                edgeCollider.points = bezierCollider.calculate2DPoints();
            }

        }
    }

    void OnSceneGUI () 
    {
        if (bezierCollider != null)
        {
            Handles.color = Color.grey;

            Handles.DrawLine(bezierCollider.transform.position+(Vector3)bezierCollider.handlerFirstPoint,bezierCollider.transform.position+(Vector3)bezierCollider.firstPoint);
            Handles.DrawLine(bezierCollider.transform.position+(Vector3)bezierCollider.handlerSecondPoint,bezierCollider.transform.position+(Vector3)bezierCollider.secondPoint);

            bezierCollider.firstPoint = Handles.FreeMoveHandle(bezierCollider.transform.position+((Vector3)bezierCollider.firstPoint),Quaternion.identity,0.04f*HandleUtility.GetHandleSize(bezierCollider.transform.position+((Vector3)bezierCollider.firstPoint)),Vector3.zero,Handles.DotCap) - bezierCollider.transform.position;
            bezierCollider.secondPoint = Handles.FreeMoveHandle(bezierCollider.transform.position+((Vector3)bezierCollider.secondPoint),Quaternion.identity,0.04f*HandleUtility.GetHandleSize(bezierCollider.transform.position+((Vector3)bezierCollider.secondPoint)),Vector3.zero,Handles.DotCap) - bezierCollider.transform.position;
            bezierCollider.handlerFirstPoint = Handles.FreeMoveHandle(bezierCollider.transform.position+((Vector3)bezierCollider.handlerFirstPoint),Quaternion.identity,0.04f*HandleUtility.GetHandleSize(bezierCollider.transform.position+((Vector3)bezierCollider.handlerFirstPoint)),Vector3.zero,Handles.DotCap) - bezierCollider.transform.position;
            bezierCollider.handlerSecondPoint = Handles.FreeMoveHandle(bezierCollider.transform.position+((Vector3)bezierCollider.handlerSecondPoint),Quaternion.identity,0.04f*HandleUtility.GetHandleSize(bezierCollider.transform.position+((Vector3)bezierCollider.handlerSecondPoint)),Vector3.zero,Handles.DotCap) - bezierCollider.transform.position;

            if (GUI.changed)
            {
                EditorUtility.SetDirty (target);
            }
        }
    }

}

在此输入图片描述

享受它吧。


(注:该段文字为对图片的描述,无需翻译)

了不起的工作!称我为新手,但有没有办法将物理材料应用于曲线? - Polygon
如果你注释掉 edgeCollider.hideFlags = HideFlags.HideInInspector; 这一行,你就能看到边缘碰撞体组件,其中你可以包含一个材料。我将在未来几天更新这个组件,以便暴露所有的边缘碰撞体变量。 - A.Quiroga
如何安装这个插件? - Lai32290
只需导入您看到的2个脚本并将它们添加到gameObject中。 - A.Quiroga
3D样条线怎么样,比如道路片段? - WDUK
上面的注释对我没有起作用。为了在检查器中显示edgeCollider,我使用了edgeCollider.hideFlags = HideFlags.None; - max pleaner

0
尝试使用这个,但是没有起作用,看起来"Handles.DotCap"已经过时了,或者什么的我也不知道。使用"Handles.DotHandleCap"就可以了,不知道为什么,但是确实有效。 :)

1
这并没有回答问题。一旦你有足够的声望,你就可以评论任何帖子;相反,提供不需要提问者澄清的答案。- 来自评论 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接