예제 #1
0
        public override void OnInspectorGUI()
        {
            //don't draw inspector fields if the path contains less than 2 points
            //(a path with less than 2 points really isn't a path)
            if (script.bPoints.Count < 2)
                return;

            //force naming scheme
            RenameWaypoints();

            //checkbox field to enable editable path properties
            EditorGUILayout.BeginHorizontal();
            script.showHandles = EditorGUILayout.Toggle("Show Handles", script.showHandles);
            EditorGUILayout.EndHorizontal();

            //checkbox field for drawing gizmo path lines
            script.drawCurved = EditorGUILayout.Toggle("Draw Smooth Lines", script.drawCurved);

            //create new color fields for editing path gizmo colors
            script.color1 = EditorGUILayout.ColorField("Color1", script.color1);
            script.color2 = EditorGUILayout.ColorField("Color2", script.color2);
            script.color3 = EditorGUILayout.ColorField("Color3", script.color3);

            //calculate path length of all waypoints
            float pathLength = WaypointManager.GetPathLength(script.pathPoints);
            //path length label, show calculated path length
            GUILayout.Label("Path Length: " + pathLength);

            float thisDetail = script.pathDetail;
            //slider to modify the smoothing factor of the final path
            script.pathDetail = EditorGUILayout.Slider("Path Detail", script.pathDetail, 0.5f, 10);
            //toggle custom detail when modifying the whole path
            if (thisDetail != script.pathDetail)
                script.customDetail = false;

            //draw custom detail settings
            EditorGUILayout.Space();
            DetailSettings();
            EditorGUILayout.Space();

            //waypoint index header
            GUILayout.Label("Waypoints: ", EditorStyles.boldLabel);

            //loop through the waypoint array
            for (int i = 0; i < script.bPoints.Count; i++)
            {
                GUILayout.BeginHorizontal();
                //indicate each array slot with index number in front of it
                GUILayout.Label(i + ".", GUILayout.Width(20));

                //create an object field for every waypoint
                EditorGUILayout.ObjectField(script.bPoints[i].wp, typeof(Transform), true);

                //display an "Add Waypoint" button for every array row except the last one
                //on click we call AddWaypointAtIndex() to insert a new waypoint slot AFTER the selected slot
                if (i < script.bPoints.Count && GUILayout.Button("+", GUILayout.Width(30f)))
                {
                    AddWaypointAtIndex(i);
                    break;
                }

                //display an "Remove Waypoint" button for every array row except the first and last one
                //on click we call RemoveWaypointAtIndex() to delete the selected waypoint slot
                if (i > 0 && i < script.bPoints.Count - 1 && GUILayout.Button("-", GUILayout.Width(30f)))
                {
                    RemoveWaypointAtIndex(i);
                    break;
                }

                GUILayout.EndHorizontal();
            }

            EditorGUILayout.Space();

            //button to move all waypoints down to the ground
            if (GUILayout.Button("Place to Ground"))
            {
                //for each waypoint of this path
                foreach (BezierPoint bp in script.bPoints)
                {
                    //define ray to cast downwards waypoint position
                    Ray ray = new Ray(bp.wp.position + new Vector3(0, 2f, 0), -Vector3.up);
                    Undo.RecordObject(bp.wp, "PlaceToGround");

                    RaycastHit hit;
                    //cast ray against ground, if it hit:
                    if (Physics.Raycast(ray, out hit, 100))
                    {
                        //position waypoint to hit point
                        bp.wp.position = hit.point;
                    }

                    //also try to raycast against 2D colliders
                    RaycastHit2D hit2D = Physics2D.Raycast(ray.origin, -Vector2.up, 100);
                    if (hit2D)
                    {
                        bp.wp.position = new Vector3(hit2D.point.x, hit2D.point.y, bp.wp.position.z);
                    }
                }
            }

            EditorGUILayout.Space();

            //invert direction of whole path
            if (GUILayout.Button("Invert Direction"))
            {
                Undo.RecordObject(script, "Invert");

                //to reverse the whole path we need to know where the waypoints were before
                //for this purpose a new copy must be created
                BezierPoint[] waypointCache = new BezierPoint[script.bPoints.Count];
                for (int i = 0; i < waypointCache.Length; i++)
                    waypointCache[i] = script.bPoints[i];

                //reverse order based on the old list
                for (int i = 0; i < waypointCache.Length; i++)
                {
                    BezierPoint currentPoint = script.bPoints[waypointCache.Length - 1 - i];
                    script.bPoints[waypointCache.Length - 1 - i] = waypointCache[i];
                    Vector3 leftHandle = currentPoint.cp[0].position;

                    Undo.RecordObject(currentPoint.cp[0], "Invert");
                    Undo.RecordObject(currentPoint.cp[1], "Invert");

                    currentPoint.cp[0].position = currentPoint.cp[1].position;
                    currentPoint.cp[1].position = leftHandle;
                }
            }

            EditorGUILayout.Space();

            //draw object field for new waypoint object
            script.replaceObject = (GameObject)EditorGUILayout.ObjectField("Replace Object", script.replaceObject, typeof(GameObject), true);

            //replace all waypoints with the prefab
            if (GUILayout.Button("Replace Waypoints with Object"))
            {
                ReplaceWaypoints();
            }

            //recalculate on inspector changes
            if (GUI.changed)
            {
                script.CalculatePath();
                EditorUtility.SetDirty(target);
            }
        }
