unity-game-engineunity3d-2dtools

How can i create a 2D 'curve' collider


I'm trying to search an smart way to create curves like the next one (using Unity3d 2D part (without using mesh collider))) , but i didn't found one

enter image description here

Any help would be appreciated.


Solution

  • seeing that the last answer (removed) doesn't fit what i want. I have made myself BezierCollider2D using Bezier curves between 2 points and EdgeCollider2D.

    VERSION 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();
                }
    
            }
        }
    }
    

    Sample:

    enter image description here

    VERSION 2 (with visual handlers and hiden edge collider in inspector)

    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);
                }
            }
        }
    
    }
    

    enter image description here

    Enjoy it.