gpt4 book ai didi

unity3d - 如何使编辑器句柄可选择以显示属性检查器窗口

转载 作者:行者123 更新时间:2023-12-04 20:28:58 24 4
gpt4 key购买 nike

这是来自 How to make individual anchor points of bezier continuous or non-continuous 的后续问题.请参阅它以获取已接受答案中的相关代码(请注意,我这样做是为了保持这个问题的清洁,因为相关代码很长)。

我正在努力实现以下目标:

  • 使贝塞尔曲线 handle /控制点可选择,以便单个 handle 的属性(例如连续性)在选中时显示在检查器窗口中。请注意,我希望在不为 handle /控制点创建游戏对象的情况下完成此操作
  • 保留处理每个点的移动的单一方法,而不是为每个点的移动使用单独的方法。
  • 最佳答案

    我迟到了吗?

    Make the bezier curve handles/control points selectable in such a way that the properties (for example continuity) for an individual handle are displayed in the inspector window when selected. Please note I'd like this to be done without making creating game objects for the handles/ control points



    我一般喜欢@jour 的解决方案,除了 1 件事:使用 Handles.Button您必须单击一个点来选择它,然后单击并拖动以移动控制点。

    我提出了一种不同的方法。使用相同的 Handles.FreeMoveHandle ,但是有一个变量来记住最后点击的句柄的 id,所以我可以识别它。

    通常,内置的 Handle 不会为您提供比其设计用途更多的信息。 FreeMoveHandle ,例如,返回其翻译的增量,仅此而已。问题是:你想捕获一个简单的点击,但是如果你只是点击并没有拖动,返回值是 Vector3.zero这就像你根本没有点击一样。

    好消息:在任何 Handle 的重载中,有一些调用带有名为 controlID 的参数。 - 它是每个可交互 GUI 对象的标识符。如果你抑制它,引擎会选择一个,你永远不知道是什么。但是,如果您传递一个 int,则该值将是句柄的 id。但是,如果我通过一个 int 并且它碰巧与我看不到的任何其他 id 冲突?嗯,你可以调用 GUIUtility.GetControlID获得一个安全的身份证。

    然后,它是直截了当的。如果 Handle 的 id 与 EditorGUIUtility.hotControl 相同(即被点击或获得键盘焦点的控件),然后我将这个点的索引保存在 selectedControlPointId并使用它在 Inspector 中显示自定义属性。

    Retain a single method that handles the movement of each point instead of having separate methods for the movement of each point.



    嗯...这里成为争议。如果我理解正确,您需要一个代码来绘制节点和切线。问题是:这两件事本质上是不同的。当然,如果你保持简单明了,它们就是场景中的机器人可移动点。但是,当您引入约束(连续性或 smooth )和选择之类的东西时,它们就变成了不同的野兽,具有不同的逻辑。即使你想做 ControlPoint一个 struct (就像我现在所做的那样)并将其作为一个整体传递,您仍然需要指出您要更新的组件,因此约束将适用于其他组件 - 您将始终需要一个“主”字段以避免循环更新(您更改 tangentBack ,这使得 tangentFront 更新,触发 tangentBack 再次更新等等)。

    这就是为什么,即使我以某种方式重组了 ControlPoint方法并使其成为 struct ,我无法制作一个逻辑来绘制节点和切线。

    这里有一些代码。我从 my answer 上的代码重新开始在上一个问题中。

    ControlPoint.cs
    using System;
    using UnityEngine;

    [Serializable]
    public struct ControlPoint
    {
    public Vector2 position;
    public Vector2 tangentBack;
    public Vector2 tangentFront;
    public bool smooth;

    static public ControlPoint MovePosition(ControlPoint pt, Vector2 newPos)
    {
    var newPt = pt;
    newPt.position = newPos;
    return newPt;
    }

    static public ControlPoint MoveTangentBack(ControlPoint pt, Vector2 newTanBack)
    {
    var newPt = pt;
    newPt.tangentBack = newTanBack;
    if (pt.smooth) newPt.tangentFront = pt.tangentFront.magnitude * -newTanBack.normalized;
    return newPt;
    }
    static public ControlPoint MoveTangentFront(ControlPoint pt, Vector2 newTanFront)
    {
    var newPt = pt;
    newPt.tangentFront = newTanFront;
    if (pt.smooth) newPt.tangentBack = pt.tangentBack.magnitude * -newTanFront.normalized;
    return newPt;
    }

    static public ControlPoint WithSmooth(ControlPoint pt, bool smooth)
    {
    var newPt = pt;
    if (smooth != pt.smooth) newPt.tangentBack = -pt.tangentFront;
    return newPt;

    }

    public ControlPoint(Vector2 position, Vector2 tanBack, Vector2 tanFront, bool smooth = false)
    {
    this.position = position;
    this.tangentBack = tanBack;
    this.tangentFront = tanFront;
    this.smooth = smooth;
    }
    }

    我删除了 ControlPointDrawer ,因此您添加的其他属性不会隐藏在检查器中。

    路径.cs
    using System;
    using UnityEngine;
    using System.Collections.Generic;

    [Serializable]
    public class Path
    {
    [SerializeField] List<ControlPoint> _points;

    [SerializeField] bool _loop = false;

    public Path(Vector2 position)
    {
    _points = new List<ControlPoint>
    {
    new ControlPoint(position, -Vector2.one, Vector2.one),
    new ControlPoint(position + Vector2.right, -Vector2.one, Vector2.one)
    };
    }

    public bool loop { get { return _loop; } set { _loop = value; } }

    public ControlPoint this[int i]
    {
    get { return _points[(_loop && i == _points.Count) ? 0 : i]; }
    set { _points[(_loop && i == _points.Count) ? 0 : i] = value; }
    }

    public int NumPoints { get { return _points.Count; } }

    public int NumSegments { get { return _points.Count - (_loop ? 0 : 1); } }

    public ControlPoint InsertPoint(int i, Vector2 position)
    {
    _points.Insert(i, new ControlPoint(position, -Vector2.one, Vector2.one));
    return this[i];
    }
    public ControlPoint RemovePoint(int i)
    {
    var item = this[i];
    _points.RemoveAt(i);
    return item;
    }
    public Vector2[] GetBezierPointsInSegment(int i)
    {
    var pointBack = this[i];
    var pointFront = this[i + 1];
    return new Vector2[4]
    {
    pointBack.position,
    pointBack.position + pointBack.tangentFront,
    pointFront.position + pointFront.tangentBack,
    pointFront.position
    };
    }

    public ControlPoint MovePoint(int i, Vector2 position)
    {
    this[i] = ControlPoint.MovePosition(this[i], position);
    return this[i];
    }

    public ControlPoint MoveTangentBack(int i, Vector2 position)
    {
    this[i] = ControlPoint.MoveTangentBack(this[i], position);
    return this[i];
    }

    public ControlPoint MoveTangentFront(int i, Vector2 position)
    {
    this[i] = ControlPoint.MoveTangentFront(this[i], position);
    return this[i];
    }
    }

    PathCreator.cs 是一样的

    PathCreatorEditor.cs
    using UnityEngine;
    using UnityEditor;

    [CustomEditor(typeof(PathCreator))]
    public class PathCreatorEditor : Editor
    {
    PathCreator creator;
    Path path;
    SerializedProperty property;
    static int selectedControlPointId = -1;

    public override void OnInspectorGUI()
    {
    serializedObject.Update();
    var loopProp = property.FindPropertyRelative("_loop");
    EditorGUI.BeginChangeCheck();
    EditorGUILayout.PropertyField(loopProp);
    var ptsProp = property.FindPropertyRelative("_points");
    var msg = "Total points in path: " + ptsProp.arraySize + "\n";
    if (selectedControlPointId >= 0 && ptsProp.arraySize > 0)
    {
    EditorGUILayout.HelpBox(msg + "Selected control point: " + selectedControlPointId, MessageType.Info);
    EditorGUILayout.Separator();
    EditorGUILayout.PropertyField(ptsProp.GetArrayElementAtIndex(selectedControlPointId), true);
    }
    else
    {
    EditorGUILayout.HelpBox(msg + "No control points selected", MessageType.Info);
    }
    if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties();
    }

    void OnSceneGUI()
    {
    Input();
    Draw();
    }

    void Input()
    {
    Event guiEvent = Event.current;
    Vector2 mousePos = HandleUtility.GUIPointToWorldRay(guiEvent.mousePosition).origin;
    mousePos = creator.transform.InverseTransformPoint(mousePos);
    if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.shift)
    {
    Undo.RecordObject(creator, "Insert point");
    path.InsertPoint(path.NumPoints, mousePos);
    }
    else if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.control)
    {
    for (int i = 0; i < path.NumPoints; i++)
    {
    if (Vector2.Distance(mousePos, path[i].position) <= .25f)
    {
    Undo.RecordObject(creator, "Remove point");
    path.RemovePoint(i);
    break;
    }
    }
    }
    }

    void Draw()
    {
    Handles.matrix = creator.transform.localToWorldMatrix;
    var rot = Quaternion.Inverse(creator.transform.rotation) * Tools.handleRotation;
    var snap = Vector2.zero;
    Handles.CapFunction cap = Handles.DotHandleCap;
    for (int i = 0; i < path.NumPoints; i++)
    {
    float size;
    var pos = path[i].position;
    size = HandleUtility.GetHandleSize(pos) * .05f;
    Handles.Label(pos, i.ToString());
    Handles.color = i == selectedControlPointId ? Handles.selectedColor : Color.red;
    int ctrlId = GUIUtility.GetControlID(FocusType.Passive);
    Vector2 newPos = Handles.FreeMoveHandle(ctrlId, pos, rot, size, snap, cap);
    if (ctrlId == EditorGUIUtility.hotControl) selectedControlPointId = i;
    if (pos != newPos)
    {
    Undo.RecordObject(creator, "Move point position");
    path.MovePoint(i, newPos);
    }
    pos = newPos;
    Handles.color = Color.black;
    if (path.loop || i != 0)
    {
    var tanBack = pos + path[i].tangentBack;
    Handles.DrawLine(pos, tanBack);
    size = HandleUtility.GetHandleSize(tanBack) * .03f;
    Vector2 newTanBack = Handles.FreeMoveHandle(tanBack, rot, size, snap, cap);
    if (tanBack != newTanBack)
    {
    Undo.RecordObject(creator, "Move point tangent");
    path.MoveTangentBack(i, newTanBack - pos);
    }
    }
    if (path.loop || i != path.NumPoints - 1)
    {
    var tanFront = pos + path[i].tangentFront;
    Handles.DrawLine(pos, tanFront);
    size = HandleUtility.GetHandleSize(tanFront) * .03f;
    Vector2 newTanFront = Handles.FreeMoveHandle(tanFront, rot, size, snap, cap);
    if (tanFront != newTanFront)
    {
    Undo.RecordObject(creator, "Move point tangent");
    path.MoveTangentFront(i, newTanFront - pos);
    }
    }
    }
    Repaint();
    }



    [DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
    static void DrawGizmo(PathCreator creator, GizmoType gizmoType)
    {
    Gizmos.matrix = creator.transform.localToWorldMatrix;
    var path = creator.path;
    for (int i = 0; i < path.NumSegments; i++)
    {
    Vector2[] points = path.GetBezierPointsInSegment(i);
    var pts = Handles.MakeBezierPoints(points[0], points[3], points[1], points[2], 30);
    Gizmos.color = Color.green;
    for (int j = 0; j < pts.Length - 1; j++)
    {
    Gizmos.DrawLine(pts[j], pts[j + 1]);
    }
    }
    }

    void OnEnable()
    {
    creator = (PathCreator)target;
    path = creator.path ?? creator.CreatePath();
    property = serializedObject.FindProperty("path");
    }
    }

    注意:我从 OnSceneGui 移动了贝塞尔线的绘图。至 DrawGizmo ,因此即使对象没有被选中,绿线也将可见,并且可以在场景编辑器中拾取它,以便选中它。

    最后,我建议对该脚本进行一些进一步的开发。使多点选择成为可能并不难。也许使默认 handle (如位置和旋转)可单独应用于点。或者将创建和删除点的方法更改为直观的移动方式,例如双击或拖动路径线。甚至是用于智能点操作的自定义工具栏,如对齐、分布、雕刻......不同的约束,如平滑、对称、尖点或直线......

    关于unity3d - 如何使编辑器句柄可选择以显示属性检查器窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51238340/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com