예제 #2
0
        //repositions the opposite control point if one changes
        private void PositionOpposite(BezierPoint point, bool isLeft, Vector3 newPos)
        {
            Vector3 pos = point.wp.position;
            Vector3 toParent = pos - newPos;
            toParent.Normalize();

            if (isLeft)
            {
                //received the left handle, manipulating the right
                float magnitude = (pos - point.cp[1].position).magnitude;
                point.cp[0].position = newPos;
                point.cp[1].position = pos + toParent * magnitude;
            }
            else
            {
                //received the right handle, manipulating the left
                float magnitude = (pos - point.cp[0].position).magnitude;
                point.cp[1].position = newPos;
                point.cp[0].position = pos + toParent * magnitude;
            }
        }
예제 #3
0
        //adds a waypoint when clicking on the "+" button in the inspector
        private void AddWaypointAtIndex(int index)
        {
            //create a new bezier point property class
            BezierPoint point = new BezierPoint();
            //create new waypoint gameobject
            Transform wp = new GameObject("Waypoint").transform;

            //disabled because of a Unity bug that crashes the editor
            //Undo.RecordObject(script, "Add");
            //Undo.RegisterCreatedObjectUndo(wp, "Add");

            //set its position to the last one
            wp.position = script.bPoints[index].wp.position;
            //assign it to the class
            point.wp = wp;
            //assign new control points
            Transform left = new GameObject("Left").transform;
            Transform right = new GameObject("Right").transform;
            left.parent = right.parent = wp;
            left.position = wp.position + new Vector3(2, 0, 0);
            right.position = wp.position + new Vector3(-2, 0, 0);
            point.cp = new[] { left, right };

            //parent bezier point to the path gameobject
            wp.parent = script.transform;
            //add new detail value for the new segment
            script.segmentDetail.Insert(index + 1, script.pathDetail);
            //finally, insert this new waypoint after the one clicked
            script.bPoints.Insert(index + 1, point);
        }
        //if this path is selected, display small info boxes above all waypoint positions
        //also display handles for the waypoints and their bezier points
        void OnSceneGUI()
        {
            //do not execute further code if we have no waypoints defined
            //(just to make sure, practically this can not occur)
            if (script.bPoints.Count == 0)
            {
                return;
            }
            Vector3 wpPos = Vector3.zero;
            float   size  = 1f;

            //handles
            for (int i = 0; i < script.bPoints.Count; i++)
            {
                //get related bezier point class
                BezierPoint point = script.bPoints[i];
                if (point == null || !point.wp)
                {
                    continue;
                }
                wpPos = point.wp.position;
                size  = HandleUtility.GetHandleSize(wpPos) * 0.4f;

                if (size < 3f)
                {
                    //begin GUI block
                    Handles.BeginGUI();
                    //translate waypoint vector3 position in world space into a position on the screen
                    var guiPoint = HandleUtility.WorldToGUIPoint(wpPos);
                    //create rectangle with that positions and do some offset
                    var rect = new Rect(guiPoint.x - 50.0f, guiPoint.y - 40, 100, 20);
                    //draw box at position with current waypoint name
                    GUI.Box(rect, point.wp.name);
                    Handles.EndGUI(); //end GUI block
                }

                Handles.color = script.color2;
                //draw bezier point handles, clamp size
                size = Mathf.Clamp(size, 0, 1.2f);

                //Vector3 newPos = Handles.PositionHandle(wpPos, Quaternion.identity);
                Vector3 newPos = Handles.FreeMoveHandle(wpPos, Quaternion.identity,
                                                        size, Vector3.zero, Handles.SphereCap);
                //Handles.RadiusHandle(Quaternion.identity, wpPos, size / 2);

                if (wpPos != newPos)
                {
                    Undo.RecordObject(point.wp, "Move Handles");
                    point.wp.position = newPos;
                }

                if (!script.showHandles)
                {
                    continue;
                }

                Handles.color = script.color3;
                //draw control point handles
                //left handle, all control points except first one
                if (i > 0)
                {
                    float controlSize = HandleUtility.GetHandleSize(point.cp[0].position) * 0.25f;
                    controlSize = Mathf.Clamp(controlSize, 0, 0.5f);

                    PositionOpposite(point, true,
                                     Handles.FreeMoveHandle(
                                         point.cp[0].position,
                                         Quaternion.identity,
                                         controlSize,
                                         Vector3.zero,
                                         Handles.SphereCap));

                    Undo.RecordObject(point.cp[0], "Move Control Left");
                }
                //right handle, all waypoints except last one
                if (i < script.bPoints.Count - 1)
                {
                    float controlSize = HandleUtility.GetHandleSize(point.cp[1].position) * 0.25f;
                    controlSize = Mathf.Clamp(controlSize, 0, 0.5f);

                    PositionOpposite(point, false,
                                     Handles.FreeMoveHandle(
                                         point.cp[1].position,
                                         Quaternion.identity,
                                         controlSize,
                                         Vector3.zero,
                                         Handles.SphereCap));

                    Undo.RecordObject(point.cp[1], "Move Control Right");
                }

                //draw line between control points
                Handles.DrawLine(point.cp[0].position, point.cp[1].position);
            }

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

            //recalculate path points after handles
            script.CalculatePath();

            if (!script.showHandles)
            {
                return;
            }

            //draw small dots for each path point (not waypoint)
            Handles.color = script.color2;
            Vector3[] pathPoints = script.pathPoints;

            for (int i = 0; i < pathPoints.Length; i++)
            {
                Handles.SphereCap(0, pathPoints[i], Quaternion.identity,
                                  Mathf.Clamp((HandleUtility.GetHandleSize(pathPoints[i]) * 0.12f), 0, 0.25f));
            }
        }
        public override void OnInspectorGUI()
        {
            //don't draw inspector fields if the path contains less than 2 points
            //(a path with less than 2 points really isn't a path)
            if (script.bPoints.Count < 2)
            {
                return;
            }

            //checkbox field to enable editable path properties
            script.showHandles = EditorGUILayout.Toggle("Show Handles", script.showHandles);
            //checkbox field for toggling control point connectedness
            script.connectHandles = EditorGUILayout.Toggle("Connect Handles", script.connectHandles);
            //checkbox field for drawing gizmo path lines
            script.drawCurved = EditorGUILayout.Toggle("Draw Smooth Lines", script.drawCurved);

            //create new color fields for editing path gizmo colors
            script.color1 = EditorGUILayout.ColorField("Color1", script.color1);
            script.color2 = EditorGUILayout.ColorField("Color2", script.color2);
            script.color3 = EditorGUILayout.ColorField("Color3", script.color3);

            //calculate path length of all waypoints
            float pathLength = WaypointManager.GetPathLength(script.pathPoints);

            //path length label, show calculated path length
            GUILayout.Label("Path Length: " + pathLength);

            float thisDetail = script.pathDetail;

            //slider to modify the smoothing factor of the final path
            script.pathDetail = EditorGUILayout.Slider("Path Detail", script.pathDetail, 0.5f, 10);
            //toggle custom detail when modifying the whole path
            if (thisDetail != script.pathDetail)
            {
                script.customDetail = false;
            }

            //draw custom detail settings
            EditorGUILayout.Space();
            DetailSettings();
            EditorGUILayout.Space();

            //waypoint index header
            GUILayout.Label("Waypoints: ", EditorStyles.boldLabel);

            //loop through the waypoint array
            for (int i = 0; i < script.bPoints.Count; i++)
            {
                GUILayout.BeginHorizontal();
                //indicate each array slot with index number in front of it
                GUILayout.Label(i + ".", GUILayout.Width(20));

                //create an object field for every waypoint
                EditorGUILayout.ObjectField(script.bPoints[i].wp, typeof(Transform), true);

                //display an "Add Waypoint" button for every array row except the last one
                //on click we call AddWaypointAtIndex() to insert a new waypoint slot AFTER the selected slot
                if (i < script.bPoints.Count && GUILayout.Button("+", GUILayout.Width(30f)))
                {
                    AddWaypointAtIndex(i);
                    break;
                }

                //display an "Remove Waypoint" button for every array row except the first and last one
                //on click we call RemoveWaypointAtIndex() to delete the selected waypoint slot
                if (i > 0 && i < script.bPoints.Count - 1 && GUILayout.Button("-", GUILayout.Width(30f)))
                {
                    RemoveWaypointAtIndex(i);
                    break;
                }

                GUILayout.EndHorizontal();
            }

            EditorGUILayout.Space();

            EditorGUILayout.BeginHorizontal();
            //button to rename waypoints to current index order
            if (GUILayout.Button("Rename Waypoints"))
            {
                string   wpName = string.Empty;
                string[] nameSplit;

                for (int i = 0; i < script.bPoints.Count; i++)
                {
                    //cache name and split into strings
                    wpName    = script.bPoints[i].wp.name;
                    nameSplit = wpName.Split(' ');

                    //ignore custom names and just rename
                    if (!script.skipCustomNames)
                    {
                        wpName = "Waypoint " + i;
                    }
                    else if (nameSplit.Length == 2 && nameSplit[0] == "Waypoint")
                    {
                        //try parsing the current index and rename,
                        //not ignoring custom names here
                        int index;
                        if (int.TryParse(nameSplit[1], out index))
                        {
                            wpName = nameSplit[0] + " " + i;
                        }
                    }

                    //set the desired index or leave it
                    script.bPoints[i].wp.name = wpName;
                }
            }

            EditorGUILayout.LabelField("Skip Custom", GUILayout.Width(80));
            script.skipCustomNames = EditorGUILayout.Toggle(script.skipCustomNames, GUILayout.Width(20));
            EditorGUILayout.EndHorizontal();

            //button to move all waypoints down to the ground
            if (GUILayout.Button("Place to Ground"))
            {
                //for each waypoint of this path
                foreach (BezierPoint bp in script.bPoints)
                {
                    //define ray to cast downwards waypoint position
                    Ray ray = new Ray(bp.wp.position + new Vector3(0, 2f, 0), -Vector3.up);
                    Undo.RecordObject(bp.wp, "PlaceToGround");

                    RaycastHit hit;
                    //cast ray against ground, if it hit:
                    if (Physics.Raycast(ray, out hit, 100))
                    {
                        //position waypoint to hit point
                        bp.wp.position = hit.point;
                    }

                    //also try to raycast against 2D colliders
                    RaycastHit2D hit2D = Physics2D.Raycast(ray.origin, -Vector2.up, 100);
                    if (hit2D)
                    {
                        bp.wp.position = new Vector3(hit2D.point.x, hit2D.point.y, bp.wp.position.z);
                    }
                }
            }

            //invert direction of whole path
            if (GUILayout.Button("Invert Direction"))
            {
                Undo.RecordObject(script, "Invert");

                //to reverse the whole path we need to know where the waypoints were before
                //for this purpose a new copy must be created
                BezierPoint[] waypointCache = new BezierPoint[script.bPoints.Count];
                for (int i = 0; i < waypointCache.Length; i++)
                {
                    waypointCache[i] = script.bPoints[i];
                }

                //reverse order based on the old list
                for (int i = 0; i < waypointCache.Length; i++)
                {
                    BezierPoint currentPoint = script.bPoints[waypointCache.Length - 1 - i];
                    script.bPoints[waypointCache.Length - 1 - i] = waypointCache[i];
                    Vector3 leftHandle = currentPoint.cp[0].position;

                    Undo.RecordObject(currentPoint.cp[0], "Invert");
                    Undo.RecordObject(currentPoint.cp[1], "Invert");

                    currentPoint.cp[0].position = currentPoint.cp[1].position;
                    currentPoint.cp[1].position = leftHandle;
                }
            }

            EditorGUILayout.Space();

            //draw object field for new waypoint object
            script.replaceObject = (GameObject)EditorGUILayout.ObjectField("Replace Object", script.replaceObject, typeof(GameObject), true);

            //replace all waypoints with the prefab
            if (GUILayout.Button("Replace Waypoints with Object"))
            {
                ReplaceWaypoints();
            }

            //recalculate on inspector changes
            if (GUI.changed)
            {
                script.CalculatePath();
                EditorUtility.SetDirty(target);
            }
        }
예제 #6
0
        //bezier path placement
        void PlaceBezierPoint(Vector3 placePos)
        {
            //create new bezier point property class
            BezierPoint newPoint = new BezierPoint();

            //instantiate waypoint gameobject
            Transform wayp = new GameObject("Waypoint").transform;

            //assign waypoint to the class
            newPoint.wp = wayp;

            //same as above
            if (wpList.Count == 0)
            {
                pathMan.transform.position = placePos;
            }

            //position current waypoint at clicked position in scene view
            if (mode2D)
            {
                placePos.z = 0f;
            }
            wayp.position = placePos;
            //parent it to the defined path
            wayp.parent = pathMan.transform;

            BezierPathManager thisPath = pathMan as BezierPathManager;
            //create new array with bezier point handle positions
            Transform left  = new GameObject("Left").transform;
            Transform right = new GameObject("Right").transform;

            left.parent = right.parent = wayp;

            //initialize positions and last waypoint
            Vector3 handleOffset = new Vector3(2, 0, 0);
            Vector3 targetDir    = Vector3.zero;
            int     lastIndex    = wpList.Count - 1;

            //position handles to the left/right of the waypoint respectively
            left.position  = wayp.position + wayp.rotation * handleOffset;
            right.position = wayp.position + wayp.rotation * -handleOffset;
            newPoint.cp    = new[] { left, right };

            //position first handle in direction of the second waypoint
            if (wpList.Count == 1)
            {
                targetDir = (wayp.position - wpList[0].transform.position).normalized;
                thisPath.bPoints[0].cp[1].localPosition = targetDir * 2;
            }
            //always position last handle to look at the previous waypoint
            else if (wpList.Count >= 1)
            {
                targetDir = (wpList[lastIndex].transform.position - wayp.position);
                wayp.transform.rotation = Quaternion.LookRotation(targetDir) * Quaternion.Euler(0, -90, 0);
            }

            //position handle direction to the center of both last and next waypoints
            //takes into account 2D mode
            if (wpList.Count >= 2)
            {
                //get last point and center direction
                BezierPoint lastPoint = thisPath.bPoints[lastIndex];
                targetDir = (wayp.position - wpList[lastIndex].transform.position) +
                            (wpList[lastIndex - 1].transform.position - wpList[lastIndex].transform.position);

                //rotate to the center 2D/3D
                Quaternion lookRot = Quaternion.LookRotation(targetDir);
                if (mode2D)
                {
                    float angle = Mathf.Atan2(targetDir.y, targetDir.x) * Mathf.Rad2Deg + 90;
                    lookRot = Quaternion.AngleAxis(angle, Vector3.forward);
                }
                lastPoint.wp.rotation = lookRot;

                //cache handle and get previous of last waypoint
                Vector3 leftPos    = lastPoint.cp[0].position;
                Vector3 preLastPos = wpList[lastIndex - 1].transform.position;

                //calculate whether right or left handle distance is greater to last waypoint
                //left handle should point to the last waypoint, so reposition if necessary
                if (Vector3.Distance(leftPos, preLastPos) > Vector3.Distance(lastPoint.cp[1].position, preLastPos))
                {
                    lastPoint.cp[0].position = lastPoint.cp[1].position;
                    lastPoint.cp[1].position = leftPos;
                }
            }

            //add waypoint to the list of waypoints
            thisPath.bPoints.Add(newPoint);
            thisPath.segmentDetail.Add(thisPath.pathDetail);
            //add waypoint to temporary list
            wpList.Add(wayp.gameObject);
            //rename waypoint to match the list count
            wayp.name = "Waypoint " + (wpList.Count - 1);
            //recalculate bezier path
            thisPath.CalculatePath();
        }